You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
calculate-utils-2.2-lib/pym/cl_template.py

3785 lines
161 KiB

#-*- coding: utf-8 -*-
# Copyright 2008-2010 Mir Calculate Ltd. http://www.calculate-linux.org
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import sys
import os
import stat
import re
import xml.dom.minidom
from xml import xpath
import subprocess
import types
import random
import string
from cl_utils import _error, scan, _toUNICODE, removeDir, getModeFile
from cl_overriding import exit
import cl_lang
tr = cl_lang.lang()
tr.setLocalDomain('cl_lib')
tr.setLanguage(sys.modules[__name__])
class _terms(_error):
"""Вычисление условий применяемых в шаблонах
"""
def _convertVers(self, verA, verB):
"""Конвертирование номеров версий для корректного сравнения
"""
elemA = verA.split(".")
elemB = verB.split(".")
if len(elemA) > len(elemB):
maxElemB = len(elemB)-1
for i in range(len(elemA)):
if i > maxElemB:
elemB.append("0")
else:
maxElemA = len(elemA)-1
for i in range(len(elemB)):
if i > maxElemA:
elemA.append("0")
for i in range(len(elemB)):
lenA = len(elemA[i])
lenB = len(elemB[i])
if lenA == lenB:
pass
elif lenA > lenB:
res = lenA - lenB
for z in range(res):
elemB[i] = "0" + elemB[i]
elif lenB > lenA:
res = lenB - lenA
for z in range(res):
elemA[i] = "0" + elemA[i]
return (".".join(elemA), ".".join(elemB))
def executeListEqual(self, listEqual):
"""Вычисляет список выражений
пример списка:
listEqual = [False, True, ' or ', True , True]
(если нет or между логическими выражениями то между ними and)
результат True
"""
lenOr = listEqual.count(" or ")
for i in xrange(lenOr):
ind = listEqual.index(' or ')
if False in listEqual[:ind]:
listEqual = listEqual[ind+1:]
continue
else:
return True
if False in listEqual:
return False
else:
return True
def _equalTerm(self, term, textError, function=False):
"""Вычисление логических выражений для условий
Для корректной работы в классе который наследует этот класс
должен быть объявлен аттрибут self.objVar
(объект для работы с переменными)
function - функция для для обработки функций в заголовке блока
"""
trm = {"&&":" and ","||":" or "}
rule = ["==", "!=", ">=", "<=", ">", "<"]
listEqual = []
for k in trm.keys():
if k in term:
term = term.replace(k,trm[k])
trs = term.split(" ")
for t in trs:
flagRule = False
for sepF in rule:
if sepF in t:
flagRule = True
vals = t.split(sepF)
break
if not flagRule:
flagLog = False
for k in trm.values():
if k.strip() == t:
flagLog = True
break
if not flagLog:
self.setError("'%s'"%term + " " + _("incorrect"))
self.setError (textError)
return False
elif k == " or ":
listEqual.append(k)
else:
#проверка на допустимость названия переменной
reDenyName = re.compile("[^a-zA-Z0-9\_\-]")
flagFunction = False
if reDenyName.search(vals[0]):
#проверка на допустимость функции
flagError = True
if function:
reFunction = re.compile(\
"([a-zA-Z0-9\_\-]+)\([a-zA-Z0-9_\-\+\,\*\/\.]+\)")
searchFunct = reFunction.search(vals[0])
if searchFunct:
flagError = False
flagFunction = True
if flagError:
self.setError("'%s'"%term + " " + _("incorrect"))
self.setError(textError)
return False
#проверка на допустимость значения
reDenyValue = re.compile("[^0-9a-zA-Z_\.-]")
if reDenyValue.search(vals[1]):
self.setError("'%s'"%term + " " + _("incorrect"))
self.setError(textError)
return False
flagIntTypeVar = None
if flagFunction:
valVars = function("#-%s-#"%vals[0])
if valVars == "":
flagFunction = False
if valVars == False:
self.setError("'%s'"%term + " " + _("incorrect"))
self.setError(textError)
return False
if "load" == searchFunct.group(1):
if re.search("\(\s*num\s*,",vals[0]):
if valVars:
try:
valVars = int(valVars)
except:
self.setError("'%s'"%term + " " + \
_("incorrect"))
self.setError (textError)
return False
flagIntTypeVar = True
else:
flagIntTypeVar = False
else:
try:
valVars = self.objVar.Get(vals[0])
except self.objVar.DataVarsError, e:
print textError
print e
exit(1)
# Cравниваем номера версий
if "_ver" in vals[0] or \
(flagFunction and "pkg" == searchFunct.group(1)) or\
(flagFunction and "load" == searchFunct.group(1) and\
re.search("\(\s*ver\s*,",vals[0])):
verFile, verVar = self._convertVers(vals[1],valVars)
exec("res=("+"'"+verVar+"'"+sepF+"'"+verFile+"'"+")")
if res:
listEqual.append(True)
else:
listEqual.append(False)
else:
if flagIntTypeVar == None:
flagIntTypeVar = True
try:
valVars = int(valVars)
except:
flagIntTypeVar = False
if flagIntTypeVar:
if not vals[1].strip():
vals[1] = 0
try:
valFile = int(vals[1])
except:
self.setError("'%s'"%term + " " + _("incorrect"))
self.setError (textError)
return False
valVar = valVars
exec("res=(%d%s%d)"%(valVar,sepF,valFile))
if res:
listEqual.append(True)
else:
listEqual.append(False)
else:
if sepF == "!=" or sepF == "==":
if not vals[1].strip():
vals[1] = ""
valFile = vals[1]
valVar = valVars
exec("res=("+'"""'+valVar+'"""'+sepF+"'"+valFile+\
"'"+")")
if res:
listEqual.append(True)
else:
listEqual.append(False)
else:
if valVars == "":
listEqual.append(False)
else:
self.setError("'%s'"%term + " "\
+ _("incorrect"))
self.setError (textError)
return False
#exec("res=(%s)"%("".join(listEqual)))
res = self.executeListEqual(listEqual)
return res
class shareHeader:
"""Общие методы для обработки заголовков"""
reSplParHeader = re.compile("\s+",re.I)
reHeader=re.compile(r"\A\s*#\s*calculate(\s+)?\\?([^\\\n]*\\\n)+[^\\\n]*\n?\
|\s*#\s*calculate\s+([^\\\n]*\n?)",re.I|re.M)
def getHeader(self, text):
"""Получаем результат поиска и заголовок файла"""
sHeader = self.reHeader.search(text)
if sHeader:
return (sHeader, sHeader.group())
else:
return (False, "")
def getParamsHeader(self, textHeader):
"""Получаем параметры заголовка в виде списка"""
listParams = self.reSplParHeader.split(textHeader.replace("\\"," "))
if listParams[0] == "#":
return filter(lambda x: x, listParams[2:])
else:
return filter(lambda x: x, listParams[1:])
class fileHeader(shareHeader, _terms):
"""Обработка заголовков шаблонов и конфигурационных файлов
"""
# Допустимые параметры заголовка
allowParam = ("format", "comment", "append", "force", "link", "mirror",
"symbolic", "chmod", "chown", "path", "name")
# параметры без значения
listParNotVal = ("symbolic", "force", "mirror")
# Возможные типы вставки шаблонов
_fileAppend = ("join", "before", "after", "replace", "remove", "skip")
# условные операторы
terms = ('>', '<', '==', '!=', '>=', '<=')
# параметры без значения
#listParNotVal = ("symbolic", "force", "mirror")
# Форматы файлов для которых метод объединения replace если он не задан
#replaceFormats = ("raw","bin")
def delHeaderConfFile(self, text, comment):
"""Удаляет заголовок в тексте конфигурационного файла"""
if comment and text:
# Удаление Заголовка Calculate в конфигурационном файле
# В случае текста XML
if type(comment) == types.TupleType and len(comment) == 2:
_titleList = (_("Modified"), _("File of a template"))
reCalcHeader =\
re.compile(u"\s*%s\s+%s.+Calculate.+\s+%s.+\s+%s\s?"%(\
comment[0],
_titleList[0].decode("UTF-8"),
_titleList[1].decode("UTF-8"),
comment[1],
),
re.M|re.I|re.U)
textUnicode = text.decode("UTF-8")
reS = reCalcHeader.search(textUnicode)
if reS:
textBody = textUnicode[:reS.start()]+textUnicode[reS.end():]
if textBody:
return textBody.encode("UTF-8")
else:
# В остальных случаях
reCalcHeader =\
re.compile("\s*%s\-+\s+%s.+Calculate.+\s+%s.+\s+%s\-+\s?"%(\
comment,
comment,
comment,
comment,
),
re.M|re.I)
reS = reCalcHeader.search(text)
if reS:
return text[reS.end():]
return text
def getPropertyTemplate(self, textHeader, foundHeader, fileType, objVar,
function, fileName):
"""Получаем свойства шаблона из текста заголовка шаблона"""
# Объект с переменными
self.objVar=objVar
# Параметры файла шаблона
params = {}
# Будет ли шаблон применен
headerTerm = True
# Бинарный шаблон
if fileType=="bin":
params["format"] = fileType
params["_position"] = 0
params["append"] = "replace"
params["_apply"] = headerTerm
# текстовый шаблон с заголовком
elif foundHeader:
# некорректные параметры
incorrectParams = set([])
# Получаем список параметров шаблона
paramList = self.getParamsHeader(textHeader)
if paramList:
errTerm = _("header template '%s' not valid")%fileName
for i in paramList:
foundTerm = False
for term in self.terms:
if term in i:
foundTerm = True
rezTerm = self._equalTerm(i, "%s: %s"%(errTerm,i),
function)
if not rezTerm:
headerTerm = False
break
if not foundTerm:
par = i.split("=")
if len(par) == 1:
ret = self.checkParams(i, None, params)
if not ret:
incorrectParams = set([i])
break
elif len(par) == 2:
ret = self.checkParams(par[0], par[1], params)
if not ret:
incorrectParams = set([i])
break
if not "format" in params:
# format - raw
params["format"] = "raw"
if not "append" in params:
params["append"] = "replace"
else:
if not "append" in params:
# в зависимости от формата - join или replace
formatTemplate = params["format"]
if formatTemplate in ("raw", "bin", ""):
params["append"] = "replace"
else:
params["append"] = "join"
if incorrectParams:
headerTerm = False
self.setError(_("incorrect header parameters - '%s'")\
%" ".join(list(incorrectParams)))
params["_position"] = foundHeader.end()
params["_apply"] = headerTerm
# текстовый шаблон без заголовка
else:
params["format"] = "raw"
params["_position"] = 0
params["append"] = "replace"
params["_apply"] = headerTerm
return params
def checkParams(self, name, value, dictPar):
"""Проверка параметра заголовка, при успехе запись в словарь dictPar"""
# Проверка на допустимые параметры заголовка
if not name in self.allowParam:
return False
if name in self.listParNotVal and not value is None:
return False
if name == "append":
if not value in self._fileAppend:
return False
dictPar[name] = value
return True
class dirHeader(shareHeader, _terms):
"""Обработка заголовков шаблонов директорий
"""
# Допустимые параметры заголовка
allowParam = ("append", "chmod", "chown", "path", "name")
# Возможные типы вставки шаблонов
_fileAppend = ("join", "remove", "skip")
# условные операторы
terms = ('>', '<', '==', '!=', '>=', '<=')
def getPropertyTemplate(self, text, objVar, function, fileName):
"""Получаем свойства шаблона из текста шаблона"""
# Объект с переменными
self.objVar=objVar
# Параметры описанные в заголовке файла шаблона
params = {}
# Некорректные параметры
incorrectParams = set([])
# Будет ли шаблон применен
headerTerm = True
foundHeader, textHeader = self.getHeader(text)
if foundHeader:
paramList = self.getParamsHeader(textHeader)
if paramList:
errTerm = _("header template '%s' not valid")%fileName
for i in paramList:
foundTerm = False
for term in self.terms:
if term in i:
foundTerm = True
rezTerm = self._equalTerm(i, "%s: %s"%(errTerm,i),
function)
if not rezTerm:
headerTerm = False
break
if not foundTerm:
par = i.split("=")
if len(par) == 1:
ret = self.checkParams(i, None, params)
if not ret:
incorrectParams = set([i])
break
elif len(par) == 2:
ret = self.checkParams(par[0], par[1], params)
if not ret:
incorrectParams = set([i])
break
if not "append" in params:
# По умолчанию join
params["append"] = "join"
if incorrectParams:
headerTerm = False
self.setError(_("incorrect header parameters - '%s'")\
%" ".join(list(incorrectParams)))
params["_apply"] = headerTerm
# текстовый шаблон без заголовка
else:
headerTerm = False
self.setError(_("Can not found header in template"))
params["_apply"] = headerTerm
return params
def checkParams(self, name, value, dictPar):
"""Проверка параметра заголовка, при успехе запись в словарь dictPar"""
# Проверка на допустимые параметры заголовка
if not name in self.allowParam:
return False
if name == "append":
if not value in self._fileAppend:
return False
if value is None:
return False
dictPar[name] = value
return True
class objShare:
"""Общий клас для объектов, наследуем
"""
def createFieldTerm(self, name, value, quote, docObj):
"""Создание поля переменная - значение
при создании поля проверяется первый символ названия переменной
и добавляется тег action
"!" - <action>drop</action> удаляет
"+" - <action>join</action> добавляет
"-" - <action>replace</action> заменяет
"""
fieldAction = False
if name:
if name[0] == "!" or name[0] == "-" or name[0] == "+":
qnt = self.removeSymbolTerm(quote)
fieldXML = docObj.createField("var",[qnt],
name[1:], [value],
False, False)
if name[0] == "!":
fieldAction = "drop"
elif name[0] == "+":
fieldXML.setAttribute("type", "seplist")
fieldAction = "join"
else:
fieldXML = docObj.createField("var",
[quote.replace("\n","")],
name, [value],
False, False)
else:
fieldXML = docObj.createField("var",
[quote.replace("\n","")],
name, [value],
False, False)
if fieldAction:
docObj.setActionField(fieldXML, fieldAction)
return fieldXML
def removeSymbolTerm(self, text):
"""Удаляет первый символ названия переменной в строке
Если первый встречающийся символ с начала строки
'+', '-', '!' то он из этой строки будет удален,
если перед этим символом были пробельные символы,
то они будут сохранены, так-же если в строке есть символ
перевода строки он будет удален.
"""
reTerm = re.compile("^[ \t]*(\!|\+|\-)")
textNS = text.replace("\n","")
res = reTerm.search(textNS)
if res:
textNS = textNS[res.start():res.end()-1] + textNS[res.end():]
return textNS
def getConfig(self):
"""Выдает конфигурационный файл"""
listConfigTxt = []
childNodes = self.docObj.getNodeBody().childNodes
for node in childNodes:
if node.nodeType == node.ELEMENT_NODE:
if node.tagName == "field":
listConfigTxt.append(self.docObj.getQuoteField(node))
elif node.tagName == "area":
self.docObj.xmlToText([node], listConfigTxt)
return "".join(listConfigTxt)
def splitToFields(self, txtBloc):
"""Разбиваем текст на поля (объекты) которые имеют следующие аттрибуты
self.name - если переменная то имя переменной
self.value - если у переменной есть значение то значение переменной
self.comment - если комментарий то текст комментария
self.br - если перевод строки то текст перед переводом из пробелов
Результат список объектов полей
"""
finBloc = "\n"
if txtBloc[-1] != "\n":
finBloc = ""
linesBlocTmp = txtBloc.splitlines()
linesBloc = []
brBloc = []
z = 0
lenLines = len(linesBlocTmp)
for i in linesBlocTmp:
if self.reComment.split(i)[0]:
findCooment = self.reComment.search(i)
comment = False
par = i
if findCooment:
par = i[:findCooment.start()]
comment = i[findCooment.start():]
fields = self.reSepFields.split(par)
lenFields = len(fields)
if lenFields>1:
for fi in range(lenFields-1):
linesBloc.append(fields[fi]+ self.sepFields)
if fi == lenFields-2:
if comment:
brBloc.append("")
else:
if (lenLines-1)== z:
brBloc.append(finBloc)
else:
brBloc.append("\n")
else:
brBloc.append("")
if comment:
linesBloc.append(comment)
if (lenLines-1)== z:
brBloc.append(finBloc)
else:
brBloc.append("\n")
else:
linesBloc.append(i)
if (lenLines-1)== z:
brBloc.append(finBloc)
else:
brBloc.append("\n")
else:
linesBloc.append(i)
if (lenLines-1)== z:
brBloc.append(finBloc)
else:
brBloc.append("\n")
z +=1
fields = self.setDataField(linesBloc, brBloc)
return fields
class xmlShare:
"""Общий класс для объектов XML, наследуем
"""
def _createElement(self, doc, tagName, text="", attributes={}):
"""Создание нового XML элемента"""
element = doc.createElement(tagName)
if text:
txtNode = doc.createTextNode(_toUNICODE(text))
element.appendChild(txtNode)
for attr in attributes.keys():
attribute = doc.createAttribute(attr)
attribute.nodeValue = attributes[attr]
element.setAttributeNode(attribute)
return element
class xmlNode(xmlShare):
"""Класс для создания нод без аттрибутов
"""
def __init__(self):
self.node = False
def createNode(self, doc, tagName, text=""):
"""Создает XML элемент без аттрибутов"""
self.node=self._createElement(doc, tagName, text)
return self.node
def getNode(self):
return self.node
class xmlCaption:
"""Класс XML заголовок
"""
def __init__(self):
#Заголовок области XML нода
self.caption = False
def createCaption(self, doc, name, quotes, action=False):
"""Создание заголовка области"""
tmpNode = xmlNode()
self.caption = tmpNode.createNode(doc, "caption")
nameNode = tmpNode.createNode(doc, "name",name)
self.caption.appendChild(nameNode)
if action:
actNode = tmpNode.createNode(doc, "action", action)
self.caption.appendChild(actNode)
for q in quotes:
quoteNode = tmpNode.createNode(doc, "quote", q)
self.caption.appendChild(quoteNode)
return self.caption
def getCaption(self):
"""Выдает XML ноду заголовка области"""
return self.caption
class xmlField(xmlShare):
"""Класс для работы с XML полем
"""
def __init__(self):
# XML нода поле
self.field = False
def createField(self, doc, typeField, quotes, name="",
values=[],action=False):
"""Cоздание XML ноды поле"""
self.field = self._createElement(doc, "field", "", {"type":typeField})
if name:
nameNode = self._createElement(doc, "name", name)
self.field.appendChild(nameNode)
for v in values:
valueNode = self._createElement(doc, "value", v)
self.field.appendChild(valueNode)
if action:
actNode = self._createElement(doc, "action", action)
self.field.appendChild(actNode)
for q in quotes:
quoteNode = self._createElement(doc, "quote", q)
self.field.appendChild(quoteNode)
return self.field
class xmlFields:
"""Класс, в котором находится список ХМL нод field
"""
def __init__(self):
self.fields = []
def appendField(self, field):
"""Добавить XML ноду field"""
self.fields.append(field)
return self.fields
def getFields(self):
"""Выдать список XML нод"""
return self.fields
class xmlArea:
"""Класс для работы с XML областью
"""
def __init__(self):
# Область
self.area = False
def createArea(self, doc, xmlCaption, xmlFields):
"""Создание XML области"""
tmpNode = xmlNode()
self.area = tmpNode.createNode(doc, "area")
if xmlCaption and xmlCaption.getCaption():
self.area.appendChild(xmlCaption.getCaption())
if xmlFields:
fields = xmlFields.getFields()
for field in fields:
self.area.appendChild(field)
return self.area
class xmlDoc:
"""Класс для работы с XML документом
"""
def __init__(self):
# документ
self.doc = False
# главная нода
self.root = False
# тело документа
self.body = False
# Заголовок области - временный (в реальности один объект заголовок)
self.tmpCaption = False
# Поля - временные (в реальности один объект поля)
self.tmpFields = False
# Разделитель областей - по умолчанию перевод строки "\n"
self.sepAreas = False
# Разделитель разделенных списков - по умолчанию перевод строки "\n"
#self.sepSplitFields = False
def createDoc(self, typeDoc, version):
"""Создание нового документа новый документ"""
docTxt = '<?xml version="1.0" encoding="UTF-8"?><cxmlconf><head>'
docTxt += '<ver>%s</ver>'% version
docTxt += '<format>%s</format>' % typeDoc
docTxt += '</head><body></body></cxmlconf>'
self.doc = xml.dom.minidom.parseString(docTxt)
self.root = self.doc.documentElement
self.body = xpath.Evaluate('child::body',self.root)[0]
# установка разделителя областей
self.sepAreas = self.createField("br",[],"",[],False,False)
# установка разделителя областей разделенных списков
#self.sepSplitFields = self.createField("br",[],"",[],False,False)
return self.doc
def addField(self, field):
"""Добавляет поле во временный список
Из этого списка в дальнейшем формируется XML область
"""
if not self.tmpFields:
self.tmpFields = xmlFields()
self.tmpFields.appendField(field)
def createCaption(self, name, quotes, action=False):
"""Cоздает заголовок области
Помещает заголовок в временный артибут
Используется при создании области
"""
self.tmpCaption = xmlCaption()
return self.tmpCaption.createCaption(self.doc, name, quotes, action)
def createField(self, typeField, quotes=[], name="",
values=[] ,action=False,addTmpField=True):
"""Cоздает поле
Если установлена переменнная addTmpField
добавляет поле во временный список
"""
fieldObj = xmlField()
field = fieldObj.createField(self.doc, typeField, quotes, name,
values, action)
if addTmpField:
self.addField(field)
return field
def clearTmpFields(self):
"""Очищает временный список"""
self.tmpFields = False
def createArea(self):
"""Cоздает область
Область создается на основании временного атрибута и временного списка
"""
areaObj = xmlArea()
area = areaObj.createArea(self.doc, self.tmpCaption, self.tmpFields)
self.clearTmpCaptionAndFields()
return area
def clearTmpCaptionAndFields(self):
"""Очищает временный аттрибут и временный список"""
self.tmpCaption = False
self.tmpFields = False
def getNodeRoot(self):
"""Выдает корневую ноду"""
return self.root
def getNodeBody(self):
"""Выдает ноду body"""
return self.body
def setActionField(self, xmlField, actionTxt):
"""Устанавливает свойство action для XML поля"""
xmlActions = xpath.Evaluate('child::action',xmlField)
if xmlActions and xmlActions[0].firstChild:
xmlActions[0].firstChild.nodeValue = actionTxt
else:
nodeObj = xmlNode()
newNode = nodeObj.createNode(self.doc, "action", actionTxt)
xmlField.appendChild(newNode)
def setActionArea(self, xmlArea, actionTxt):
"""Устанавливает свойство action для XML области"""
xmlActions = xpath.Evaluate('child::caption/action',xmlArea)
xmlCaptions = xpath.Evaluate('child::caption',xmlArea)
if xmlActions and xmlActions[0].firstChild:
xmlActions[0].firstChild.nodeValue = actionTxt
else:
if xmlCaptions:
nodeObj = xmlNode()
newNode = nodeObj.createNode(self.doc, "action", actionTxt)
xmlCaptions[0].appendChild(newNode)
def joinField(self, xmlArea, xmlNewField):
"""Объединяет XML ноду область и XML ноду поле"""
newNameField = self.getNameField(xmlNewField)
if not newNameField or not newNameField.strip():
return False
fieldsOldComp = xpath.Evaluate("child::field[child::name='%s']"\
%(newNameField), xmlArea)
# Если поле не найдено добавляем его
typeNewField = self.getTypeField(xmlNewField)
if not fieldsOldComp and typeNewField != "seplist":
if self.getActionField(xmlNewField) != "drop":
self.setActionField(xmlNewField, "append")
xmlArea.appendChild(xmlNewField)
return True
newFieldsAction = self.getActionField(xmlNewField)
newValues = self.getFieldValues(xmlNewField)
flagCompare = True
for nodeFieldOld in fieldsOldComp:
if newFieldsAction == "drop":
if nodeFieldOld.nextSibling and\
self.getTypeField(nodeFieldOld.nextSibling) == "br":
xmlArea.removeChild(nodeFieldOld.nextSibling)
elif nodeFieldOld.previousSibling and\
self.getTypeField(nodeFieldOld.previousSibling) == "br":
xmlArea.removeChild(nodeFieldOld.previousSibling)
xmlArea.removeChild(nodeFieldOld)
continue
oldValues = self.getFieldValues(nodeFieldOld)
# Сравнение значений переменной шаблона и файла
if set(newValues) != set(oldValues):
flagCompare = False
if self.getActionField(xmlNewField) == "drop":
return True
appSplLst = []
insSplLst = []
if typeNewField == "seplist":
if fieldsOldComp:
xmlOldField = fieldsOldComp[-1]
else:
xmlOldField = False
seplistNewXML = self.getSepListToField(xmlNewField)
if seplistNewXML:
for nodeSeplist in seplistNewXML:
if self.getActionField(nodeSeplist) != "drop":
if newFieldsAction == "join":
flagCompareSeplist = False
newValues = self.getFieldValues(nodeSeplist)
for nodeFieldOld in fieldsOldComp:
oldValues = self.getFieldValues(nodeFieldOld)
for newValue in newValues:
if newValue in oldValues:
flagCompareSeplist = True
break
if not flagCompareSeplist:
nextNode = xmlOldField.nextSibling
newInsNode = nodeSeplist.cloneNode(True)
self.setActionField(newInsNode,"append")
if nextNode:
appSplLst.append((newInsNode,
nextNode,
"insert"))
else:
appSplLst.append((newInsNode,
False,
"append"))
else:
newInsNode = nodeSeplist.cloneNode(True)
if self.getActionField(newInsNode) == "join":
self.setActionField(newInsNode,"append")
if xmlOldField:
insSplLst.append((newInsNode,
xmlOldField,
"insert"))
else:
insSplLst.append((newInsNode,
False,
"append"))
#xmlArea.insertBefore(\
#nodeSeplist.cloneNode(True),
#xmlOldField)
parentNode = nodeSeplist.parentNode
parentNode.removeChild(nodeSeplist)
insNodesRepl = []
for newNode, nxtNode, app in insSplLst:
flagCompareSeplist = False
newValues = self.getFieldValues(newNode)
for nodeRepl, nxtNode, app in insNodesRepl:
oldValues = self.getFieldValues(nodeRepl)
for newValue in newValues:
if newValue in oldValues:
flagCompareSeplist = True
break
if not flagCompareSeplist:
if xmlOldField:
insNodesRepl.append((newNode, nxtNode, app))
for newNode, nxtNode, app in insNodesRepl:
if app == "insert":
xmlArea.insertBefore(newNode,nxtNode)
elif app == "append":
xmlArea.appendChild(newNode)
if xmlOldField:
parentNode = xmlOldField.parentNode
if parentNode and newFieldsAction != "join":
parentNode.removeChild(xmlOldField)
for newNode, nxtNode, app in appSplLst:
if app == "insert":
xmlArea.insertBefore(newNode,nxtNode)
elif app == "append":
xmlArea.appendChild(newNode)
if not flagCompare and typeNewField != "seplist":
# Устанавливаем action=replace
self.setActionField(xmlNewField, "replace")
# Если параметры поля не сходятся заменяем поле
xmlArea.replaceChild(xmlNewField.cloneNode(True),
fieldsOldComp[-1])
if newFieldsAction == "join":
fieldsOldRemove = []
else:
fieldsOldRemove = fieldsOldComp[:-1]
for nodeFieldOld in fieldsOldRemove:
actionOldNode = self.getActionField(nodeFieldOld)
if actionOldNode == "insert" or actionOldNode == "append":
pass
else:
if nodeFieldOld.nextSibling and\
self.getTypeField(nodeFieldOld.nextSibling) == "br":
xmlArea.removeChild(nodeFieldOld.nextSibling)
xmlArea.removeChild(nodeFieldOld)
return True
def getSepListToField(self, xmlField):
"""Выдает элементы распределенного массива
Область предок поля, в этой области ищутся
элементы распределенного массива
"""
nameField = self.getNameField(xmlField)
if not nameField:
return []
parentNode = xmlField.parentNode
#print parentNode.toprettyxml()
if parentNode:
fieldsVal = xpath.Evaluate(\
"child::field[attribute::type='seplist'][child::name='%s'] "\
%(nameField), parentNode)
#print nameField
return fieldsVal
else:
return []
def removeComment(self, xmlArea):
"""Удаляет комментарии в XML области"""
fieldNodes = xpath.Evaluate('descendant::field',xmlArea)
for fieldNode in fieldNodes:
if fieldNode.hasAttribute("type"):
if fieldNode.getAttribute("type") == "comment" or\
fieldNode.getAttribute("type") == "br":
parentNode = fieldNode.parentNode
parentNode.removeChild(fieldNode)
else:
if self.getActionField(fieldNode) == "drop":
pass
elif self.getActionField(fieldNode) == "join":
pass
else:
self.setActionField(fieldNode,"append")
def joinBody(self, baseBody, newBody):
"""Объединяет две области Body"""
newFields = xpath.Evaluate('child::field',newBody)
xmlNewAreas = xpath.Evaluate('child::area',newBody)
for xmlNewArea in xmlNewAreas:
self.joinArea(baseBody,xmlNewArea)
joinNewFields = xpath.Evaluate("child::field[child::action='join']"
,newBody)
self.addNewFielsOldArea(newFields, joinNewFields, baseBody)
def getRemoveNodeSepList(self, removeNodesDict, baseNode, xmNewlField):
"""Находит элементы разделенного списка
Параметры:
removeNodesDict - оварь удаляемых полей разделенного списка
формируется программой
baseNode - Нода в которой идет поиск
xmNewlField - Нода field которая проверяется на принадлежность
к разделенному списку
"""
flagNewNodeSeplist = False
if self.getTypeField(xmNewlField) == "seplist":
flagNewNodeSeplist = True
nameNewField = self.getNameField(xmNewlField)
if nameNewField:
if removeNodesDict.has_key(nameNewField):
return removeNodesDict[nameNewField]
else:
oldFields = xpath.Evaluate('child::field', baseNode)
removeNodes = []
lenOldFields = len(oldFields)
for i in range(lenOldFields):
oldNode = oldFields[i]
flagSep = self.getTypeField(oldNode) == "seplist"
if flagNewNodeSeplist:
flagSep = True
if flagSep and\
nameNewField == self.getNameField(oldNode):
removeNodes.append(oldNode)
if i+1<lenOldFields:
nextNode = oldFields[i+1]
if self.getTypeField(nextNode) == "br":
removeNodes.append(nextNode)
removeNodesDict[nameNewField] = removeNodes
return removeNodes
def addNewFielsOldArea(self, newFields, joinNewFields, xmlOldArea):
"""Добавляет новые XML поля в область шаблона"""
removeNodesDict = {}
notRemoveNodesDict = {}
for notRemNode in joinNewFields:
nameField = self.getNameField(notRemNode)
if not notRemoveNodesDict.has_key(nameField):
notRemoveNodesDict[nameField] = []
notRemoveNodesDict[nameField].append(notRemNode)
else:
notRemoveNodesDict[nameField].append(notRemNode)
notSepListField = []
sepListField = []
for nField in newFields:
if self.getRemoveNodeSepList(removeNodesDict, xmlOldArea,
nField):
sepListField.append(nField)
else:
if self.getNameField(nField):
notSepListField.append(nField)
for name in notRemoveNodesDict.keys():
if removeNodesDict.has_key(name):
removeNodesDict[name] = []
for removeNodes in removeNodesDict.values():
if removeNodes:
if self.getTypeField(removeNodes[-1]) == "seplist":
removeNodes = removeNodes[:-1]
else:
removeNodes = removeNodes[:-2]
for removeNode in removeNodes:
xmlOldArea.removeChild(removeNode)
for node in sepListField:
node.setAttribute("type", "seplist")
if not (self.getActionField(node) == "join" or\
self.getActionField(node) == "drop"):
self.setActionField(node,"insert")
self.joinField(xmlOldArea, node)
#else:
#self.setActionField(node, "append")
#baseBody.appendChild(node)
for node in notSepListField:
if self.getTypeField(node) == "seplist":
#if removeNodesDict.has_key(self.getNameField(node)):
#print removeNodesDict[self.getNameField(node)]
self.setActionField(node, "append")
xmlOldArea.appendChild(node)
else:
self.joinField(xmlOldArea, node)
def insertBeforeSepAreas(self, xmlArea):
"""Добавляет разделитель областей перед каждой областью"""
if not self.sepAreas:
return False
areaNodes = xpath.Evaluate('descendant::area',xmlArea)
for areaNode in areaNodes:
prevNode = areaNode.previousSibling
if prevNode:
parentNode = areaNode.parentNode
parentNode.insertBefore(self.sepAreas.cloneNode(True),
areaNode)
return True
def getAreaFields(self, nameArea, xmlArea):
"""По имени области выводит названия и значения всех переменных
поиск ведется только в 1-х потомках области xmlArea
на выход словарь переменных {имя:значение}
"""
namesAreaComare = xpath.Evaluate(\
"child::area/child::caption[child::name='%s']" %(nameArea),
xmlArea)
if not namesAreaComare:
return False
fields = xpath.Evaluate("child::field/child::name",
namesAreaComare[0].parentNode)
dictVar = {}
for fieldName in fields:
nodeField = fieldName.parentNode
fieldValue = xpath.Evaluate("child::value",nodeField)
name = fieldName.firstChild.nodeValue
value = ""
if fieldValue and fieldValue[0].firstChild:
value = fieldValue[0].firstChild.nodeValue
dictVar[name] = value
return dictVar
def getAreaFieldValues(self, nameArea, nameField, xmlArea):
"""По имени области и имени переменной выводит значениe переменной
поиск ведется только в 1-х потомках области xmlArea
"""
namesAreaComare = xpath.Evaluate(\
"child::area/child::caption[child::name='%s']" %(nameArea),
xmlArea)
fieldsVal = False
for areaComp in namesAreaComare:
fieldsVal = xpath.Evaluate(\
"child::field[child::name='%s'] "\
%(nameField), areaComp.parentNode)
if fieldsVal:
break
if not fieldsVal:
return False
fieldValue = xpath.Evaluate("child::value",
fieldsVal[0])
return fieldValue[0].firstChild.nodeValue
def joinArea(self, baseNode, xmlNewArea):
"""Объединяет область c областью Body (xmlNewArea c baseNode)"""
def appendArea(baseNode, xmlNewArea):
fieldsRemove = xpath.Evaluate(\
"descendant::field[child::action='drop']", xmlNewArea)
for rmNode in fieldsRemove:
parentNode = rmNode.parentNode
parentNode.removeChild(rmNode)
captionAreasRemove = xpath.Evaluate(\
"descendant::area/child::caption[child::action='drop']",
xmlNewArea)
for rmNodeCapt in captionAreasRemove:
rmNode = rmNodeCapt.parentNode
parentNode = rmNode.parentNode
parentNode.removeChild(rmNode)
self.setActionArea(xmlNewArea, "append")
# Добавляем разделитель областей во вложенные области
areaNodes = xpath.Evaluate('descendant::area',xmlNewArea)
for areaNode in areaNodes:
self.setActionArea(areaNode,"append")
parentNode = areaNode.parentNode
parentNode.insertBefore(self.sepAreas.cloneNode(True),
areaNode)
baseNode.appendChild(xmlNewArea)
# Добавляем разделитель областей
baseNode.insertBefore(self.sepAreas.cloneNode(True), xmlNewArea)
nodesNames = xpath.Evaluate('child::area/caption/name',baseNode)
nodesNewArea = xpath.Evaluate('child::caption/name',xmlNewArea)
if not nodesNames:
# Добавляем область
if nodesNewArea:
newAreaAction = self.getActionArea(xmlNewArea)
if not (newAreaAction == "drop" or newAreaAction == "replace"):
appendArea(baseNode, xmlNewArea)
return True
if not nodesNames or not nodesNewArea:
return False
nameArea = ""
if nodesNewArea[0].firstChild:
nameArea = nodesNewArea[0].firstChild.nodeValue.strip()
flagFindArea = False
baseNodes = []
for oName in nodesNames:
newAreaAction = self.getActionArea(xmlNewArea)
oArea = oName.parentNode.parentNode
oNameTxt = ""
if oName.firstChild:
oNameTxt = oName.firstChild.nodeValue
if nameArea == oNameTxt:
flagFindArea = True
# При использовании удаления
if newAreaAction == "drop":
prevNode = oName.parentNode.parentNode.previousSibling
removePrevNodes = []
while (prevNode) and self.getTypeField(prevNode) == "br":
removePrevNodes.append(prevNode)
prevNode = prevNode.previousSibling
for removeNode in removePrevNodes:
baseNode.removeChild(removeNode)
baseNode.removeChild(oName.parentNode.parentNode)
continue
elif newAreaAction == "replace":
oldAreaNode = oName.parentNode.parentNode
newAreaCaption = xpath.Evaluate('child::caption',
xmlNewArea)[0]
oldAreaCaption = xpath.Evaluate('child::caption',
oldAreaNode)[0]
if newAreaCaption and oldAreaCaption:
xmlNewArea.replaceChild(oldAreaCaption,newAreaCaption)
self.setActionArea(xmlNewArea,"replace")
baseNode.replaceChild(xmlNewArea,
oldAreaNode)
continue
baseNodes.append(oName.parentNode.parentNode)
newFields = xpath.Evaluate('child::field',xmlNewArea)
joinNewFields = xpath.Evaluate(\
"child::field[child::action='join']"
,xmlNewArea)
self.addNewFielsOldArea(newFields, joinNewFields, oArea)
if not flagFindArea:
# Добавляем область
if not (newAreaAction == "drop" or\
newAreaAction == "replace"):
appendArea(baseNode, xmlNewArea)
else:
tmpXmlNewAreas = xpath.Evaluate('child::area',xmlNewArea)
for na in tmpXmlNewAreas:
for bn in baseNodes:
self.joinArea(bn, na)
return True
def joinDoc(self, xmlNewDoc):
"""Объединяет два документа"""
newRootNode = xmlNewDoc.documentElement
newBodyNode = xpath.Evaluate('child::body',newRootNode)[0]
newImportBodyNode = self.doc.importNode(newBodyNode, True)
# Перед объединение области с документом
# удаляем комментарии
self.removeComment(newImportBodyNode)
self.joinBody(self.body, newImportBodyNode)
# расставляем BR
self.insertBRtoBody(self.body)
def getQuoteField(self, xmlField):
"""Выдает текст из поля"""
xmlQuotes = xpath.Evaluate('child::quote',xmlField)
br = ""
if xmlField.hasAttribute("type") and\
xmlField.getAttribute("type") == "br":
br = "\n"
if xmlQuotes:
field = xmlQuotes[0]
if field.firstChild:
return field.firstChild.nodeValue + br
return "" + br
def getFieldsArea(self, xmlArea):
"""Выдает потомков XML области"""
xmlFields = []
childNodes = xmlArea.childNodes
for node in childNodes:
if node.nodeType == node.ELEMENT_NODE:
if node.tagName == "area" or node.tagName == "field":
xmlFields.append(node)
return xmlFields
def getTypeField(self, xmlField):
"""Выдает тип поля"""
if xmlField.hasAttribute("type"):
return xmlField.getAttribute("type")
else:
return False
def getNameField(self, xmlField):
"""Выдает имя поля"""
xmlNameFields = xpath.Evaluate('child::name', xmlField)
if xmlNameFields and xmlNameFields[0].firstChild:
return xmlNameFields[0].firstChild.nodeValue
else:
return False
def getNameArea(self, xmlArea):
"""Выдает имя области"""
xmlNameAreas = xpath.Evaluate('child::caption/name', xmlArea)
if xmlNameAreas and xmlNameAreas[0].firstChild:
return xmlNameAreas[0].firstChild.nodeValue
else:
return False
def xmlToText(self, xmlAreas, text):
"""Преобразует список XML областей в текст"""
def getQuotesArea(xmlArea):
quotes = []
xmlQuotes = xpath.Evaluate('child::caption/quote',xmlArea)
for node in xmlQuotes:
if node.firstChild:
quotes.append(node.firstChild.nodeValue)
if len(quotes) == 0:
quotes.append("")
quotes.append("")
elif len(quotes) == 1:
quotes.append("")
return quotes
for i in xmlAreas:
if i.tagName == "area":
quotesI = getQuotesArea(i)
startAreaI = quotesI[0]
endAreaI = quotesI[1]
text.append(startAreaI)
xmlFieldsI = self.getFieldsArea(i)
for f in xmlFieldsI:
if f.tagName == "area":
quotesF = getQuotesArea(f)
startAreaF = quotesF[0]
endAreaF = quotesF[1]
text.append(startAreaF)
xmlFieldsF = self.getFieldsArea(f)
self.xmlToText(xmlFieldsF, text)
text.append(endAreaF)
else:
fieldF = self.getQuoteField(f)
text.append(fieldF)
text.append(endAreaI)
else:
fieldI = self.getQuoteField(i)
text.append(fieldI)
def getActionField(self, xmlField):
"""Выдает свойство action XML поля"""
xmlActions = xpath.Evaluate('child::action',xmlField)
if xmlActions and xmlActions[0].firstChild:
return xmlActions[0].firstChild.nodeValue
else:
return False
def getFieldValues(self, xmlField):
"""Выдает значения XML поля в виде массива"""
vals = []
xmlValues = xpath.Evaluate('child::value',xmlField)
if xmlValues:
for node in xmlValues:
if node.firstChild:
vals.append(node.firstChild.nodeValue)
return vals
def getActionArea(self, xmlArea):
"""Выдает свойство action XML области"""
xmlActions = xpath.Evaluate('child::caption/action',xmlArea)
if xmlActions and xmlActions[0].firstChild:
return xmlActions[0].firstChild.nodeValue
else:
return False
def delActionNodeArea(self, xmlArea):
"""Удаляет свойство action XML области"""
xmlActions = xpath.Evaluate('child::caption/action',xmlArea)
if xmlActions and xmlActions[0].firstChild:
parentNode = xmlActions[0].parentNode
parentNode.removeChild(xmlActions[0])
return True
else:
return False
def delActionNodeField(self, xmlField):
"""Удаляет свойство action XML поля"""
xmlActions = xpath.Evaluate('child::action',xmlField)
if xmlActions and xmlActions[0].firstChild:
parentNode = xmlActions[0].parentNode
parentNode.removeChild(xmlActions[0])
return True
else:
return False
# Создает распределенные списки
def postParserListSeplist(self, xmlArea):
"""Создает распределенные списки из элементов области"""
# Потомки
childNodes = self.getFieldsArea(xmlArea)
# содержит списки нод полей с одинаковыми именами в одной области
fieldsSeplist = {}
for node in childNodes:
if node.tagName == "area":
self.postParserListSeplist(node)
else:
fieldName = False
xmlFieldNameNodes = xpath.Evaluate('child::name',node)
if xmlFieldNameNodes and xmlFieldNameNodes[0].firstChild:
fieldName = xmlFieldNameNodes[0].firstChild.nodeValue
if fieldName:
if fieldsSeplist.has_key(fieldName):
fieldsSeplist[fieldName].append(node)
else:
fieldsSeplist[fieldName] = []
fieldsSeplist[fieldName].append(node)
for listNodes in fieldsSeplist.values():
if len(listNodes) > 1:
for node in listNodes:
node.setAttribute("type", "seplist")
def insertBRtoBody(self, xmlArea):
"""Добавляет необходимые переводы строк
"""
# Потомки
childNodes = self.getFieldsArea(xmlArea)
# нода BR
fieldXMLBr = self.createField("br",[],"",[],False, False)
# разделитель поля
fieldSplit = False
# Предыдущая нода
lastNode = False
# Cледующая нода
nextNode = False
lenChildNodes = len(childNodes)
for i in range(lenChildNodes):
node = childNodes[i]
lastTmpNode = node
# Нода area
if node.tagName == "area":
if self.getActionArea(node) == "append" or\
self.getActionArea(node) == "join":
self.delActionNodeArea(node)
if lastNode and lastNode.hasAttribute("type") and\
lastNode.getAttribute("type") == "br" or\
lastNode and lastNode.hasAttribute("type") and\
lastNode.getAttribute("type") == "comment":
indNext = i + 1
if indNext == lenChildNodes:
xmlArea.appendChild(fieldXMLBr.cloneNode(True))
else:
nextNode = childNodes[indNext]
lastTmpNode = xmlArea.insertBefore(\
fieldXMLBr.cloneNode(True),
nextNode)
else:
xmlArea.insertBefore(fieldXMLBr.cloneNode(True),
node)
self.insertBRtoBody(node)
# Нода field
else:
if self.getActionField(node) == "append" or\
self.getActionField(node) == "join":
self.delActionNodeField(node)
if lastNode and lastNode.hasAttribute("type") and\
lastNode.getAttribute("type") == "br" or\
lastNode and lastNode.hasAttribute("type") and\
lastNode.getAttribute("type") == "comment":
indNext = i + 1
if indNext == lenChildNodes:
xmlArea.appendChild(fieldXMLBr.cloneNode(True))
else:
nextNode = childNodes[indNext]
lastTmpNode = xmlArea.insertBefore(\
fieldXMLBr.cloneNode(True),
nextNode)
else:
xmlArea.insertBefore(fieldXMLBr.cloneNode(True),
node)
lastNode = lastTmpNode
def postParserList(self):
"""Находит подходящие XML области и делаем из них поля-массивы"""
xmlAreas = xpath.Evaluate('descendant::area', self.body)
for xmlArea in xmlAreas:
flagListXml = True
fieldValues = []
xmlFields = xpath.Evaluate('child::field',xmlArea)
if not xmlFields:
flagListXml = False
lenXmlFields = len(xmlFields)
lenBrArea = 0
for xmlField in xmlFields:
xmlNames = xpath.Evaluate('child::name',xmlField)
xmlVals = xpath.Evaluate('child::value',xmlField)
if xmlField.hasAttribute("type") and\
xmlField.getAttribute("type") == "br":
lenBrArea += 1
continue
if not xmlNames and not xmlVals:
flagListXml = False
break
if xmlNames and xmlNames[0].firstChild and\
xmlNames[0].firstChild.nodeValue:
flagListXml = False
break
if not (xmlVals and xmlVals[0].firstChild and\
xmlVals[0].firstChild.nodeValue):
flagListXml = False
break
else:
fieldValues.append(xmlVals[0].firstChild.nodeValue)
if lenXmlFields == lenBrArea:
flagListXml = False
if flagListXml:
nameNode = xpath.Evaluate('child::caption/name',xmlArea)[0]
fieldName = ""
if nameNode.firstChild:
fieldName = nameNode.firstChild.nodeValue
listArea = []
self.xmlToText([xmlArea],listArea)
fieldQuote = "".join(listArea)
fieldXMLBr = False
if fieldQuote and fieldQuote[-1] == "\n":
fieldQuote = fieldQuote[:-1]
fieldXMLBr = self.createField("br",[],"",[],False, False)
fieldXML = self.createField("list",
[fieldQuote],
fieldName, fieldValues,
False, False)
areaAction = self.getActionArea(xmlArea)
if areaAction:
self.setActionField(fieldXML, areaAction)
parentNode = xmlArea.parentNode
parentNode.insertBefore(fieldXML,xmlArea)
if fieldXMLBr:
parentNode.insertBefore(fieldXMLBr,xmlArea)
parentNode.removeChild(xmlArea)
class blocText:
"""Разбиваем текст на блоки"""
def splitTxtToBloc(self, text ,openTxtBloc,closeTxtBloc,
commentTxtBloc, sepField):
"""Делит текст на блоки (без заголовков)
openTxtBloc - регулярное выражение для начала блока
closeTxtBloc - регулярное выражение для конца блока
commentTxtBloc - регулярное выражение - комментарий
возвращает блоки текста
"""
blocs = []
level = 0
# Нахождение нескольких блоков в строке
# разделители линий, разделителями могут быть ("","\n")
sepsLines = []
# линии
txtLines = []
# Исходные строки
txtLinesSrc = text.splitlines()
for line in txtLinesSrc:
lineBR = ""
lineTmpA = line
closeBl = False
txtLinesTmp = []
commentSpl = commentTxtBloc.split(line)
flagCommentLine = False
if commentSpl[0].strip():
closeBl = True
if len(commentSpl) > 1:
commentBl = commentTxtBloc.search(line)
textLine =commentSpl[0]
commentLine = line[commentBl.start(0):]
lineTmpA = textLine
flagCommentLine = True
while (closeBl):
closeBl = sepField.search(lineTmpA)
if closeBl:
lineTmpB = lineTmpA[closeBl.end(0):]
txtLinesTmp.append(lineTmpA[:closeBl.end(0)])
lineTmpA = lineTmpB
if lineTmpA.strip():
txtLinesTmp.append(lineTmpA)
# Если есть значение и комментарий в строке
if flagCommentLine:
for l in txtLinesTmp:
txtLines.append(l)
sepsLines.append("")
if not txtLinesTmp:
txtLines.append(textLine)
sepsLines.append("")
txtLines.append(commentLine)
sepsLines.append("\n")
# Если есть несколько блоков в строке
elif len(txtLinesTmp)>1 and txtLinesTmp[1].strip():
lenTmpLines = len(txtLinesTmp)
for l in range(lenTmpLines):
txtLines.append(txtLinesTmp[l])
if l == lenTmpLines-1:
sepsLines.append("\n")
else:
sepsLines.append("")
# Cтрока не преобразована
else:
txtLines.append(line)
sepsLines.append("\n")
# разбивание на блоки
z = 0
bl = ""
for i in txtLines:
if commentTxtBloc.split(i)[0].strip() and openTxtBloc.search(i):
level += len(openTxtBloc.split(i)) - 1
if commentTxtBloc.split(i)[0].strip() and closeTxtBloc.search(i):
level -= len(closeTxtBloc.split(i)) - 1
bl += i + sepsLines[z]
if level == 0:
if bl:
blocs.append(bl)
bl = ""
z += 1
# cоздание блоков с элементами не входящими в блоки
realBlocs = []
z = 0
bl = ""
for i in blocs:
txtLines = i.splitlines()
if len(txtLines) > 0:
line = txtLines[0]
else:
line = i
if commentTxtBloc.split(i)[0].strip() and openTxtBloc.search(line):
if bl:
realBlocs.append(bl)
bl = ""
realBlocs.append(i)
else:
bl += i
z += 1
if bl:
realBlocs.append(bl)
bl = ""
if level == 0:
if text and text[-1] != "\n":
tmpBlocs = realBlocs.pop()
tmpBlocs = tmpBlocs[:-1]
realBlocs.append(tmpBlocs)
return realBlocs
else:
return []
def findArea(self, text, reTextHeader, reTextArea, numGroupArea=0):
""" Делит текст на области (с заголовками)
reTextHeader - регулярное выражение для заголовка области
reTextArea - регулярное выражение для всей области
numGroupArea - номер групы результата поиска по регулярному выражению
по всей области
возвращает два списка: первый - заголовки, второй - тела областей без
заголоков
"""
# Заголовки областей
headersArea = []
# Тексты областей без заголовков
textBodyArea = []
r = reTextArea.search(text)
if not r:
headersArea.append("")
textBodyArea.append(text)
return (headersArea, textBodyArea)
txtWr = text
while r:
textArea = r.group(numGroupArea)
txtSpl = txtWr.split(textArea)
area = txtSpl[0]
txtWr = txtSpl[1]
if area:
headersArea.append("")
textBodyArea.append(area)
res = reTextHeader.search(textArea)
header = textArea[:res.end()]
body = textArea[res.end():]
headersArea.append(header)
textBodyArea.append(body)
if txtWr:
r = reTextArea.search(txtWr)
else:
r = False
if txtWr:
headersArea.append("")
textBodyArea.append(txtWr)
return (headersArea, textBodyArea)
def findBloc(self, text, captionTxtBloc, bodyTxtBloc):
""" Делит текст на блоки (с заголовками)
captionTxtBloc - регулярное выражение для заголовка блока
bodyTxtBloc - регулярное выражение для тела блока
возвращает два списка: первый - заголовки, второй - тела блоков
"""
# Заголовки блоков
headersTxt = []
# Тексты блоков
blocsTxt = []
r = captionTxtBloc.search(text)
if r:
headersTxt.append(r.group(0))
txtSpl = text.split(r.group(0))
blocTxt = txtSpl[0]
txtWr = txtSpl[1]
rb = bodyTxtBloc.search(blocTxt)
if not blocTxt:
blocsTxt.append(blocTxt)
if rb:
blocsTxt.append(rb.group(0))
while (r):
r = captionTxtBloc.search(txtWr)
if r:
headersTxt.append(r.group(0))
txtSpl = txtWr.split(r.group(0))
blocTxt = txtSpl[0]
txtWr = txtSpl[1]
rb = bodyTxtBloc.search(blocTxt)
if rb:
blocsTxt.append(rb.group(0))
else:
blocsTxt.append(txtWr)
if headersTxt and blocsTxt:
if len(headersTxt)>len(blocsTxt):
blocsTxt.insert(0,"")
elif len(headersTxt)<len(blocsTxt):
headersTxt.insert(0,"")
if len(headersTxt)!=len(blocsTxt):
return False
return (headersTxt, blocsTxt)
else:
return False
class _file(_error, scan):
"""Класс для работы с файлами
"""
def __init__(self):
# Имя файла старого шаблона
self.nameFileOld = ""
# Старый шаблон
self.oldTemplate = ""
# Имя файла нового шаблона
self.nameFileNew = ""
# Новый шаблон
self.newTemplate = ""
# Дескриптор файла нового шаблона
self.FN = False
# Дескриптор файла старого шаблона
self.FO = False
def getFileType(self, fileName=""):
"""выдать тип файла (text, bin)
"""
if fileName:
nameFile = fileName
elif self.nameFileNew:
nameFile = self.nameFileNew
else:
return ""
sp = subprocess.Popen("file '%s'"%nameFile, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
close_fds=True, shell=True)
fout, fin, ferr = (sp.stdout, sp.stdin, sp.stderr)
fin.close()
textLine = fout.readline()
fout.readlines()
fout.close()
retText = ""
if textLine:
listTextLine = textLine.split(":")
if len(listTextLine) == 2:
textFormats = ["text", "XML"]
retText = "bin"
fileTypeString = listTextLine[1]
for textFormat in textFormats:
if textFormat in fileTypeString:
retText = "text"
break
ferr.close()
return retText
def absFileName(self, nameFile):
"""Вычисление пути к файлу"""
pathList = nameFile.split("/")
chortNameFile = pathList.pop()
absPath = os.path.abspath("/".join(pathList))
File = absPath + "/" + chortNameFile
return File
def saveOldFile(self):
"""Записать конфигурацию"""
if self.FO:
self.FO.truncate(0)
self.FO.seek(0)
if not self.oldTemplate:
self.oldTemplate = self.newTemplate
try:
self.FO.write(self.oldTemplate)
except:
self.setError (_("not open file:" ) + self.nameFileOld)
return False
return True
def openNewFile(self, nameFileNew):
"""Открыть файл шаблона"""
try:
FN = open (nameFileNew, "r")
except:
self.setError (_("not open file:" ) + nameFileNew)
return False
return FN
def closeNewFile(self):
if self.FN:
self.FN.close()
self.FN = False
def __closeOldFile(self):
if self.FO:
self.FO.close()
self.FO = False
def __openOldFile(self, nameFileOld, funcCreateFile, removeLink):
"""Открыть конфигурационный файл"""
FO = False
try:
if os.path.islink(nameFileOld) and removeLink:
# если ссылка то удаляем её
os.unlink(nameFileOld)
FO = open (nameFileOld, "r+")
except:
#try:
# Создаем новый файл
funcCreateFile.__call__(nameFileOld)
FO = open(nameFileOld, "r+")
#except:
#self.setError (_("not open file:" ) + nameFileOld)
#return False
return FO
def openFiles(self, nameFileNew, nameFileOld, funcCreateFile, seekFileNew,
removeLink):
"""Открывает шаблон и конфигурационный файл"""
self.oldTemplate = ""
self.newTemplate = ""
self.closeFiles()
self.nameFileOld = self.absFileName(nameFileOld)
self.nameFileNew = self.absFileName(nameFileNew)
self.FN = self.openNewFile(self.nameFileNew)
self.FO = self.__openOldFile(self.nameFileOld,funcCreateFile,removeLink)
if not (self.FN and self.FO):
return False
self.FN.seek(seekFileNew)
self.newTemplate = self.FN.read()
self.oldTemplate = self.FO.read()
return True
def __del__(self):
self.closeFiles()
def closeFiles(self):
"""Закрытие файлов"""
self.closeNewFile()
self.__closeOldFile()
class utfBin:
"""Класс для преобразования в utf-8
преобразование бинарного или смеси бинарного и utf-8 кода в utf-8 и
обратное преобразование
методы класса encode и decode
"""
def _retUTF(self, char):
byte = ord(char)
if byte<=127:
return ('_ch_',1)
elif byte<=191:
return ('_nb_',1)
elif byte<=223:
return ('_fb_',2)
elif byte<=239:
return ('_fb_',3)
elif byte<=247:
return ('_fb_',4)
else:
return ('_er_',1)
def _sumbUtf(self, symbols, lenTail):
if not symbols:
return (False,0)
lenSymb = len(symbols)
if lenSymb >= 4:
l = 4
elif lenSymb >= 3:
l = 3
elif lenSymb >= 2:
l = 2
else:
if symbols[0] == '_ch_':
return (True,1)
else:
return (False,1)
result = False
for i in range(l):
if i == 0 and symbols[i] != '_fb_':
break
elif i > 0 and symbols[i] != '_nb_':
break
i += 1
if lenTail>1 and lenTail != i:
return (False,1)
if i > 0:
result = True
return (result, i)
def encode(self, text):
"""Кодирует смешанный формат в UTF-8"""
ind = 0
l = 0
utf = []
lenUtf = []
indErr = []
i = 0
for ch in text:
r, l = self._retUTF(ch)
utf.append(r)
lenUtf.append(l)
i+=1
while 1:
if utf[ind] == '_fb_':
res, l = self._sumbUtf(utf[ind:(ind+lenUtf[ind])],lenUtf[ind])
if res == False:
indErr.append(ind)
if l>0:
ind +=l
if ind >= len(utf):
break
else:
if utf[ind] != '_ch_':
indErr.append(ind)
ind +=1
if ind >= len(utf):
break
if indErr:
lenIndErr = len(indErr)
block = []
blocks = []
if lenIndErr > 1:
i = 1
while 1:
if i == 1:
block.append(indErr[i-1])
if indErr[i] - indErr[i-1] == 1:
block.append(indErr[i])
else:
if block:
blocks.append(block)
block = []
block.append(indErr[i])
i +=1
if i >= lenIndErr:
break
else:
block.append(indErr[0])
if block:
blocks.append(block)
listErr = []
for block in blocks:
string = ""
for elem in block:
string += hex(ord(text[elem]))[-2:]
listErr.append((block[0],"__hex__?%s?__hex__" %string,elem))
textOut = text
deltaInd = 0
for erEl in listErr:
startInd = erEl[0] + deltaInd
endInd = erEl[2] + 1 + deltaInd
textOut = textOut[:startInd] + erEl[1] + textOut[endInd:]
deltaInd += len(erEl[1])-(erEl[2]-erEl[0]+1)
return textOut
def decode(self, text):
"""Декодирует UTF-8 в смешанный формат"""
varStart = "__hex__\?"
varEnd = "\?__hex__"
# -1 Это экранирование '?' которое тоже считается
deltVarStart = len(varStart)-1
deltVarEnd = len(varEnd)-1
reVar = re.compile(("%s[a-f0-9]+%s")%(varStart,varEnd),re.M)
resS = reVar.search(text)
textTemplateTmp = text
while resS:
mark = textTemplateTmp[resS.start():resS.end()]
hexString = mark[deltVarStart:-deltVarEnd]
i = 0
stringInsert = ""
hexCode = ""
for ch in hexString:
if i>=1:
hexCode += ch
stringInsert += chr(int(hexCode, 16))
hexCode = ""
i = 0
else:
hexCode += ch
i += 1
textTemplateTmp = textTemplateTmp.replace(mark, stringInsert)
resS = reVar.search(textTemplateTmp)
return textTemplateTmp
class _shareTemplate:
"""Общие аттрибуты для классов шаблонов"""
# Метка начала переменной
varStart = "#-"
# Метка конца переменной
varEnd = "-#"
_deltVarStart = len(varStart)
_deltVarEnd = len(varEnd)
class templateFunction(_shareTemplate):
"""Класс для функций шаблонов"""
# Список названий функций шаблона
namesTemplateFunction = []
# Словарь {название функции шаблона: функция шаблона, ...}
templateFunction = {}
# Имена установленных программ
installProg = []
# Версии установленных программ
installProgVersions = []
# кеш вызванных значений программа, номер версии
cacheInstallProg = {}
# Регулярное выражение для сложения
sNum = re.compile("\-[^\-\+]+|[^\-\+]+")
# Регулярное выражение для умножениея и деления
sMD = re.compile("[^\-\+\*\/]+")
# Имя обрабатываемого шаблона
nameTemplate = ""
def __init__(self, objVar):
# Если не определен словарь функций шаблона
#print "dict", templateFunction.__dict__.items()
if not self.templateFunction:
# префикс функций шаблона
pref = "func"
# cписок [(название функции, функция), ...]
dictFunc = filter(lambda x: x[0].startswith(pref) and\
hasattr(x[1],"__call__"),
self.__class__.__dict__.items())
# удаляем у названия функции префикс и переводим остаток названия
# в нижний регистр
dictFunc = map(lambda x: (x[0][len(pref):].lower(), x[1]), dictFunc)
# Формируем словарь функций шаблона
self.templateFunction.update(dictFunc)
# Формируем список функций шаблона
for nameFunction in self.templateFunction.keys():
self.namesTemplateFunction.append(nameFunction)
# Объект хранения переменных
self.objVar = objVar
self._reFunc = re.compile(("%s[a-zA-Z0-9_\-\+\(\)\, \*\/\.]+%s")\
%(self.varStart,self.varEnd),re.M)
def equalTerm(self, term, localVars):
"""Метод для вычисления выражения"""
terms = self.sNum.findall(term)
if terms:
strNumers = []
for n in terms:
strNum = n.strip()
if "*" in strNum or "/" in strNum:
strNum = self.multAndDiv(strNum,localVars)
try:
num = int(strNum)
except:
minus = False
if strNum[:1] == "-":
minus = True
strNum = strNum[1:]
if localVars.has_key(strNum):
num = localVars[strNum]
elif self.objVar.exists(strNum):
try:
num = int(self.objVar.Get(strNum))
except:
print _("error in template %s")%self.nameTemplate
print _("error var %s not int")%str(strNum)
exit(1)
else:
print _("error in template %s")%self.nameTemplate
print _("error local var %s not defined")\
%str(strNum)
exit(1)
if minus:
num =-num
strNumers.append(num)
return sum(strNumers)
print _("error in template %s")%self.nameTemplate
print _("error template term %s, incorrect data")%str(term)
exit(1)
def multAndDiv(self, term, localVars):
"""Метод для умножения и деления"""
termTmp = term
varsLocal = self.sMD.findall(term)
for var in varsLocal:
flagVarTxt = True
try:
int(var)
except:
flagVarTxt = False
if flagVarTxt:
continue
varReplace = str(self.equalTerm(var, localVars))
termTmp = termTmp.replace(var,varReplace)
ret = eval(termTmp)
return ret
def funcSum(self, funTxt, resS, localVars, textTemplateTmp):
"""Функция шаблона, вычисляет функцию sum()"""
terms = funTxt.replace(" ","").split(",")
# Название локальной переменной
nameLocVar = terms[0]
if not localVars.has_key(nameLocVar):
localVars[nameLocVar] = 0
if len(terms) == 2:
if terms[1].strip():
localVars[nameLocVar] = self.equalTerm(terms[1], localVars)
replace = str(localVars[nameLocVar])
else:
replace = ""
textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\
textTemplateTmp[resS.end():]
elif len(terms) == 3:
if terms[1].strip():
replaceInt = self.equalTerm(terms[1], localVars)
replace = str(replaceInt)
else:
replace = ""
localVars[nameLocVar] = self.equalTerm(terms[2], localVars)
textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\
textTemplateTmp[resS.end():]
else:
print _("error in template %s")%self.nameTemplate
print _("error template term %s")%str(funTxt)
exit(1)
return textTemplateTmp
def funcLoad(self, funTxt, resS, localVars, textTemplateTmp):
"""Функция шаблона load(),
если файл существует читает из файла локальную переменную
если один параметр - выводит значение локальной переменной
"""
terms = funTxt.replace(" ","").split(",")
if not terms[0].strip() or\
(len(terms)==2 and not terms[1].strip()) or\
len(terms)>2:
print _("error in template %s")%self.nameTemplate
print _("error template term %s")%str(funTxt)
exit(1)
if len(terms) == 2:
if not terms[0] in ["ver","num","char","key"]:
print _("error in template %s")%self.nameTemplate
print _("error template term %s")%str(funTxt)
print _("first argument function is not 'ver' or 'num' or\
'char'")
exit(1)
if len(terms) == 1:
fileName = terms[0].strip()
if fileName[0] != "/":
print _("error in template %s")%self.nameTemplate
print _("error template term %s")%str(funTxt)
print _("incorrect path %s")%fileName
exit(1)
else:
fileName = terms[1].strip()
if fileName[0] != "/":
print _("error in template %s")%self.nameTemplate
print _("error template term %s")%str(funTxt)
print _("incorrect path %s")%fileName
exit(1)
replace = ""
if os.path.exists(fileName):
FD = open(fileName)
replace = FD.read().strip()
FD.close
if not replace and len(terms) == 2 and terms[0] in ["ver","num"]:
replace = "0"
textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\
textTemplateTmp[resS.end():]
return textTemplateTmp
def getInstallPkgGentoo(self, names = [], versions = []):
"""Выдает два списка, инсталлированные программы и номера версий"""
baseDir = "/var/db/pkg"
pkgs = []
reVer = re.compile("(?<=\-)\d+\.?\d*\.?\d*")
def getFilesDir(pkgs, dirname, names):
for nameFile in names:
absNameFile = os.path.join(dirname,nameFile)
if os.path.isdir(absNameFile):
tail = absNameFile.split(baseDir)
if len(tail)==2:
tail = tail[1].split('/')
if len(tail)==3 and tail[1]!='virtual':
pkgs.append(tail[2])
return True
os.path.walk(baseDir,getFilesDir, pkgs)
pkgs.sort()
for pkg in pkgs:
findVer = reVer.search(pkg)
if findVer:
ver = findVer.group()
versions.append(ver)
names.append(pkg.split(ver)[0][:-1])
#return pkgs
return names, versions
def pkg(self, nameProg, names, versions):
"""Выдает установленные версии по имени программы"""
# Значение версии из кеша
if nameProg in self.cacheInstallProg:
return self.cacheInstallProg[nameProg]
i = 0
vers = []
for name in names:
if nameProg == name:
while nameProg == names[i]:
vers.append(versions[i])
i += 1
break
i += 1
if vers:
version = vers[-1]
# Запись значения версии в кеш
self.cacheInstallProg[nameProg] = version
return version
else:
return ""
def funcPkg(self, funTxt, resS, localVars, textTemplateTmp):
"""Функция шаблона pkg(), выдает номер версии программы"""
terms = funTxt.replace(" ","")
# Название программы
nameProg = terms
if not self.installProg:
# Получение всех названий и версий установленных программ
self.installProg,self.installProgVersions =\
self.getInstallPkgGentoo(names=self.installProg,
versions=self.installProgVersions)
replace = self.pkg(nameProg,self.installProg,self.installProgVersions)
textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\
textTemplateTmp[resS.end():]
return textTemplateTmp
def funcRnd(self, funTxt, resS, localVars, textTemplateTmp):
"""Функция шаблона rnd(), выдает строку случайных символов
первый аргумент:
'num' - числа,
'pas' - цифры и буквы
второй аргумент:
количество символов
"""
terms = funTxt.replace(" ","").split(",")
if not terms[0].strip() or\
(len(terms)==2 and not terms[1].strip()) or\
len(terms)!=2:
print _("error in template %s")%self.nameTemplate
print _("error template term %s")%str(funTxt)
exit(1)
fArgvNames = ['num','pas']
if not terms[0] in fArgvNames:
print _("error in template %s")%self.nameTemplate
print _("error template term %s")%str(funTxt)
print _("first argument function is not 'num' or 'pas'")
exit(1)
try:
lenStr = int(terms[1])
except:
print _("error in template %s")%self.nameTemplate
print _("error template term %s")%str(funTxt)
print _("two argument function is not number")
exit(1)
if terms[0] == fArgvNames[0]:
replace=''.join([random.choice(string.digits)\
for i in xrange(lenStr)])
elif terms[0] == fArgvNames[1]:
replace=''.join([random.choice(string.ascii_letters + \
string.digits) for i in xrange(lenStr)])
else:
print _("error in template %s")%self.nameTemplate
print _("error template term %s")%str(funTxt)
exit(1)
textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\
textTemplateTmp[resS.end():]
return textTemplateTmp
def funcCase(self, funTxt, resS, localVars, textTemplateTmp):
"""Функция шаблона case(), выдает переменную в определенном регистре
первый аргумент:
'upper' - верхний регистр,
'lower' - нижний регистр,
'capitalize' - первая буква в верхнем регистре
второй аргумент:
название переменной
"""
terms = funTxt.replace(" ","").split(",")
if not terms[0].strip() or\
(len(terms)==2 and not terms[1].strip()) or len(terms)!=2:
print _("error in template %s")%self.nameTemplate
print _("error template term %s")%str(funTxt)
exit(1)
fArgvNames = ['upper','lower','capitalize']
if not terms[0] in fArgvNames:
print _("error in template %s")%self.nameTemplate
print _("error template term %s")%str(funTxt)
print _("first argument function is not 'upper' or 'lower' or\
'capitalize'")
exit(1)
try:
strValue = str(self.objVar.Get(terms[1]))
except:
print _("error in template %s")%self.nameTemplate
print _("error var %s not found")%str(terms[1])
exit(1)
replace = ""
strValue = _toUNICODE(strValue)
if terms[0] == 'upper':
replace = strValue.upper()
elif terms[0] == 'lower':
replace = strValue.lower()
elif terms[0] == 'capitalize':
replace = strValue.capitalize()
if replace:
replace = replace.encode("UTF-8")
textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\
textTemplateTmp[resS.end():]
return textTemplateTmp
def applyFuncTemplate(self, textTemplate, nameTemplate):
"""Применяет функции к тексту шаблона"""
# Локальные переменные
localVars = {}
# Имя обрабатываемого шаблона
self.nameTemplate = nameTemplate
# Регулярное выражение для поиска функции в шаблоне
reFunc = self._reFunc
resS = reFunc.search(textTemplate)
textTemplateTmp = textTemplate
while resS:
mark = textTemplateTmp[resS.start():resS.end()]
funTxt = mark[self._deltVarStart:-self._deltVarEnd]
funcName, spl, funcEnd = funTxt.partition("(")
if funcName in self.namesTemplateFunction:
# аргументы функции - '(' аргументы ')'
funArgv = funcEnd.rpartition(")")[0]
# вызов функции шаблона
textTemplateTmp = self.templateFunction[funcName](self, funArgv,
resS, localVars,
textTemplateTmp)
resS = reFunc.search(textTemplateTmp)
else:
print _("error in template %s")%self.nameTemplate
print _("error template term %s")%str(funTxt)
print _("can not found template function '%s'")%funTxt
exit(1)
return textTemplateTmp
class template(_file, _terms, xmlShare, _shareTemplate):
"""Класс для работы с шаблонами
На вход 2 параметра: объект хранения переменных, имя сервиса - не
обязательный параметр
"""
# Импортированные классы поддерживаемых форматов шаблонов
importFormats = {}
# Название файла шаблона директории
templDirNameFile = ".calculate_directory"
def __init__(self, objVar, dirsFilter=[], filesFilter=[]):
# Необрабатываемые директории
self.dirsFilter = dirsFilter
# Необрабатываемые файлы
self.filesFilter = filesFilter
_file.__init__(self)
# Словарь для создания объектов новых классов по образцу
# (proftpd создается на основе apache)
self.newObjProt = {'proftpd':'apache'}
# Заголовок title
self.__titleHead = "--------------------------------------\
----------------------------------------"
self._titleBody = ""
self._titleList = (_("Modified"), _("File of a template"))
self._reVar = re.compile(("%s[a-zA-Z0-9_-]+%s")\
%(self.varStart,self.varEnd),re.M)
# Условия
self._reTermBloc = re.compile("#\?(?P<rTerm>[a-zA-Z0-9\-_]+)\
(?P<func>\([a-zA-Z0-9_\-\+\,\*\/\.]+\))?\
(?P<lTerm>[\>\<\=\!\&\|]+\
[a-zA-Z0-9\>\<\=\!\|\&\-\+\*\/_\.\,\(\)]*)#\
\n*(?P<body>.+?)\n*#(?P=rTerm)#(?P<end>[ ,\t]*\n?)",re.M|re.S)
# Объект с переменными
self.objVar = objVar
# Базовая директория переноса шаблонов "/mnt/calculate" или "/" и.т.д
# По умолчанию /
self._baseDir = self.objVar.Get("cl_root_path")
if self._baseDir == "/":
self._baseDir = ""
# Объект функций шаблона
self.functObj = templateFunction(self.objVar)
# Метод применения функций к шаблонам
self.applyFuncTemplate = self.functObj.applyFuncTemplate
def __octToInt(self, strOct):
"""Преобразование восьмеричного в целое (ввод строка, вывод число)"""
if strOct:
z = 0
i = 0
lst = list(strOct)
lst.reverse()
for ch in lst:
try:
v = int(ch)
except:
self.setError(_("Not valid oct value: ") + str(strOct))
return False
if v > 7:
self.setError (_("Not valid oct value: ") + str(strOct))
return False
z = z + v*8**i
i = i +1
return z
else:
self.setError (_("Empty oct value"))
return False
def getFormatObj(self, formatTemplate, textTemplate):
"""Создание объекта формата шаблона.
Объект создается на основании формата шаблона и текста шаблона"""
if formatTemplate in self.importFormats:
classFormat = self.importFormats[formatTemplate]
else:
try:
classFormat = getattr(__import__("format.%s"%formatTemplate,
globals(), locals(),
[formatTemplate]),
formatTemplate)
except (ImportError, AttributeError):
#Создаем объект из self.newObjProt с помощью
# метаклассов
if formatTemplate in self.newObjProt:
# Прототип класса
nameProt = self.newObjProt[formatTemplate]
try:
classProt = getattr(__import__("format.%s"%nameProt,
globals(), locals(),
[nameProt]),
nameProt)
except (ImportError, AttributeError):
return False
newCl = self.createNewClass(formatTemplate, (classProt,))
self.importFormats[formatTemplate] = newCl
return newCl(textTemplate)
else:
return False
self.importFormats[formatTemplate] = classFormat
return classFormat(textTemplate)
def createConfFile(self, pathTemplate, path=False):
"""Создает конфигурационный файл если его нет"""
# Создаем директорию если ее нет
if not path:
path = os.path.split(pathTemplate)[0]
if not os.path.exists(path):
createDirs = self.createDir(path)
if not createDirs:
self.setError (_("Can not create file:" ) + pathTemplate)
return False
# Создаем файл если его нет
if not os.path.exists(pathTemplate):
try:
dMode, uid, gid = getModeFile(path)
#mode = dMode & ~0111
# Создаем файл
open(pathTemplate,"w").close()
#os.chmod(pathTemplate, mode)
os.chown(pathTemplate, uid, gid)
except:
self.setError (_("Can not create file:" ) + pathTemplate)
return False
return True
def createDir(self, dirName, mode=False, uid=False, gid=False):
"""Создает директорию"""
if os.access(dirName, os.F_OK):
return [dirName]
else:
dMode = False
prevDir, tmpSubdir = os.path.split(dirName)
createDirs = []
while not os.access(prevDir, os.F_OK):
createDirs.append(prevDir)
prevDir = os.path.split(prevDir)[0]
try:
tmpMode,dUid,dGid = getModeFile(prevDir)
except OSError:
self.setError (_("Not access dir: " ) + prevDir)
return False
if not mode is False:
dMode = mode
if not uid is False:
dUid = uid
if not gid is False:
dGid = gid
createDirs.reverse()
for nameDir in createDirs:
try:
if dMode:
os.mkdir(nameDir, dMode)
else:
os.mkdir(nameDir)
os.chown(nameDir, dUid, dGid)
except:
self.setError (_("Can not create dir: " ) + nameDir)
return False
try:
if dMode:
os.mkdir(dirName, dMode)
else:
os.mkdir(dirName)
os.chown(dirName, dUid, dGid)
createDirs.append(dirName)
except:
self.setError (_("Can not create dir: " ) + dirName)
return False
return createDirs
def applyVarsTemplate(self, textTemplate, nameTemplate):
""" Заменяет переменные на их значения
"""
resS = self._reVar.search(textTemplate)
textTemplateTmp = textTemplate
while resS:
mark = textTemplateTmp[resS.start():resS.end()]
varName = mark[self._deltVarStart:-self._deltVarEnd]
varValue = ""
try:
varValue = str(self.objVar.Get(varName))
except self.objVar.DataVarsError, e:
print _("error in template %s")%nameTemplate
print e
exit(1)
textTemplateTmp = textTemplateTmp.replace(mark, varValue)
resS = self._reVar.search(textTemplateTmp)
return textTemplateTmp
def applyTermsTemplate(self,textTemplate,nameTemplate,nameSystemFile=False):
""" Применяет условия, к условным блокам текста
"""
textTerm = ""
resS = self._reTermBloc.search(textTemplate)
textTemplateTmp = textTemplate
def function(text):
"""Функция обработки функций в заголовке"""
return self.applyFuncTemplate(text, nameTemplate)
if nameSystemFile:
while resS:
mark = resS.group(0)
body = resS.group("body")
end = resS.group("end")
parent = resS.group("func")
if not parent:
parent = ""
term = resS.group("rTerm") + parent +\
resS.group("lTerm")
if self._equalTerm(term, _("content template not valid: ")+\
nameTemplate, function):
textTemplateTmp = textTemplateTmp.replace(mark, body+end)
else:
textTemplateTmp = textTemplateTmp.replace(mark, "")
resS = self._reTermBloc.search(textTemplateTmp)
else:
while resS:
mark = resS.group(0)
body = resS.group("body")
end = resS.group("end")
term = resS.group("rTerm") + resS.group("lTerm")
if self._equalTerm(term, _("content template not valid: ")+\
nameTemplate):
textTemplateTmp = textTemplateTmp.replace(mark, body+end)
else:
textTemplateTmp = textTemplateTmp.replace(mark, "")
resS = self._reTermBloc.search(textTemplateTmp)
return textTemplateTmp
def getTitle(self, comment, commentList):
"""Выдает заголовок шаблона ( версия и.т.д)"""
if comment:
commentFirst = comment
commentInsert = comment
commentLast = comment
flagList = False
# В случае открывающего и закрывающего комментария
if type(comment) == types.TupleType and len(comment) == 2:
commentFirst = comment[0]
commentInsert = ""
commentLast = comment[1]
flagList = True
if flagList:
self._titleBody = commentFirst + "\n"
else:
self._titleBody = commentFirst + self.__titleHead + "\n"
z = 0
lenCommentList = len(commentList) - 1
for com in self._titleList:
if lenCommentList < z:
self._titleBody += commentInsert + " " + com + "\n"
else:
self._titleBody += commentInsert + " " + com +\
" " + commentList[z] + "\n"
z += 1
if flagList:
self._titleBody += commentLast +"\n"
else:
self._titleBody += commentLast + self.__titleHead + "\n"
return self._titleBody
else:
return ""
def numberAllTemplates(self, number):
"""Количество шаблонов
Вызов происходит перед наложением шаблонов
в момент вызова в number находится количество обрабатываемых файлов
Наследуемая функция
Используется для отображения прогресса при наложениии шаблонов
"""
return True
def numberProcessTemplates(self, number):
"""Номер текущего обрабатываемого шаблона
Вызов происходит при наложении шаблона
в момент вызова в number находится номер обрабатываемого шаблона
Наследуемая функция
Используется для отображения прогресса при наложениии шаблонов
"""
return True
def __clearInErrorDirObj(self, dirObj):
"""Очищает объект директории при ошибке"""
# директории [(путь к директории, свойства директории)...]
dirObj.dirs = []
# файлы [(путь к файлу, свойства файла)...]
dirObj.files = []
# Ошибка
dirObj.flagError = True
return dirObj
def __scanDir(self, templatesDir, dirObj, dirProperties=()):
"""Измененное cканирование одной директории"""
# сканирование файлов и директорий (следующие сканирования)
if dirProperties:
# Выход при ошибке
if dirObj.flagError:
return dirObj
dirName, prop = dirProperties
# В случае удаления не смотрим внутренние директории
if "append" in prop and prop["append"] == "remove":
return dirObj
filesOrDirs = os.listdir(dirName)
# Сортируем файлы и директории
filesOrDirs.sort()
# Добавляем директорию и ее свойства
dirObj.dirs.append((dirName, prop))
if self.templDirNameFile in filesOrDirs:
# Удаляем файл описания директории из списка файлов
filesOrDirs.remove(self.templDirNameFile)
for fileOrDir in filesOrDirs:
# Выход при ошибке
if dirObj.flagError:
return dirObj
absPath = os.path.join(dirName,fileOrDir)
statInfo = os.lstat(absPath)[stat.ST_MODE]
if stat.S_ISREG(statInfo):
# Свойства файла
propF, applyFile=self.__isApplyHeadTemplate(dirObj.fHeadObj,
absPath)
if self.getError():
self.setError(_("Incorrect template: " ) + absPath)
return self.__clearInErrorDirObj(dirObj)
if not applyFile:
continue
# Обработка skip
if "append" in propF and propF["append"] == "skip":
continue
if not "path" in propF:
propF["path"] = prop["_real_path"]
if not "name" in propF:
propF["name"] = os.path.split(absPath)[1]
propF["_real_path"] = os.path.join(propF["path"],
propF["name"])
dirObj.files.append((absPath, propF))
elif stat.S_ISDIR(statInfo):
# Информация о директории
pDir = {}
# Файл информации о директории
dirInfoFile = os.path.join(absPath,
self.templDirNameFile)
if os.path.exists(dirInfoFile) and\
stat.S_ISREG(os.lstat(dirInfoFile)[stat.ST_MODE]):
# Настройки директории и применение
pDir, applyDir = self.__isApplyHeadDir(dirObj.dHeadObj,
dirInfoFile)
if self.getError():
self.setError(_("Incorrect template: " ) +\
dirInfoFile)
return self.__clearInErrorDirObj(dirObj)
if not applyDir:
continue
if not "path" in pDir:
pDir["path"] = prop["_real_path"]
if not "name" in pDir:
pDir["name"] = os.path.split(absPath)[1]
# Обработка skip
if "append" in pDir and pDir["append"] == "skip":
pDir["_real_path"] = pDir["path"]
else:
pDir["_real_path"] = os.path.join(pDir["path"],
pDir["name"])
self.__scanDir(False, dirObj, (absPath, pDir))
return dirObj
# Сканирование для получения настроек директории (первое)
else:
if templatesDir and stat.S_ISDIR(os.lstat(templatesDir)[stat.ST_MODE]):
# настройки директории
pDir = {}
# Файл информации о директории
dirInfoFile = os.path.join(templatesDir, self.templDirNameFile)
if os.path.exists(dirInfoFile) and\
stat.S_ISREG(os.lstat(dirInfoFile)[stat.ST_MODE]):
# Настройки директории и применение
pDir,applyDir = self.__isApplyHeadDir(dirObj.dHeadObj,
dirInfoFile)
if self.getError():
self.setError(_("Incorrect template: " ) +\
dirInfoFile)
return self.__clearInErrorDirObj(dirObj)
if not applyDir:
return dirObj
if not "path" in pDir:
pDir["path"] = "/"
if not "name" in pDir:
pDir["name"] = os.path.split(templatesDir)[1]
# Обработка skip
if "append" in pDir and pDir["append"] == "skip":
pDir["_real_path"] = pDir["path"]
else:
pDir["_real_path"] = os.path.join(pDir["path"],
pDir["name"])
self.__scanDir(False, dirObj, (templatesDir, pDir))
return dirObj
def scanDirs(self, templatesDirs, objVar):
"""Измененное cканирование директорий на вход список
Выход список объктов _dir
"""
dirs = []
# Объект заголовка файла
fHeadObj = fileHeader()
# Объект заголовка директории
dHeadObj = dirHeader()
# Объект переменных
for templateDir in templatesDirs:
dirP = scan._dir()
dirP.baseDir = templateDir
# Ошибка при сканировании
dirP.flagError = False
# Объект заголовка файла
dirP.fHeadObj = fHeadObj
# Объект заголовка директории
dirP.dHeadObj = dHeadObj
# Объект переменных
dirP.objVar = objVar
try:
self.__scanDir(templateDir, dirP)
except OSError, e:
print e.strerror, e.filename
self.__clearInErrorDirObj(dirP)
return [dirP]
if dirP.flagError:
return [dirP]
dirs.append(dirP)
return dirs
def applyTemplates(self):
"""Применяет шаблоны к конфигурационным файлам"""
if not self.objVar.defined("cl_template_path"):
self.setError (_("not defined Var: ") + "cl_template_path")
return False
dirsTemplates = self.objVar.Get("cl_template_path")
dirsTemplates.sort()
dirObjs = self.scanDirs(dirsTemplates,self.objVar)
#файлы к которым были применены шаблоны
filesApply = []
#созданные директории
createdDirs = []
# Получаем общее количество шаблонов (нужно для прогресбара)
allApplyFiles = self.scanTemplates(dirObjs)
if allApplyFiles == False:
return False
numberAllTemplates = len(allApplyFiles)
# Вызываем пустой метод с параметром общее количество шаблонов
self.numberAllTemplates(numberAllTemplates)
# номер обрабатываемого файла
numberProcessTemplates = 0
# имя текущей программы
_nameProgram = self.objVar.Get("cl_name").capitalize()
# версия текущей программы
_versionProgram = self.objVar.Get("cl_ver")
# имя и версия текущей программы
programVersion = "%s %s"%(_nameProgram, _versionProgram)
# Объект обработки заголовков файлов шаблонов
fileHeadObj = fileHeader()
# Проверка на ошибки
for dirObj in dirObjs:
if dirObj.flagError:
return False
for dirObj in dirObjs:
# сортируем файлы по названию
if dirObj.files:
dirObj.files.sort()
# сортируем директории по названию
if dirObj.dirs:
dirObj.dirs.sort()
blockDirs = []
propDir = {}
for dirTemplate, pDirs in dirObj.dirs:
# Получаем реальный путь директории
pathDir = pDirs["_real_path"]
# Фильтрация шаблонов по названию директории
if pathDir in self.dirsFilter:
blockDirs.append(dirTemplate)
continue
dirInfoFile = os.path.join(dirTemplate, self.templDirNameFile)
# Если есть базовая директория наложения шаблонов
# то изменяем путь к директории в системе
if self._baseDir:
pDirs["_real_path"] = os.path.join(self._baseDir,
pathDir[1:])
pathDir, propDir, crDirs = self.__getApplyHeadDir(pDirs)
if not propDir and self.getError():
self.setError(_("Error in apply template: " ) +\
dirInfoFile)
return False
if crDirs:
createdDirs += crDirs
for fileTemplate, propFile in dirObj.files:
findBlock = False
pathFile = propFile["_real_path"]
for blDir in blockDirs:
st,mid,end = pathFile.partition(blDir)
if (not st) and mid and end:
findBlock = True
break
if findBlock:
continue
numberProcessTemplates += 1
self.numberProcessTemplates(numberProcessTemplates)
# Фильтрация шаблонов по названию файла
if pathFile in self.filesFilter:
continue
titleBaseDir = os.path.split(dirObj.baseDir)[0]
titlePath = fileTemplate.partition(titleBaseDir)[2]
templTitle = '"' + titlePath[1:] + '"'
# Если есть базовая директория наложения шаблонов
# то изменяем путь к файлу в системе
# и директорию где этот файл находится
if self._baseDir:
pathFile = os.path.join(self._baseDir, pathFile[1:])
propFile["_real_path"] = pathFile
propFile["path"] = os.path.join(self._baseDir,
propFile["path"][1:])
# Записываем в переменную обрабатываемый файл
self.objVar.Set("cl_pass_file",pathFile)
filesApl = self.join(fileTemplate, propFile,
(programVersion,templTitle), fileHeadObj)
if filesApl:
filesApply += filesApl
else:
if self.getError():
#print self.getError()
return False
self.closeFiles()
return (createdDirs, filesApply)
def scanTemplates(self, dirObjs=False):
"""Сканирует директории шаблонов - выводит список файлов"""
if not self.objVar.defined("cl_template_path"):
self.setError (_("not defined Var: ") + "cl_template_path")
return False
if not dirObjs:
dirsTemplates = self.objVar.Get("cl_template_path")
dirObjs = self.scanDirs(dirsTemplates, self.objVar)
#файлы к которым были применены шаблоны
filesApply = []
# Проверка на ошибки
for dirObj in dirObjs:
if dirObj.flagError:
return False
for dirObj in dirObjs:
# сортируем файлы по названию
if dirObj.files:
dirObj.files.sort()
# сортируем директории по названию
if dirObj.dirs:
dirObj.dirs.sort()
blockDirs = []
for dirTemplate, pDirs in dirObj.dirs:
pathDir = pDirs["_real_path"]
# Фильтрация шаблонов по названию директории
if pathDir in self.dirsFilter:
blockDirs.append(dirTemplate)
continue
for fileTemplate, propFile in dirObj.files:
findBlock = False
pathFile = propFile["_real_path"]
# Фильтрация файлов по названию директории
for blDir in blockDirs:
st,mid,end = pathFile.partition(blDir)
if (not st) and mid and end:
findBlock = True
break
if findBlock:
continue
# Фильтрация шаблонов по названию файла
if pathFile in self.filesFilter:
continue
filesApply.append(pathFile)
return filesApply
def __isApplyHeadDir(self, headerObj, templateDirFile):
"""Будет ли применен шаблон корневой директории
Возвращает:
(Настройки директории, и будет ли она применена (True, False))
"""
# Настройки для директории
dPrefs = {}
def function(text):
"""Функция обработки функций в заголовке"""
return self.applyFuncTemplate(text, templateDirFile)
if not os.path.exists(templateDirFile):
return (dPrefs, True)
try:
FD = open(templateDirFile)
textTemplate = FD.read()
FD.close()
except:
self.setError(_("Error open template: " ) + templateDirFile)
return (dPrefs, False)
# Заменяем переменные на их значения
textTemplate = self.applyVarsTemplate(textTemplate, templateDirFile)
# Обработка заголовка
propDir = headerObj.getPropertyTemplate(textTemplate,self.objVar,
function, templateDirFile)
if not propDir["_apply"]:
if headerObj.getError():
self.setError(_("Incorrect template: " ) + templateDirFile)
return (dPrefs, False)
# Записываем настройки
dPrefs.update(propDir)
# Тип добавления
if dPrefs['append'] == "remove":
# Удаление конфигурационного файла
return (dPrefs, False)
# chmod - изменяем права
if "chmod" in dPrefs:
mode = self.__octToInt(dPrefs["chmod"])
if mode:
dPrefs["chmod"] = mode
else:
self.setError (_("False value 'chmod' in template: " ) +\
templateDirFile)
return (dPrefs, False)
# chown - изменяем владельца и группу
if "chown" in dPrefs:
owner = dPrefs["chown"]
uid = False
gid = False
if ":" in owner:
strUid, strGid = owner.split(":")
import pwd
try:
uid = pwd.getpwnam(strUid)[2]
except:
self.setError (_("Not user in this system: ") + strUid)
self.setError (_("False value 'chown' in template: " )+\
templateDirFile)
return (dPrefs, False)
try:
import grp
gid = grp.getgrnam(strGid)[2]
except:
self.setError (_("Not group in this system: ")+strGid)
self.setError (_("False value 'chown' in template: " )+\
templateDirFile)
return (dPrefs, False)
dPrefs["chown"] = [uid, gid]
else:
self.setError (_("False value 'chown' in template: " ) +\
templateDirFile)
return (dPrefs, False)
# Проверяем path
if "path" in dPrefs and\
(not dPrefs["path"] or not dPrefs["path"][0] == "/"):
self.setError (_("False value 'path' in template: " )+\
templateDirFile)
return (dPrefs, False)
# Проверяем name
if "name" in dPrefs and\
(dPrefs["name"] and dPrefs["name"][0] == "/"):
self.setError (_("False value 'name' in template: " )+\
templateDirFile)
return (dPrefs, False)
return (dPrefs, True)
def __getApplyHeadDir(self, dictPropDir):
"""Применяет шаблон к директории (права, владелец, и.т. д)"""
newDirMv = dictPropDir["_real_path"]
applyDir = newDirMv
# Созданные директории
createDirs = []
if "append" in dictPropDir:
if dictPropDir["append"]=="skip":
return (applyDir, dictPropDir, createDirs)
# Удаляем директорию
elif dictPropDir["append"]=="remove":
if os.path.isdir(newDirMv):
# удаляем директорию
try:
removeDir(newDirMv)
except (OSError, Exception), e:
print e
self.setError(_("Can not delete dir: " ) +\
newDirMv)
return (applyDir, False, createDirs)
# Флаг проверки существования директории
flagFoundDir = os.path.exists(newDirMv)
mode = False
uid = False
gid = False
# chmod - изменяем права
if "chmod" in dictPropDir:
mode = dictPropDir['chmod']
if flagFoundDir:
os.chmod(newDirMv, mode)
# chown - изменяем владельца и группу
if "chown" in dictPropDir:
uid, gid = dictPropDir['chown']
if flagFoundDir:
os.chown(newDirMv, uid, gid)
if not flagFoundDir:
createDirs = self.createDir(newDirMv, mode, uid, gid)
if not createDirs:
return (applyDir, False, createDirs)
return (applyDir, dictPropDir, createDirs)
def __isApplyHeadTemplate(self, headerObj, templateName):
"""Будет ли применен файл шаблона
Возвращает:
(Настройки файла, и будет ли он применен (True, False))
"""
# Настройки для файла
fPrefs = {}
def function(text):
"""Функция обработки функций в заголовке"""
return self.applyFuncTemplate(text, templateName)
if not os.path.exists(templateName):
return (fPrefs, True)
try:
FD = open(templateName)
textTemplate = FD.read()
FD.close()
except:
self.setError(_("Error open template: " ) + templateName)
return (fPrefs, False)
# Бинарный или текстовый файл шаблона
templateType = self.getFileType(templateName)
foundHeader = False
textHeader = ""
if templateType != "bin":
# Получаем заголовок файла шаблона
foundHeader, textHeader = headerObj.getHeader(textTemplate)
# Заменяем переменные на их значения
textHeader = self.applyVarsTemplate(textHeader, templateName)
propFile = headerObj.getPropertyTemplate(textHeader, foundHeader,
templateType, self.objVar,
function,templateName)
if not propFile["_apply"]:
if headerObj.getError():
self.setError(_("Incorrect template: " ) + templateName)
return (fPrefs, False)
if propFile["append"] == "remove":
# Удаление конфигурационного файла
return (propFile, False)
# Записываем настройки
fPrefs.update(propFile)
# chmod - изменяем права
if "chmod" in fPrefs:
mode = self.__octToInt(fPrefs["chmod"])
if mode:
fPrefs["chmod"] = mode
else:
self.setError (_("False value 'chmod' in template: " ) +\
templateName)
return (fPrefs, False)
# chown - изменяем владельца и группу
if "chown" in fPrefs:
owner = fPrefs["chown"]
uid = False
gid = False
if ":" in owner:
strUid, strGid = owner.split(":")
import pwd
try:
uid = pwd.getpwnam(strUid)[2]
except:
self.setError (_("Not user in this system: ") + strUid)
self.setError (_("False value 'chown' in template: " )+\
templateName)
return (fPrefs, False)
try:
import grp
gid = grp.getgrnam(strGid)[2]
except:
self.setError (_("Not group in this system: ")+strGid)
self.setError (_("False value 'chown' in template: " )+\
templateName)
return (fPrefs, False)
fPrefs["chown"] = [uid, gid]
else:
self.setError (_("False value 'chown' in template: " ) +\
templateName)
return (fPrefs, False)
# Проверяем path
if "path" in fPrefs and\
(not fPrefs["path"] or not fPrefs["path"][0] == "/"):
self.setError (_("False value 'path' in template: " )+\
templateDirFile)
return (fPrefs, False)
# Проверяем name
if "name" in fPrefs and\
(not fPrefs["name"] or fPrefs["name"][0] == "/"):
self.setError (_("False value 'name' in template: " )+\
templateDirFile)
return (fPrefs, False)
return (fPrefs, True)
def __getApplyHeadTemplate(self, newFile, dictPropFile):
"""Применяет заголовок к шаблону (права, владелец, и.т. д)"""
# Конфигурационный файл в системе
pathOldFile = dictPropFile["_real_path"]
# Директория в которой находится шаблон
newDir = dictPropFile["path"]
# Файлы в системе к которым были применены шаблоны
applyFiles = [pathOldFile]
pathProg = ""
# В случае force
if "force" in dictPropFile:
if os.path.islink(pathOldFile):
# удаляем ссылку
try:
os.unlink(pathOldFile)
except:
self.setError(_("Can not delete link: " ) +\
pathOldFile)
return (applyFiles, False)
if os.path.isfile(pathOldFile):
# удаляем файл
try:
os.remove(pathOldFile)
except:
self.setError(_("Can not delete file: " ) +\
pathOldFile)
return (applyFiles, False)
# Удаляем оригинальный файл
if dictPropFile["append"] == "remove":
if os.path.islink(pathOldFile):
# удаляем ссылку
try:
os.unlink(pathOldFile)
except:
self.setError(_("Can not delete link: " ) +\
pathOldFile)
if os.path.isfile(pathOldFile):
# удаляем файл
try:
os.remove(pathOldFile)
except:
self.setError(_("Can not delete file: " ) +\
pathOldFile)
return (applyFiles, False)
flagSymlink = False
flagForce = False
# Если есть параметр mirror
if "mirror" in dictPropFile:
if "link" in dictPropFile:
templateFile = dictPropFile['link']
if not os.path.exists(templateFile):
if os.path.exists(pathOldFile):
os.remove(pathOldFile)
return (applyFiles, False)
elif not os.path.exists(pathOldFile):
return (applyFiles, False)
# Если есть указатель на файл шаблона (link)
if "link" in dictPropFile and\
not "symbolic" in dictPropFile:
templateFile = dictPropFile['link']
foundTemplateFile = os.path.exists(templateFile)
if foundTemplateFile:
FO = self.openNewFile(templateFile)
buff = FO.read()
FO.close()
if os.path.exists(pathOldFile):
os.remove(pathOldFile)
if foundTemplateFile:
if not self.createConfFile(pathOldFile, newDir):
return (applyFiles, False)
FON = open (pathOldFile, "r+")
FON.write(buff)
FON.close()
# Если символическая ссылка
if "symbolic" in dictPropFile:
prevOldFile = pathOldFile
if "link" in dictPropFile:
pathOldFile = dictPropFile['link']
flagSymlink = True
if not "/" == pathOldFile[0]:
pathLink = os.path.split(os.path.abspath(prevOldFile))[0]
pathProg = os.getcwd()
os.chdir(pathLink)
# Флаг - создан конфигурационный файл
flagCreateFile = False
# chmod - изменяем права
if "chmod" in dictPropFile:
mode = dictPropFile['chmod']
if not os.path.exists(pathOldFile):
if not self.createConfFile(pathOldFile, newDir):
return (applyFiles, False)
flagCreateFile = True
os.chmod(pathOldFile, mode)
# chown - изменяем владельца и группу
if "chown" in dictPropFile:
uid, gid = dictPropFile['chown']
if not flagCreateFile and not os.path.exists(pathOldFile):
if not self.createConfFile(pathOldFile, newDir):
return (applyFiles, False)
os.chown(pathOldFile, uid, gid)
if flagSymlink:
if os.path.exists(prevOldFile) or os.path.islink(prevOldFile):
if os.path.islink(prevOldFile):
# если ссылка то удаляем её
os.unlink(prevOldFile)
else:
# иначе удаляем файл
os.remove(prevOldFile)
if not "/" == pathOldFile[0]:
os.symlink(pathOldFile, prevOldFile)
applyFiles = [prevOldFile,os.path.join(pathLink,pathOldFile)]
else:
os.symlink(pathOldFile, prevOldFile)
applyFiles = [prevOldFile,pathOldFile]
removeLink = not flagSymlink
# Создаем конфигурационный файл если он отсутствует
# открываем файл шаблона и конфигурационный файл
# указатель начала файла шаблона указыввает на текст после заголовка
# файла
if not self.openFiles(newFile, pathOldFile, self.createConfFile,
dictPropFile["_position"], removeLink):
return (applyFiles, False)
if pathProg:
os.chdir(pathProg)
# Если файлы заменяются не нужно их обрабатывать дальше
if "replace" in dictPropFile and\
not "symbolic" in dictPropFile and\
"link" in dictPropFile:
return (applyFiles, False)
return (applyFiles, dictPropFile)
def createNewClass(self, name, bases, attrs={}):
"""Создает объект нового класса
createNewClass(self, name, bases, attrs)
name - имя класса - str,
bases - cписок наследуемых классов - (tuple),
attrs - аттрибуты класса - {dict}
"""
class newMethod:
#Объединяем конфигурации
def join(self, newObj):
if newObj.__class__.__name__ == self.__class__.__name__:
self.docObj.joinDoc(newObj.doc)
# Пост обработка
if 'postXML' in dir(self):
self.postXML()
attrsNew = {}
attrsNew["configName"] = name
if attrs:
for key in attrs.keys():
attrsNew[key] = attrs[key]
newCl = type(name, bases + (newMethod, object), attrsNew)
return newCl
def textIsUtf8(self, text):
"""Проверяет текст на кодировку UTF-8"""
try:
text.decode("UTF-8")
except:
return False
return True
def setTemplateRules(self, templateProp, textTemplate, templFile, confFile):
"""Устанавливаем значения переменных, условий, функций для текста"""
# Вычисляем условные блоки
textTemplate = self.applyTermsTemplate(textTemplate,
templFile, confFile)
# Заменяем переменные на их значения
textTemplate = self.applyVarsTemplate(textTemplate,
templFile)
# Вычисляем функции
textTemplate = self.applyFuncTemplate(textTemplate, templFile)
return textTemplate
def join(self, newFile, propFile, ListOptTitle, fileHeadObj):
"""Объединения шаблона и конф. файла
join(newFile, oldFile, ListOptTitle)
Объединение шаблона newFile и конф. файла oldFile,
propFile - словарь свойств конфигурационного файла
ListOptTitle - список строк которые добавятся в заголовок
"""
# Применяем свойства к конфигурационному файлу
# Открываем файл шаблона и конфигурационный файл
# В случае type=print (печатаем содержимое шаблона)
flagPrintTemplate = False
filesApply, templateProp = self.__getApplyHeadTemplate(newFile,propFile)
if not templateProp:
if self.getError():
return []
return filesApply
# Путь к конфигурационному файлу
oldFile = templateProp["_real_path"]
# Формат шаблона
formatTemplate = templateProp["format"]
# Тип объединение шаблона
appendTemplate = templateProp["append"]
if formatTemplate != "bin":
# Применение условий, переменных, функций и переменных заголовка
self.newTemplate = self.setTemplateRules(templateProp,
self.newTemplate, newFile,
oldFile)
else:
# Копируем содержимое шаблона в содержимое конфигурационного файла
self.oldTemplate = self.newTemplate
# Если файл шаблона имеет поддерживаемый формат
if not formatTemplate in ["raw", "bin"]:
# Флаг- кодировка с бинарными примесями у файла шаблона включаем при
# условии текстового файла и кодировки отличной от UTF-8
flagNotUtf8New = False
# Флаг - кодировка с бинарными примесями у оригинального файла
flagNotUtf8Old = False
# проверяем кодировку шаблона
if not self.textIsUtf8(self.newTemplate):
flagNotUtf8New = True
if not ("link" in templateProp and "symbolic" in templateProp):
# проверяем кодировку оригинального файла
if not self.textIsUtf8(self.oldTemplate):
flagNotUtf8Old = True
# Титл конфигурационного файла
title = ""
if ListOptTitle and "comment" in templateProp:
title = self.getTitle(templateProp["comment"],
ListOptTitle)
title = title.encode("UTF-8")
# Удаляем заголовок Calculate из конфигурационного файла
if "comment" in templateProp:
self.oldTemplate = fileHeadObj.delHeaderConfFile(self.oldTemplate,
templateProp["comment"])
# Создаем объект в случае параметра format в заголовке
if not formatTemplate in ["raw", "bin"] and\
appendTemplate in ["replace", "before", "after"]:
# Преобразовываем бинарные файлы
if flagNotUtf8New:
objTxtCoder = utfBin()
self.newTemplate = objTxtCoder.encode(self.newTemplate)
# создаем объект формата шаблона
objTemplNew = self.getFormatObj(formatTemplate, self.newTemplate)
if not objTemplNew:
self.setError (\
_("Incorrect header parmeter format=%s in template")\
%formatTemplate + " " + newFile)
return False
if "xml_" in formatTemplate:
if objTemplNew.getError():
self.setError (_("False template: " ) + newFile)
return False
nameRootNode = oldFile.rpartition("/")[2].split(".")[0]
objTemplNew.setNameBodyNode(nameRootNode)
# Объект Документ
docObj = objTemplNew.docObj
# Удаление комментариев из документа
docObj.removeComment(docObj.getNodeBody())
# Добавление необходимых переводов строк
docObj.insertBRtoBody(docObj.getNodeBody())
# Добавление необходимых разделителей между областями
docObj.insertBeforeSepAreas(docObj.getNodeBody())
# Пост обработка
if 'postXML' in dir(objTemplNew):
objTemplNew.postXML()
# Получение текстового файла из XML документа
self.newTemplate = objTemplNew.getConfig().encode("UTF-8")
# Если не UTF-8 производим преобразование
if flagNotUtf8New:
self.newTemplate = objTxtCoder.decode(self.newTemplate)
# Титл для объединения
if ListOptTitle:
title = self.getTitle(objTemplNew._comment,
ListOptTitle)
title = title.encode("UTF-8")
# Замена
if appendTemplate == "replace":
if "xml_" in formatTemplate:
data = self.newTemplate.split("\n")
data.insert(1,title)
self.oldTemplate = "\n".join(data)
else:
self.oldTemplate = title + self.newTemplate
self.saveOldFile()
return filesApply
# Впереди
elif appendTemplate == "before":
if "xml_" in formatTemplate:
self.setError (\
_("False option append=before in template %s") %newFile)
return False
if self.newTemplate:
if self.newTemplate[-1] == "\n":
tmpTemplate = self.newTemplate + self.oldTemplate
else:
tmpTemplate = self.newTemplate + "\n" + self.oldTemplate
else:
tmpTemplate = self.oldTemplate
self.oldTemplate = title + tmpTemplate
self.saveOldFile()
return filesApply
# Cзади
elif appendTemplate == "after":
if "xml_" in formatTemplate:
self.setError (\
_("False option append=after in template %s") %newFile)
return False
if self.newTemplate:
if self.newTemplate[-1] == "\n":
tmpTemplate = self.oldTemplate + self.newTemplate
else:
tmpTemplate = self.oldTemplate + "\n" + self.newTemplate
else:
tmpTemplate = self.oldTemplate
self.oldTemplate = title + tmpTemplate
self.saveOldFile()
return filesApply
# Объединение
elif appendTemplate == "join":
if flagNotUtf8New:
objTxtCoder = utfBin()
self.newTemplate = objTxtCoder.encode(self.newTemplate)
# создаем объект формата шаблона
objTemplNew = self.getFormatObj(formatTemplate, self.newTemplate)
if not objTemplNew:
self.setError (\
_("Incorrect header parmeter format=%s in template")\
%formatTemplate + " " + newFile)
return False
if "xml_" in formatTemplate:
if objTemplNew.getError():
self.setError (_("False template: " ) + newFile)
return False
nameRootNode = oldFile.rpartition("/")[2].split(".")[0]
objTemplNew.setNameBodyNode(nameRootNode)
# Титл для объединения
if ListOptTitle:
title = self.getTitle(objTemplNew._comment,
ListOptTitle)
title = title.encode("UTF-8")
# В случае пустого конфигурационного файла
reNoClean = re.compile("[^\s]",re.M)
if not self.oldTemplate or\
not reNoClean.search(self.oldTemplate):
self.oldTemplate = ""
# Удаляем заголовок Calculate из конфигурационного файла
self.oldTemplate = fileHeadObj.delHeaderConfFile(self.oldTemplate,
objTemplNew._comment)
if flagNotUtf8Old:
objTxtCoder = utfBin()
self.oldTemplate = objTxtCoder.encode(self.oldTemplate)
# создаем объект формата шаблона для конфигурационного файла
objTemplOld = self.getFormatObj(formatTemplate, self.oldTemplate)
if not objTemplOld:
self.setError (_("Error in template %s") %oldFile)
return False
if "xml_" in formatTemplate:
if objTemplOld.getError():
self.setError (_("False template: " ) + oldFile)
return False
nameRootNode = oldFile.rpartition("/")[2].split(".")[0]
objTemplOld.setNameBodyNode(nameRootNode)
#print "#%s#" %(objTemplOld.docObj.body.toprettyxml())
#print "#%s#" %(objTemplNew.docObj.body.toprettyxml())
objTemplOld.join(objTemplNew)
if "xml_" in formatTemplate:
if objTemplOld.getError():
self.setError (_("False template: " ) + newFile)
return False
data = \
objTemplOld.getConfig().encode("UTF-8").split("\n")
data.insert(1,title)
self.oldTemplate = "\n".join(data)
else:
self.oldTemplate = title +\
objTemplOld.getConfig().encode("UTF-8")
# Декодируем если кодировка не UTF-8
if flagNotUtf8New or flagNotUtf8Old:
self.newTemplate = objTxtCoder.decode(self.newTemplate)
self.oldTemplate = objTxtCoder.decode(self.oldTemplate)
self.saveOldFile()
return filesApply
else:
self.setError (_("False (type append) template: " )+appendTemplate)
return False
class iniParser(_error):
"""Класс для работы с ini файлами
"""
def __init__(self, iniFile):
# Класс samba
self.samba = getattr(__import__("format.samba",
globals(), locals(),
["samba"]), "samba")
# название ini файла
self.iniFile = iniFile
# права создаваемого ini-файла
self.mode = 0640
# Cоответствует ли формат файла нужному
self.checkIni = None
def setMode(self, mode):
"""установка прав создаваемого ini-файла"""
self.mode = mode
def openIniFile(self):
if not os.access(self.iniFile, os.R_OK):
return ""
FD = open(self.iniFile, "r")
textIni = FD.read()
FD.close()
return textIni
def writeIniFile(self, txtConfig):
if not os.path.exists(self.iniFile):
fd = os.open(self.iniFile, os.O_CREAT)
os.close(fd)
os.chmod(self.iniFile, self.mode)
if not os.path.exists(self.iniFile):
self.setError(_("Unable to create file") + ": " + self.iniFile)
return False
FD = open(self.iniFile, "r+")
FD.truncate(0)
FD.seek(0)
FD.write(txtConfig)
FD.close()
def setVar(self, strHeader, dictVar):
"""Заменяет или добавляет область и переменные
Добавляет область в ini-файл или объединяет с существующей
strHeader - имя области
dictVar - словарь переменных
"""
textIni = self.openIniFile()
if not self.checkIniFile(textIni):
return False
# создаем объект типа samba и записываем в него содержимое ini-файла
objIni = self.samba(textIni)
# создаем текст в формате samba из строки заголовка и
# словаря переменных области
txtConfig = objIni.createTxtConfig(strHeader, dictVar)
# создаем объект типа samba и записываем в него текст
objIniAdd = self.samba(txtConfig)
# объединяем объекты для получения результирующего текста
objIni.join(objIniAdd)
# получаем текст
txtConfig = objIni.getConfig().encode("UTF-8")
# записываем его в ini файл
self.writeIniFile(txtConfig)
return True
def isEmptyFile(self, textIni):
"""Является ли файл пустым"""
if not textIni.strip():
return True
else:
return False
def checkIniFile(self, textIni):
"""Проверка на правильность формата файла"""
if self.checkIni == None:
# Ошибка
if textIni == False:
self.checkIni = False
return False
self.checkIni = True
# В файле есть данные
if not self.isEmptyFile(textIni):
objIni = self.samba(textIni)
xmlBody = objIni.docObj.getNodeBody()
if not xmlBody.firstChild:
self.checkIni = False
return self.checkIni
def delVar(self, strHeader, nameVar):
"""Удаляем переменную из ini файла"""
delNameVar = "!%s" %(nameVar)
dictVar = {delNameVar:"del"}
res = self.setVar(strHeader, dictVar)
return res
def delArea(self, strHeader):
"""Удаляем область из ini файла"""
delStrHeader = "!%s" %(strHeader)
dictVar = {"del":"del"}
res = self.setVar(delStrHeader, dictVar)
return res
def getVar(self, strHeader, nameVar):
"""Получаем значение переменной из ini-файла"""
textIni = self.openIniFile()
if not self.checkIniFile(textIni):
return False
# создаем объект типа samba и записываем в него содержимое ini-файла
objIni = self.samba(textIni)
# получаем ноду body
xmlBody = objIni.docObj.getNodeBody()
# находим в области переменную
res = objIni.docObj.getAreaFieldValues(strHeader, nameVar, xmlBody)
if res == False:
return ""
else:
return res
def getAreaVars(self,strHeader):
"""Получаем все переменнные области из ini-файла"""
textIni = self.openIniFile()
if not self.checkIniFile(textIni):
return False
# создаем объект типа samba и записываем в него содержимое ini-файла
objIni = self.samba(textIni)
# получаем ноду body
xmlBody = objIni.docObj.getNodeBody()
# если находим область то выдаем словарем все переменные иначе False
res = objIni.docObj.getAreaFields(strHeader, xmlBody)
if res == False:
return {}
else:
return res
def getAllSectionNames(self):
"""Получаем все имена секций определенных в ini файле"""
textIni = self.openIniFile()
if not self.checkIniFile(textIni):
return False
# создаем объект типа samba и записываем в него содержимое ini-файла
objIni = self.samba(textIni)
# получаем ноду body
xmlBody = objIni.docObj.getNodeBody()
xmlNodes = objIni.docObj.getFieldsArea(xmlBody)
# Имена секций ini файла
namesSection = []
for xmlNode in xmlNodes:
if xmlNode.tagName == "area":
nSect = objIni.docObj.getNameArea(xmlNode)
if nSect:
namesSection.append(nSect)
return namesSection