master 3.7.0.3
Mike Hiretsky 3 years ago
parent 7b8d2939e7
commit 4f531e31ff

@ -1,322 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2008-2016 Mir Calculate. 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 re
from generic import objShare
from calculate.lib.cl_template import blocText
from calculate.lib.cl_xml import xmlDoc
class bind(objShare):
"""Класс для обработки конфигурационного файла типа bind
"""
_comment = "//"
configName = "bind"
configVersion = "0.1"
__openArea = "{"
__closeArea = "[ \t]*\}[ \t]*;[ \t]*"
sepFields = ";"
reOpen = re.compile(__openArea)
reClose = re.compile(__closeArea)
reCloseArea = re.compile(__closeArea + "\s*\Z")
reComment = re.compile(
"[ \t]+%s|^%s|(?<=;)%s" % (_comment, _comment, _comment))
reSepFields = re.compile(sepFields)
reSeparator = re.compile("[ \t]+")
def prepare(self):
self.blocTextObj = blocText()
# Объект документ
self.docObj = self.textToXML()
# Создаем поля-массивы
self.docObj.postParserList()
# XML документ
self.doc = self.docObj.doc
# Делим область на составные части
def findOpenClose(self, text, reOpen, reClose, reComment):
"""Делит область на составные части
начальный текстовый блок,
открывающий блок,
блок-тело,
закрывающий блок
"""
first_bloc = ""
start_bloc = ""
end_bloc = ""
# если в одной строки и закрывающий и открывающий блок
text_lines = text.replace("} ", "}\n").splitlines()
find_open = False
if text_lines:
find_open = reOpen.search(text_lines[0])
open_bl = reOpen.search(text)
if find_open and reComment.split(text)[0].strip():
bloc_a = text[open_bl.end():]
first_bloc = text[:open_bl.start()]
start_bloc = text[open_bl.start():open_bl.end()]
close_bl = reClose.search(bloc_a)
end_bloc = bloc_a[close_bl.start():close_bl.end()]
body_bloc = bloc_a[:close_bl.start()]
return first_bloc, start_bloc, body_bloc, end_bloc
else:
return first_bloc, start_bloc, text, end_bloc
# Делим текст на области включая вложенные (areas массив областей)
def splitToAllArea(self, text, areas, reOpen, reClose, reCloseArea,
reComment, reSepFields):
"""Делит текст на области включая вложенные
возвращает список объектов областей (переменная areas)
"""
class area:
def __init__(self):
self.header = False
self.start = False
self.fields = []
self.end = False
blocs = self.blocTextObj.splitTxtToBloc(text, reOpen, reClose,
reComment, reSepFields)
for i in blocs:
areaA = area()
first, start, body, end = self.findOpenClose(i, reOpen, reCloseArea,
reComment)
areaA.header = first.replace(" ", "").replace("\t", "")
areaA.start = first + start
areaA.end = end
if areaA.end:
blocsA = self.blocTextObj.splitTxtToBloc(body, reOpen, reClose,
reComment, reSepFields)
if blocsA and blocsA[0] == body:
areaA.fields.append(body)
areas.append(areaA)
else:
for ar in blocsA:
self.splitToAllArea(ar, areaA.fields, reOpen,
reClose,
reCloseArea, reComment,
reSepFields)
areas.append(areaA)
else:
areaA.fields.append(body)
areas.append(areaA)
return areas
def setDataField(self, txtLines, endtxtLines):
"""Создаем список объектов с переменными"""
class fieldData:
def __init__(self):
self.name = False
self.value = False
self.comment = False
self.br = False
fields = []
field = fieldData()
z = 0
for k in txtLines:
textLine = k + endtxtLines[z]
z += 1
findComment = self.reComment.search(textLine)
if not textLine.strip():
field.br = textLine
fields.append(field)
field = fieldData()
elif findComment:
field.comment = textLine
fields.append(field)
field = fieldData()
else:
pars = textLine.strip()
nameValue = self.reSeparator.split(pars)
if len(nameValue) == 1:
field.name = ""
field.value = textLine.replace(self.sepFields, "")
field.br = textLine
fields.append(field)
field = fieldData()
if len(nameValue) > 2:
valueList = nameValue[1:]
nameValue = [nameValue[0], " ".join(valueList).replace(
self.sepFields, "")]
if len(nameValue) == 2:
name = nameValue[0]
value = nameValue[1].replace(self.sepFields, "")
field.name = name.replace(" ", "").replace("\t", "")
field.value = value
field.br = textLine
fields.append(field)
field = fieldData()
return fields
def createCaptionTerm(self, header, start, end, docObj):
"""Создание пустой области с заголовком
при создании области проверяется первый символ заголовка
и добавляется тег action
"!" - <action>drop</action>
"-" - <action>replace</action>
"""
areaAction = False
if header:
if header[0] == "!":
docObj.createCaption(header[1:], [start,
end.replace("\n", "")])
areaAction = "drop"
elif header[0] == "-":
docObj.createCaption(header[1:], [start,
end.replace("\n", "")])
areaAction = "replace"
else:
docObj.createCaption(header, [start,
end.replace("\n", "")])
else:
docObj.createCaption(header, [start,
end.replace("\n", "")])
areaXML = docObj.createArea()
if areaAction:
docObj.setActionArea(areaXML, areaAction)
return areaXML
def createXML(self, areas, rootNode, docObj):
"""Создаем из массивов областей XML"""
for i in areas:
if str(i.__class__.__name__) == "area":
if i.header and i.start:
areaXML = self.createCaptionTerm(i.header, i.start,
i.end.replace("\n", ""),
docObj)
else:
areaXML = rootNode
for f in i.fields:
if str(f.__class__.__name__) == "area":
if f.header and f.start:
areaXMLChild = self.createCaptionTerm(f.header,
f.start,
f.end.replace(
"\n", ""),
docObj)
self.createXML(f.fields, areaXMLChild, docObj)
areaXML.appendChild(areaXMLChild)
else:
self.createXML(f.fields, areaXML, docObj)
if "\n" in f.end:
fieldXMLBr = docObj.createField("br", [],
"", [],
False, False)
areaXML.appendChild(fieldXMLBr)
else:
if not f:
continue
fields = self.splitToFields(f)
for field in fields:
if field.name is not False:
fieldXML = self.createFieldTerm(field.name,
field.value,
field.br,
docObj)
areaXML.appendChild(fieldXML)
if field.br[-1] == "\n":
fieldXMLBr = docObj.createField("br", [],
"", [],
False,
False)
areaXML.appendChild(fieldXMLBr)
elif field.comment is not False:
fieldXML = docObj.createField("comment",
[field.comment],
"", [],
False, False)
areaXML.appendChild(fieldXML)
elif field.br is not False:
brText = field.br.replace("\n", "")
if brText:
fieldXML = docObj.createField('br',
[brText],
"", [],
False, False)
else:
fieldXML = docObj.createField('br',
[],
"", [],
False, False)
areaXML.appendChild(fieldXML)
if i.header and i.start:
rootNode.appendChild(areaXML)
if "\n" in i.end:
fieldXMLBr = docObj.createField("br", [], "", [],
False, False)
rootNode.appendChild(fieldXMLBr)
else:
fields = self.splitToFields(i)
for field in fields:
if field.name is not False:
fieldXML = self.createFieldTerm(field.name,
field.value,
field.br, docObj)
rootNode.appendChild(fieldXML)
if field.br[-1] == "\n":
fieldXMLBr = docObj.createField("br", [], "", [],
False, False)
rootNode.appendChild(fieldXMLBr)
elif field.comment is not False:
fieldXML = docObj.createField("comment",
[field.comment],
"", [],
False, False)
rootNode.appendChild(fieldXML)
elif field.br is not False:
brText = field.br.replace("\n", "")
if brText:
fieldXML = docObj.createField('br', [brText], "",
[],
False, False)
else:
fieldXML = docObj.createField('br', [], "", [],
False, False)
rootNode.appendChild(fieldXML)
# rootNode.appendChild(areaXML)
def textToXML(self):
"""Преобразуем текст в XML"""
areas = []
if self.text.strip():
self.splitToAllArea(self.text, areas, self.reOpen, self.reClose,
self.reCloseArea, self.reComment,
self.reSepFields)
docObj = xmlDoc()
# Создание объекта документ c пустым разделителем между полями
docObj.createDoc(self.configName, self.configVersion)
if not areas:
return docObj
self.createXML(areas, docObj.getNodeBody(), docObj)
return docObj
def join(self, bindObj):
"""Объединяем конфигурации"""
if isinstance(bindObj, bind):
self.docObj.joinDoc(bindObj.doc)

@ -1,655 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2008-2016 Mir Calculate. 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 re
import types
import copy
from calculate.lib.cl_xml import xpath, xmlDoc
from calculate.lib.cl_template import blocText
from calculate.lib.format.samba import samba
class PlasmaArea(object):
def __init__(self):
self.header = False
self.start = False
self.fields = []
self.end = ""
class plasmaBlocText(blocText):
def findBloc(self, text, captionTxtBloc, bodyTxtBloc):
""" Делит текст на блоки (с заголовками)
captionTxtBloc - регулярное выражение для заголовка блока
bodyTxtBloc - регулярное выражение для тела блока
возвращает два списка: первый - заголовки, второй - тела блоков
"""
# Заголовки блоков
headersTxt = []
# Тексты блоков
blocsTxt = []
r = captionTxtBloc.search(text)
if r:
headersTxt.append(r.group(0))
txtSpl = text.partition(r.group(0))
blocTxt = txtSpl[0]
txtWr = txtSpl[2]
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.partition(r.group(0))
blocTxt = txtSpl[0]
txtWr = txtSpl[2]
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 xmlDocPlasma(xmlDoc):
"""Класс для замены метода joinArea в xmlDoc для plasma"""
# заменяемый метод для xmlDoc
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"):
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 = []
newAreaAction = None
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)
# Заменяем QUOTE
oldAreaNode = oName.parentNode.parentNode
oldAreaQuote = xpath.Evaluate('child::caption/quote',
oldAreaNode)[0]
if oldAreaQuote and not oldAreaQuote.firstChild:
newAreaQuote = xpath.Evaluate('child::caption/quote',
xmlNewArea)[0]
oldAreaCaption = xpath.Evaluate('child::caption',
oldAreaNode)[0]
if newAreaQuote and oldAreaCaption:
oldAreaCaption.replaceChild(newAreaQuote, oldAreaQuote)
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"):
appendArea(baseNode, xmlNewArea)
else:
tmpXmlNewAreas = xpath.Evaluate('child::area', xmlNewArea)
for na in tmpXmlNewAreas:
for bn in baseNodes:
self.joinArea(bn, na)
return True
class plasma(samba):
"""Класс для обработки конфигурационного файла типа kde
"""
_comment = "#"
configName = "plasma"
configVersion = "0.1"
reHeader = re.compile("^[\t ]*\[[^\[\]]+\].*\n?", re.M)
reBody = re.compile(".+", re.M | re.S)
reComment = re.compile("^\s*%s.*" % _comment)
reSeparator = re.compile("=")
sepFields = "\n"
reSepFields = re.compile(sepFields)
def prepare(self):
self.blocTextObj = plasmaBlocText()
self._splitToFields = self.splitToFields
# Объект документ
self.docObj = self._textToXML()
# XML документ
self.doc = self.docObj.doc
# Делим текст на области включая вложенные (areas массив областей)
def splitToAllArea(self, text, areas):
"""Делит текст на области включая вложенные
возвращает список объектов областей (переменная areas)
"""
area = PlasmaArea
def findPathArea(listPath, areaF):
"""Ищет путь в области
areaF - объект area
listPath - cписок названий областей
"""
ret = False
if not listPath:
return ret
flagList = False
if type(areaF) == types.ListType:
fields = areaF
flagList = True
else:
fields = areaF.fields
if areaF.header == listPath[0]:
ret = areaF
else:
return ret
for i in fields:
if isinstance(i, PlasmaArea):
add = False
if not flagList:
add = listPath.pop(0)
if not listPath:
break
ret = False
if i.header == listPath[0]:
ret = findPathArea(listPath, i)
break
else:
if add:
listPath.insert(0, add)
if ret == areaF and len(listPath) > 1:
ret = False
return ret
blTmp = self.blocTextObj.findBloc(self.text, self.reHeader, self.reBody)
blocs = self.getFullAreas(blTmp)
if isinstance(blocs, list) and not blocs:
return []
reH = re.compile("\[([^\[\]]+)\]")
# Список имен блоков
namesBlockList = []
# Временные поля
fieldsTmp = []
# Добавляем заголовки
z = 0
for h in blocs[0]:
if not h:
if blocs[1][z] == "":
fieldsTmp.append("")
# fieldsTmp.append("\n")
else:
fieldsTmp.append(blocs[1][z])
z += 1
continue
z += 1
slpNamesBlock = reH.split(h)
# Отступ слева для заголовка
indentionLeft = slpNamesBlock[0]
namesBlock = filter(lambda x: x.strip(), slpNamesBlock)
# namesBlock = map(lambda x: self.removeSymbolTerm(x), namesBlock)
findArea = findPathArea(copy.copy(namesBlock), areas)
findAreaPrev = None
namesBlockList.append(namesBlock)
if findArea:
if len(namesBlock) > 1:
namesBlockView = map(lambda x: self.removeSymbolTerm(x),
namesBlock)
else:
namesBlockView = namesBlock
findArea.start = (indentionLeft + "[" +
"][".join(namesBlockView) + "]")
else:
i = 0
lenNamesBlock = len(namesBlock)
namesBlockTmp = []
for nameB in namesBlock:
namesBlockTmp.append(nameB)
findArea = findPathArea(copy.copy(namesBlockTmp), areas)
i += 1
if not findArea:
areaNew = area()
areaNew.header = nameB
if lenNamesBlock == i:
if len(namesBlock) > 1:
namesBlockView = map(
lambda x: self.removeSymbolTerm(x),
namesBlock)
else:
namesBlockView = namesBlock
areaNew.start = (indentionLeft + "[" +
"][".join(namesBlockView) + "]")
else:
areaNew.start = ""
areaNew.end = ""
if i == 1:
if lenNamesBlock == i:
areas += fieldsTmp
areas.append(areaNew)
findAreaPrev = areas[-1]
else:
if (lenNamesBlock == i and
isinstance(findAreaPrev, area)):
findAreaPrev.fields += fieldsTmp
findAreaPrev.fields.append(areaNew)
findAreaPrev = findAreaPrev.fields[-1]
else:
findAreaPrev = findArea
fieldsTmp = []
i = 0
delt = 0
# Добавляем тела
for body in blocs[1]:
if not blocs[0][i]:
i += 1
delt += 1
continue
## В случае последнего комментария не добавляем перевод строки
# if self.reComment.search(body.splitlines()[-1]):
body = "\n" + body
namesBlock = namesBlockList[i - delt]
findArea = findPathArea(copy.copy(namesBlock), areas)
if isinstance(findArea, area):
# if findArea.fields:
# if type(findArea.fields[0]) == types.StringType:
# findArea.fields.pop(0)
findArea.fields.insert(0, body)
i += 1
return areas
def createCaptionTerm(self, header, start, end, docObj):
"""Создание пустой области с заголовком
при создании области проверяется первый символ заголовка
и добавляется тег action
"!" - <action>drop</action>
"-" - <action>replace</action>
"""
areaAction = False
if header:
if header[0] == "!":
docObj.createCaption(header[1:], [start,
end.replace("\n", "")])
areaAction = "drop"
elif header[0] == "-":
docObj.createCaption(header[1:], [start,
end.replace("\n", "")])
areaAction = "replace"
else:
docObj.createCaption(header, [start,
end.replace("\n", "")])
else:
docObj.createCaption(header, [start,
end.replace("\n", "")])
areaXML = docObj.createArea()
if areaAction:
docObj.setActionArea(areaXML, areaAction)
return areaXML
def createXML(self, areas, rootNode, docObj):
"""Создаем из массивов областей XML"""
for i in areas:
if isinstance(i, PlasmaArea):
if i.header:
areaXML = self.createCaptionTerm(i.header, i.start,
i.end.replace("\n", ""),
docObj)
for f in i.fields:
if isinstance(f, PlasmaArea):
if f.header:
areaXMLChild = self.createCaptionTerm(f.header,
f.start,
f.end.replace(
"\n", ""),
docObj)
self.createXML(f.fields, areaXMLChild, docObj)
areaXML.appendChild(areaXMLChild)
else:
self.createXML(f.fields, areaXML, docObj)
if "\n" in f.end:
fieldXMLBr = docObj.createField("br", [],
"", [],
False, False)
areaXML.appendChild(fieldXMLBr)
else:
if not f:
continue
fields = self.splitToFields(f)
for field in fields:
if field.name is not False:
fieldXML = self.createFieldTerm(
field.name, field.value, field.br, docObj)
areaXML.appendChild(fieldXML)
if field.br[-1] == "\n":
fieldXMLBr = docObj.createField(
"br", [], "", [], False, False)
areaXML.appendChild(fieldXMLBr)
elif field.comment is not False:
fieldXML = docObj.createField(
"comment", [field.comment], "", [],
False, False)
areaXML.appendChild(fieldXML)
elif field.br is not False:
brText = field.br.replace("\n", "")
if brText:
fieldXML = docObj.createField(
'br', [brText], "", [], False, False)
else:
fieldXML = docObj.createField(
'br', [], "", [], False, False)
if areaXML and hasattr(areaXML, "appendChild"):
areaXML.appendChild(fieldXML)
if i.header:
rootNode.appendChild(areaXML)
if "\n" in i.end:
fieldXMLBr = docObj.createField("br", [],
"", [],
False, False)
rootNode.appendChild(fieldXMLBr)
else:
if not i:
continue
fields = self.splitToFields(i)
for field in fields:
if field.name is not False:
fieldXML = self.createFieldTerm(field.name,
field.value,
field.br, docObj)
rootNode.appendChild(fieldXML)
if field.br[-1] == "\n":
fieldXMLBr = docObj.createField("br", [], "", [],
False, False)
rootNode.appendChild(fieldXMLBr)
elif field.comment is not False:
fieldXML = docObj.createField(
"comment", [field.comment], "", [], False, False)
rootNode.appendChild(fieldXML)
elif field.br is not False:
brText = field.br.replace("\n", "")
if brText:
fieldXML = docObj.createField(
'br', [brText], "", [], False, False)
else:
fieldXML = docObj.createField(
'br', [], "", [], False, False)
rootNode.appendChild(fieldXML)
# rootNode.appendChild(areaXML)
def createTxtConfig(self, strHeader, dictVar):
"""Cоздает область с заголовком
создает текст конфигурационного файла в формате samba из
заголовка (строка) и словаря переменных
"""
if not strHeader:
return ""
if type(strHeader) in (tuple, list):
outTxt = "".join(map(lambda x: "[" + x + "]", strHeader))
if not outTxt:
return ""
outTxt += "\n"
else:
outTxt = "[" + strHeader + "]\n"
for key in dictVar.keys():
outTxt += "%s=%s\n" % (key, dictVar[key])
return outTxt
def _textToXML(self):
"""Преобразуем текст в XML"""
areas = []
if self.text.strip():
self.splitToAllArea(self.text, areas)
# docObj = xmlDoc()
# Создаем новый класс xmlDoc с измененным методом joinArea
# Создаем экземпляр нового класса
docObj = xmlDocPlasma()
# Создание объекта документ c пустым разделителем между полями
docObj.createDoc(self.configName, self.configVersion)
if not areas:
return docObj
self.createXML(areas, docObj.getNodeBody(), docObj)
return docObj
def postXML(self):
"""Последующая постобработка 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
xmlAreas = xpath.Evaluate("descendant::area", self.docObj.body)
for xmlArea in xmlAreas:
# Перед пустой областью и после нее удаляем переводы строк
if getQuotesArea(xmlArea) == ["", ""]:
if (xmlArea.previousSibling and
self.docObj.getTypeField(
xmlArea.previousSibling) == "br"):
parentNode = xmlArea.previousSibling.parentNode
prev_prev_sbl = xmlArea.previousSibling.previousSibling
if (prev_prev_sbl and
self.docObj.getTypeField(
prev_prev_sbl) == "br"):
parentNode.removeChild(
xmlArea.previousSibling.previousSibling)
parentNode.removeChild(xmlArea.previousSibling)
if (xmlArea.nextSibling and
self.docObj.getTypeField(
xmlArea.nextSibling) == "br"):
parentNode = xmlArea.nextSibling.parentNode
next_next_sbl = xmlArea.nextSibling.nextSibling
if (next_next_sbl and
self.docObj.getTypeField(
next_next_sbl) == "br"):
parentNode.removeChild(xmlArea.nextSibling.nextSibling)
parentNode.removeChild(xmlArea.nextSibling)
continue
# Собираем поля в кучку
xmlChildAreas = xpath.Evaluate("child::area", xmlArea)
if xmlChildAreas:
childNodes = self.docObj.getFieldsArea(xmlArea)
firstChildArea = xmlChildAreas[0]
if (firstChildArea.previousSibling and
self.docObj.getTypeField(
firstChildArea.previousSibling) == "br"):
prev_prev_sbl = (
firstChildArea.previousSibling.previousSibling)
if prev_prev_sbl:
if self.docObj.getTypeField(prev_prev_sbl) == "br":
firstChildArea = firstChildArea.previousSibling
flagFoundArea = False
it = 0
lenChild = len(childNodes)
for node in childNodes:
it += 1
if node.tagName == "area":
flagFoundArea = True
continue
if flagFoundArea and node.tagName == "field":
if self.docObj.getTypeField(node) == "var":
xmlArea.insertBefore(node, firstChildArea)
if it < lenChild:
node_type = self.docObj.getTypeField(
childNodes[it])
if node_type == "br":
xmlArea.insertBefore(childNodes[it],
firstChildArea)
# Добавление перевода строк в если его нет между полями
if (self.docObj.getTypeField(node) == "var" and
node.previousSibling and
not (self.docObj.getTypeField(
node.previousSibling) in ("br", "comment"))):
xmlArea.insertBefore(self.docObj.createField(
"br", [], "", [], False, False), node)
# Добавляем BR если его нет первым полем
xmlFields = xpath.Evaluate("child::field", xmlArea)
if not (xmlFields and
(self.docObj.getTypeField(xmlFields[0]) == "br" or
self.docObj.getTypeField(
xmlFields[0]) == "comment")):
if xmlFields:
xmlArea.insertBefore(self.docObj.createField("br",
[], "", [],
False, False),
xmlFields[0])
# Добавление переводов строк между полями
if xmlFields:
for node in xmlFields:
# Добавление перевода строк в если его нет между полями
if (self.docObj.getTypeField(node) == "var" and
node.previousSibling and
not (self.docObj.getTypeField(
node.previousSibling) in ("br", "comment"))):
xmlArea.insertBefore(self.docObj.createField("br",
[], "", [],
False,
False),
node)
# Если последним полем BR, удаляем его
if xmlFields and self.docObj.getTypeField(xmlFields[-1]) == "br":
if not xmlFields[-1].nextSibling:
xmlArea.removeChild(xmlFields[-1])
# Если предыдущим полем не (BR или комментарий) - добавляем BR
if (xmlArea.previousSibling and
not (self.docObj.getTypeField(
xmlArea.previousSibling) == "br" or
self.docObj.getTypeField(
xmlArea.previousSibling) == "comment")):
parentNode = xmlArea.parentNode
parentNode.insertBefore(self.docObj.createField(
"br", [], "", [], False, False), xmlArea)
# Если есть предыдущее поле, и поле предыдущеее предыдущему
# не равно BR или комментарий то добавляем BR
if xmlArea.previousSibling:
prPrSibling = xmlArea.previousSibling.previousSibling
if (prPrSibling and
not (self.docObj.getTypeField(
prPrSibling) == "br" or
self.docObj.getTypeField(
prPrSibling) == "comment")):
parentNode = xmlArea.parentNode
parentNode.insertBefore(self.docObj.createField(
"br", [], "", [], False, False), xmlArea)
# Если после есть BR а за ним ничего нет, удаляем BR
if (xmlArea.nextSibling and
self.docObj.getTypeField(xmlArea.nextSibling) == "br"):
if not xmlArea.nextSibling.nextSibling:
parentNode = xmlArea.nextSibling.parentNode
parentNode.removeChild(xmlArea.nextSibling)
def join(self, kdeObj):
"""Объединяем конфигурации"""
if isinstance(kdeObj, plasma):
self.docObj.joinDoc(kdeObj.doc)
self.postXML()

@ -1,126 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2008-2016 Mir Calculate. 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 re
from calculate.lib.cl_xml import xpath
from generic import objShare
from calculate.lib.cl_xml import xmlDoc
class procmail(objShare):
"""Класс для обработки конфигурационного файла типа procmail
"""
_comment = "#"
configName = "procmail"
configVersion = "0.1"
sepFields = "\n"
reComment = re.compile("[ \t]*%s" % _comment)
reSepFields = re.compile(sepFields)
# разделитель названия и значения переменной
reSeparator = re.compile("=")
def prepare(self):
self.docObj = self.textToXML()
self.doc = self.docObj.doc
def postXML(self):
"""Последующая постобработка XML"""
xmlFields = xpath.Evaluate("child::field", self.docObj.body)
# Добавление переводов строк между полями
for node in xmlFields:
# Добавление перевода строк в если его нет между полями
if (self.docObj.getTypeField(node) == "var" and
node.previousSibling and
(self.docObj.getTypeField(node.previousSibling) not in
("br", "comment"))):
self.docObj.body.insertBefore(self.docObj.createField(
"br", [], "", [], False, False), node)
def setDataField(self, txtLines, endtxtLines):
"""Создаем список объектов с переменными"""
class fieldData:
def __init__(self):
self.name = False
self.value = False
self.comment = False
self.br = False
fields = []
field = fieldData()
z = 0
for k in txtLines:
textLine = k + endtxtLines[z]
z += 1
findComment = self.reComment.search(textLine)
if not textLine.strip():
field.br = textLine
fields.append(field)
field = fieldData()
elif findComment:
field.comment = textLine
fields.append(field)
field = fieldData()
else:
pars = textLine.strip()
nameValue = self.reSeparator.split(pars)
if len(nameValue) == 2:
name = nameValue[0]
value = nameValue[1].replace(self.sepFields, "")
field.name = name.replace(" ", "").replace("\t", "")
field.value = value
field.br = textLine
fields.append(field)
field = fieldData()
return fields
def textToXML(self):
docObj = xmlDoc()
docObj.createDoc(self.configName, self.configVersion)
if self.text:
nodeBody = docObj.getNodeBody()
fields = self.splitToFields(self.text)
for field in fields:
if field.name is not False:
fieldXML = self.createFieldTerm(field.name,
field.value,
field.br, docObj)
nodeBody.appendChild(fieldXML)
if field.br[-1] == "\n":
fieldXMLBr = docObj.createField(
"br", [], "", [], False, False)
nodeBody.appendChild(fieldXMLBr)
elif field.comment is not False:
fieldXML = docObj.createField(
"comment", [field.comment], "", [], False, False)
nodeBody.appendChild(fieldXML)
elif field.br is not False:
brText = field.br.replace("\n", "")
if brText:
fieldXML = docObj.createField(
'br', [brText], "", [], False, False)
else:
fieldXML = docObj.createField(
'br', [], "", [], False, False)
nodeBody.appendChild(fieldXML)
return docObj
def join(self, procmailObj):
"""Объединяем конфигурации"""
if isinstance(procmailObj, procmail):
self.docObj.joinDoc(procmailObj.doc)
self.postXML()

@ -1,299 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2008-2016 Mir Calculate. 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 re
from calculate.lib.cl_xml import xpath
from generic import objShare
from calculate.lib.cl_template import blocText
from calculate.lib.cl_xml import xmlDoc
class samba(objShare):
"""Класс для обработки конфигурационного файла типа samba
"""
_comment = "#"
configName = "samba"
configVersion = "0.1"
reHeader = re.compile("^[\t ]*\[[^\[\]]+\].*\n", re.M)
reBody = re.compile(".+", re.M | re.S)
reComment = re.compile("\s*%s.*|\s*;.*" % _comment)
reSeparator = re.compile("\s*=\s*")
sepFields = "\n"
reSepFields = re.compile(sepFields)
def prepare(self):
self.blocTextObj = blocText()
self._splitToFields = self.splitToFields
# Объект документ
self.docObj = self._textToXML()
# XML документ
self.doc = self.docObj.doc
def postXML(self):
"""Последующая постобработка XML"""
# удаляем пустые области
xmlAreas = xpath.Evaluate("child::area", self.docObj.body)
removeList = []
for xmlArea in xmlAreas:
xmlFields = xpath.Evaluate("child::field/name", xmlArea)
if not xmlFields:
removeList.append(xmlArea)
for xmlArea in removeList:
parentNode = xmlArea.parentNode
parentNode.removeChild(xmlArea)
# Для добавления перевода строки между областями если его нет
xmlAreas = xpath.Evaluate("child::area", self.docObj.body)
xmlArea = None
for xmlArea in xmlAreas:
xmlFields = xpath.Evaluate("child::field", xmlArea)
if not (xmlFields and (
self.docObj.getTypeField(xmlFields[-1]) == "br" or
self.docObj.getTypeField(xmlFields[-1]) == "comment")):
if xmlArea.nextSibling:
parentNode = xmlArea.parentNode
nextNode = xmlArea.nextSibling
parentNode.insertBefore(self.docObj.createField(
"br", [], "", [], False, False), nextNode)
# Добавление переводов строк между полями
if xmlFields:
for node in xmlFields:
# Добавление перевода строк в если его нет между полями
if (self.docObj.getTypeField(node) == "var" and
node.previousSibling and
not (self.docObj.getTypeField(
node.previousSibling) in ("br", "comment"))):
xmlArea.insertBefore(self.docObj.createField(
"br", [], "", [], False, False), node)
# Удаление лишних переводов строк
childNodes = self.docObj.getFieldsArea(self.docObj.body)
lenBr = 0
removeBrNodes = []
for node in childNodes:
if (node.tagName == "field" and
self.docObj.getTypeField(node) == "br"):
lenBr += 1
if lenBr > 2:
removeBrNodes.append(node)
else:
lenBr = 0
# Удаление
for rmNode in removeBrNodes:
self.docObj.body.removeChild(rmNode)
# Если после есть BR а за ним ничего нет, удаляем BR
if xmlArea:
if (xmlArea.nextSibling and
self.docObj.getTypeField(xmlArea.nextSibling) == "br"):
if not xmlArea.nextSibling.nextSibling:
parentNode = xmlArea.nextSibling.parentNode
parentNode.removeChild(xmlArea.nextSibling)
def join(self, sambaObj):
"""Объединяем конфигурации"""
if isinstance(sambaObj, samba):
self.docObj.joinDoc(sambaObj.doc)
self.postXML()
def setDataField(self, txtLines, endtxtLines):
"""Создаем список объектов с переменными"""
class fieldData:
def __init__(self):
self.name = False
self.value = False
self.comment = False
self.br = False
fields = []
field = fieldData()
z = 0
for k in txtLines:
textLine = k + endtxtLines[z]
z += 1
findComment = self.reComment.search(textLine)
if not textLine.strip():
field.br = textLine
fields.append(field)
field = fieldData()
elif findComment:
field.comment = textLine
fields.append(field)
field = fieldData()
else:
pars = textLine.strip()
nameValue = self.reSeparator.split(pars)
if len(nameValue) > 2:
valueList = nameValue[1:]
nameValue = [nameValue[0], "=".join(valueList)]
if len(nameValue) == 2:
name = nameValue[0]
value = nameValue[1].replace(self.sepFields, "")
field.name = name.replace(" ", "").replace("\t", "")
field.value = value
field.br = textLine
fields.append(field)
field = fieldData()
return fields
def splitCleanBloc(self, txtBloc):
"""Делим блок на две части (переменные, пустые строки в конце)"""
txtLines = txtBloc.split("\n")
nextBloc = []
txtLines.reverse()
z = 0
for txtLine in txtLines:
if not txtLine.strip():
nextBloc.append(txtLine)
else:
break
z += 1
txtLines.reverse()
firstBloc = txtLines[:-z]
nextBloc.reverse()
if nextBloc:
firstBloc.append("")
if nextBloc and "\n".join(nextBloc):
return "\n".join(firstBloc), "\n".join(nextBloc)
else:
return False
def getFullAreas(self, blocs):
"""Делит текст на области, (Заголовок, тело)
Возвращает два списка: заголовки, тела
"""
headsAreas = []
bodyAreas = []
if not blocs:
return []
lenBlocs = len(blocs[0])
for i in range(lenBlocs):
txtBloc = blocs[1][i]
clean = self.splitCleanBloc(txtBloc)
if clean:
headsAreas.append(blocs[0][i])
bodyAreas.append(clean[0])
headsAreas.append("")
bodyAreas.append(clean[1])
else:
headsAreas.append(blocs[0][i])
bodyAreas.append(blocs[1][i])
return headsAreas, bodyAreas
def createTxtConfig(self, strHeader, dictVar):
"""Cоздает область с заголовком
создает текст конфигурационного файла в формате samba из
заголовка (строка) и словаря переменных
"""
if not strHeader:
return ""
outTxt = "[" + strHeader + "]\n"
for key in dictVar.keys():
outTxt += "%s = %s\n" % (key, dictVar[key])
return outTxt
def _textToXML(self):
"""Преобразует текст в XML"""
blTmp = self.blocTextObj.findBloc(self.text, self.reHeader, self.reBody)
blocs = self.getFullAreas(blTmp)
headers = []
startHeaders = []
finHeaders = []
docObj = xmlDoc()
docObj.createDoc(self.configName, self.configVersion)
rootNode = docObj.getNodeBody()
# Если пустой текст то создаем пустой документ
if not blocs:
return docObj
for h in blocs[0]:
listfinH = h.split("]")
finH = listfinH[0]
if "[" in finH:
startHeaders.append(finH + "]")
else:
startHeaders.append(finH)
if len(listfinH) == 2:
finHeaders.append(listfinH[1])
else:
finHeaders.append("")
headers.append(finH.replace("[", "").replace("]", "").strip())
bodys = blocs[1]
z = 0
for h in headers:
if not bodys[z]:
z += 1
continue
areaAction = False
if h:
if h[0] == "!":
docObj.createCaption(h[1:], [startHeaders[z], ""])
areaAction = "drop"
elif h[0] == "-":
docObj.createCaption(h[1:], [startHeaders[z], ""])
areaAction = "replace"
else:
docObj.createCaption(h, [startHeaders[z], ""])
else:
docObj.createCaption(h, [startHeaders[z], ""])
if "\n" in blocs[0][z]:
if self.reComment.search(finHeaders[z]):
docObj.createField('comment', [finHeaders[z]])
elif not finHeaders[z].strip() and \
finHeaders[z].replace("\n", ""):
docObj.createField('br',
[finHeaders[z].replace("\n", "")])
else:
docObj.createField('br')
fields = self._splitToFields(bodys[z])
for f in fields:
if (f.name is not False and
f.value is not False and f.br is not False):
# Обработка условий для samba
if (f.name[0] == "!" or
f.name[0] == "-" or f.name[0] == "+"):
qns = self.removeSymbolTerm(f.br)
xmlField = docObj.createField(
"var", [qns], f.name[1:], [f.value])
if f.name[0] == "!":
# Удаляемое в дальнейшем поле
docObj.setActionField(xmlField, "drop")
else:
docObj.createField("var", [f.br.replace("\n", "")],
f.name, [f.value])
docObj.createField('br')
elif f.comment is not False:
docObj.createField('comment', [f.comment])
elif f.br is not False:
docObj.createField('br', [f.br.replace("\n", "")])
if h.strip():
area = docObj.createArea()
if areaAction:
docObj.setActionArea(area, areaAction)
rootNode.appendChild(area)
else:
fieldsNodes = docObj.tmpFields.getFields()
for fieldNode in fieldsNodes:
rootNode.appendChild(fieldNode)
docObj.clearTmpFields()
z += 1
return docObj

@ -1,56 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2014-2016 Mir Calculate. 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.
from printing import Print
from palette import TextState
from info import Terminal
Colors = TextState.Colors
from converter import ConsoleCodes256Converter, XmlConverter
from output import XmlOutput, ColorTerminal16Output, TerminalPositionOutput, \
ColorTerminal256Output
def convert_console_to_xml(s):
"""Преобразовать вывод консоли в xml для внутреннего использования"""
return ConsoleCodes256Converter(output=XmlOutput()).transform(s)
def get_color_print():
"""
Получить объект для вывода текста в цвете
"""
return Print(output=XmlOutput())
def get_terminal_output():
return (ColorTerminal256Output()
if Terminal().colors > 16
else ColorTerminal16Output())
def get_terminal_print(printfunc=lambda x: x):
"""
Получить объект для вывода в терминал
"""
# TODO: возвращать объект 256 или 16 терминал в зависимости от параметров
return Print(output=get_terminal_output(),
position_controller=TerminalPositionOutput(),
printfunc=printfunc)
def convert_xml_to_terminal(s):
return XmlConverter(output=get_terminal_output()).transform(s)

@ -1,366 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2014-2016 Mir Calculate. 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.
from output import BaseOutput
from palette import (TextState, BaseColorMapping, ConsoleCodesInfo,
LightColorMapping, ConsoleColor256, XmlFormat)
from calculate.lib.utils.tools import SavableIterator, ignore
from itertools import ifilter
from HTMLParser import HTMLParser
import re
class BaseConverter(object):
"""
Базовый класс обработки (ничего не конвертирует - возвращает как есть)
"""
def __init__(self, output=BaseOutput()):
self.output = output
def transform(self, s):
return self.output.outputText(s)
def detect(self, s):
return True
class ConsoleCodesConverter(BaseConverter):
"""Преобразователь текста из цветного консольного вывода через объект
форматирования.
Объект форматирования должен реализовывать BaseOutput
>>> cct = ConsoleCodesConverter(BaseOutput())
>>> outtext = "\033[32;1mHello\033[0;39m"
>>> cct.transform(outtext)
'Hello'
>>> from output import SpanCssOutput
>>> cct = ConsoleCodesConverter(SpanCssOutput())
>>> outtext = "\033[32;1mHello\033[0;39m"
>>> cct.transform(outtext)
'<span style="color:Green;font-weight:bold;">Hello</span>'
"""
class CodeElement(object):
"""Элемент кода в ESC последовательности"""
def __init__(self, condition=lambda code: False, action=None):
self.action = action
self.condition = condition
def tryParse(self, code):
"""Обрабатывает ли экземпляр код"""
return self.condition(code)
def parse(self, code, codes):
"""Обработать код, вызвать действие"""
if callable(self.action):
return self.action()
return None
def _next_code(self, other):
"""
Получить следующий код
"""
try:
return int(other.next())
except StopIteration:
return None
class ColorElement:
"""Элемент кода для указания стандартного цвета
Проверка кода в интервале, запуск действия с передачей цвета
"""
# соответствие консольных цветов внутренним цветам
mapColors = BaseColorMapping.mapConsole_TS
def __init__(self, action=None, begin=None, end=None):
self.action = action
self.begin = begin
self.end = end
def tryParse(self, code):
return self.begin <= code <= self.end
def parse(self, code, codes):
if callable(self.action):
return self.action(self.mapColors.get(code - self.begin,
TextState.Colors.DEFAULT))
else:
return None
def __init__(self, output=BaseOutput(), escSymb="\033"):
super(ConsoleCodesConverter, self).__init__(output)
self.escSymb = escSymb
self.escBlock = (r"{esc}(?:\[(\d*(?:;\d+)*)(m)|"
"\]\d+;.*?\x07|"
"\([B0UK]|"
"\[\d*[A-D])".format(esc=escSymb))
self.otherSymb = "(?:\r*\n|\t)"
self.reEscBlock = re.compile(self.escBlock)
self.reParse = re.compile(
"(?:{0}|({1}))?(.*?)(?=$|{0}|{1})".format(self.escBlock,
self.otherSymb),
re.DOTALL)
resetBoldHalfbright = lambda: (
(self.output.resetBold() or "") +
(self.output.resetHalfbright() or ""))
cci = ConsoleCodesInfo
element = self.CodeElement
# набор правил обработки кодов
reset = element(lambda code: code == cci.RESET, self.output.reset)
bold = element(lambda code: code == cci.BOLD, self.output.setBold)
halfbright = element(lambda code: code == cci.HALFBRIGHT,
self.output.setHalfbright)
underline = element(lambda code: code == cci.UNDERLINE,
self.output.setUnderline)
nounderline = element(lambda code: code == cci.NOUNDERLINE,
self.output.resetUnderline)
invert = element(lambda code: code == cci.INVERT,
self.output.setInvert)
noinvert = element(lambda code: code == cci.NOINVERT,
self.output.resetInvert)
normal = element(lambda code: code == cci.NORMAL,
resetBoldHalfbright)
reset_foreground = element(lambda code: code == cci.FOREGROUND_DEFAULT,
self.output.resetForeground)
reset_background = element(lambda code: code == cci.BACKGROUND_DEFAULT,
self.output.resetBackground)
foreground = self.ColorElement(begin=cci.FOREGROUND,
end=cci.FOREGROUND_END,
action=self.output.setForeground)
background = self.ColorElement(begin=cci.BACKGROUND,
end=cci.BACKGROUND_END,
action=self.output.setBackground)
newline = element(
lambda code: type(code) != int and ("\r" in code or "\n" in code),
self.output.newLine)
tab = element(lambda code: type(code) != int and "\t" in code,
self.output.tab)
self.grams = [reset, bold, halfbright, underline, nounderline, normal,
invert, noinvert, reset_foreground, reset_background,
foreground, background, tab, newline]
def evaluteGram(self, code, codes=None):
"""Выполнить грамматику"""
if codes is None:
codes = SavableIterator([])
for gram in ifilter(lambda x: x.tryParse(code),
self.grams):
return gram.parse(code, codes)
def transform(self, s):
"""
Запустить преобразование текста
"""
def generator():
for ctrl, m, other, txt, _s, _m in self.reParse.findall(s):
if m:
codes = SavableIterator(ctrl.split(';'))
for code in codes:
code = int(code or '0')
res = self.evaluteGram(code, codes)
if res:
yield res
elif other:
res = self.evaluteGram(other)
if res:
yield res
if txt:
yield self.output.outputText(txt)
yield self.output.endText()
return "".join(list(filter(None, generator())))
def detect(self, s):
"""
Определить есть ли в тексте управляющие последовательности
"""
return bool(self.reEscBlock.search(s))
class ConsoleCodes256Converter(ConsoleCodesConverter):
"""Расширяет возможность обработки 256 цветного терминала"""
class Color256Element(ConsoleCodesConverter.CodeElement):
def __init__(self, action=None, begin=None):
super(ConsoleCodes256Converter.Color256Element,
self).__init__(condition=lambda code: code == begin,
action=action)
def parse(self, code, codes):
"""
Тон: 38;5;0-255
Фон: 48;5;0-255
"""
colorMap = LightColorMapping(BaseColorMapping).mapConsole_TS
codes.save()
if self._next_code(codes) == ConsoleCodesInfo.COLOR256:
code = self._next_code(codes)
if code is not None:
if code in colorMap:
var = colorMap[code]
else:
var = ConsoleColor256.consoleToRgb(code)
if callable(self.action):
self.action(var)
else:
# если после 38 не 5 - не обрабатываем этот код
codes.restore()
def __init__(self, *args, **kwargs):
ConsoleCodesConverter.__init__(self, *args, **kwargs)
cci = ConsoleCodesInfo
# обработчики кодов для вывода в 256
foreground256 = self.Color256Element(begin=cci.FOREGROUND256,
action=self.output.setForeground)
background256 = self.Color256Element(begin=cci.BACKGROUND256,
action=self.output.setBackground)
self.grams.insert(0, foreground256)
self.grams.insert(0, background256)
class XmlConverter(BaseConverter):
"""
Преобразователь текста из внутреннего xml формата
"""
unescaper = XmlFormat.unescaper
def __init__(self, output=BaseOutput()):
super(XmlConverter, self).__init__(output)
Tags = XmlFormat.Tags
FontAttr = XmlFormat.FontAttributes
self.tagMap = {
Tags.BOLD: self.output.setBold,
Tags.HALFBRIGHT: self.output.setHalfbright,
Tags.INVERT: self.output.setInvert,
Tags.UNDERLINE: self.output.setUnderline,
Tags.FONT: self.parseFont
}
self.singletagMap = {
Tags.NEWLINE: self.output.newLine,
Tags.TAB: self.output.tab
}
self.colorMap = {FontAttr.FOREGROUND.lower(): self.output.setForeground,
FontAttr.BACKGROUND.lower(): self.output.setBackground}
self.reMatch = re.compile("<(?:%s)" % "|".join(self.tagMap.keys()),
re.I)
self.parser = self.createParser()
def createParser(self):
"""
Создать парсер HTML кода
"""
parser = HTMLParser()
parser.handle_starttag = self.startElementHandler
parser.handle_endtag = self.endElementHandler
parser.handle_data = self.characterDataHandler
parser.handle_startendtag = self.startendElementHandler
parser.handle_entityref = self.entityrefElementHandler
return parser
def parseFont(self, *attrs):
for k, v in attrs:
k = str(k).lower()
if k in self.colorMap:
self.colorMap[k](str(v))
def transform(self, s):
self.__outdata = []
self.__tagStack = []
self.parser.feed(s)
self.__outdata.append(self.output.endText())
return "".join(list(filter(None, self.__outdata)))
def addResultToOutdata(f):
"""Добавить возвращаемый результат в список self.__outdata"""
def wrapper(self, *args):
res = f(self, *args)
if res:
self.__outdata.append(res)
return res
return wrapper
def _buildTaq(self, name, attrs=(), startendTag=False, endTag=False):
"""
Создать тэг по параметрам
"""
lslash, rslash = '', ''
if startendTag:
rslash = '/'
elif endTag:
lslash = '/'
if attrs:
return "<{name} {attrs}{rslash}>".format(
name=name, attrs=" ".join(['%s="%s"' % (k, v)
for k, v in attrs]),
rslash=rslash)
else:
return "<{lslash}{name}{rslash}>".format(lslash=lslash, name=name,
rslash=rslash)
@addResultToOutdata
def startElementHandler(self, name, attrs):
"""Обработчик начального тега"""
if name in self.tagMap:
self.output.pushState()
self.__tagStack.append(name)
with ignore(TypeError):
return self.tagMap[name](*attrs)
else:
return self.output.outputText(self._buildTaq(name, attrs))
@addResultToOutdata
def startendElementHandler(self, name, attrs):
"""Обработчик одиночного тега"""
if name in self.singletagMap:
with ignore(TypeError):
return self.singletagMap[name](*attrs)
else:
return self.output.outputText(
self._buildTaq(name, attrs, startendTag=True))
@addResultToOutdata
def endElementHandler(self, name):
"""Обработчик завершающего тега"""
if name in self.tagMap:
if name in self.__tagStack:
while self.__tagStack and self.__tagStack.pop() != name:
self.output.popState()
self.output.popState()
else:
return self.output.outputText(self._buildTaq(name, endTag=True))
@addResultToOutdata
def characterDataHandler(self, data):
"""Обработчик текста в тэгах"""
return self.output.outputText(self.unescaper(data))
@addResultToOutdata
def entityrefElementHandler(self, data):
return self.output.outputText(self.unescaper("&%s;" % data))
def detect(self, s):
return bool(self.reMatch.search(s))
addResultToOutdata = staticmethod(addResultToOutdata)

@ -1,705 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2014-2016 Mir Calculate. 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.
from xml.etree import ElementTree as ET
from palette import (TextState, BaseColorMapping,
ConsoleCodeMapping, LightColorMapping, ConsoleColor256,
ConsoleCodesInfo, SpanPalette, XmlFormat)
class BaseOutput(object):
"""
Базовый вывод текста.
Вывод просто текста без изменения шрифта
"""
def __init__(self, state=None):
pass
def setBold(self):
"""
Выводимый текст будет жирным
"""
pass
def resetBold(self):
"""
Выводимый текст будет нежирным
"""
pass
def setUnderline(self):
"""
Выводимый текст будет подчеркнутым
"""
pass
def resetUnderline(self):
"""
Выводимый текст не будет подчеркнутым
"""
pass
def setHalfbright(self):
"""
Цвет выводимого текста использует полутона
"""
pass
def resetHalfbright(self):
"""
Цвет выводимого текста не использует полутона
"""
pass
def reset(self):
"""
Использовать шрифт по умолчанию
"""
pass
def endText(self):
"""
Обработка текста завершена
"""
return ""
def outputText(self, text):
"""
Вывести текст с установленными настройками
"""
return text
def setForeground(self, color):
"""
Установить цвет шрифта
"""
pass
def setBackground(self, color):
"""
Установить цвет фона
"""
pass
def resetForeground(self):
"""
Использовать цвет шрифта по умолчанию
"""
pass
def resetBackground(self):
"""
Использовать цвет фона по умолчанию
"""
pass
def setInvert(self):
"""
Включить инверсию
"""
def resetInvert(self):
"""
Выключить инверсию
"""
def pushState(self):
"""
Сохранить состояние текста
"""
def popState(self):
"""
Восстановить состояние текста из стека
"""
def newLine(self):
"""
Вывести текст на новой строке
"""
return "\n"
def tab(self):
"""
Вывести символ табуляции
"""
return "\t"
def clone(self):
"""
Создать копию объекта
"""
return self.__class__()
class SaveAttrOutput(BaseOutput):
"""
Базовый класс с сохранением атрибутов
"""
def __init__(self, state=None):
super(SaveAttrOutput, self).__init__()
self.prev_state = state.clone() if state else TextState()
self.current_state = self.prev_state.clone()
self.state_stack = []
def clone(self):
obj = self.__class__()
obj.current_state = self.current_state.clone()
obj.state_stack = list(self.state_stack)
return obj
def setBold(self):
self.current_state.bold = True
def resetBold(self):
self.current_state.bold = False
def setUnderline(self):
self.current_state.underline = True
def resetUnderline(self):
self.current_state.underline = False
def setHalfbright(self):
self.current_state.halfbright = True
def resetHalfbright(self):
self.current_state.halfbright = False
def reset(self):
self.resetBold()
self.resetHalfbright()
self.resetUnderline()
self.resetForeground()
self.resetBackground()
self.resetInvert()
def setForeground(self, color):
self.current_state.foreground = color
def resetForeground(self):
self.current_state.foreground = TextState.Colors.DEFAULT
def setBackground(self, color):
self.current_state.background = color
def resetBackground(self):
self.current_state.background = TextState.Colors.DEFAULT
def resetInvert(self):
self.current_state.invert = False
def setInvert(self):
self.current_state.invert = True
def pushState(self):
self.state_stack.append(self.current_state.clone())
def popState(self):
self.current_state = self.state_stack.pop()
class ColorTerminalOutput(SaveAttrOutput):
"""
Форматирует текст для вывода в консоль
"""
mapColors = ConsoleCodeMapping(ConsoleCodesInfo.FOREGROUND,
BaseColorMapping)
mapLightColors = ConsoleCodeMapping(ConsoleCodesInfo.FOREGROUND -
LightColorMapping.offset,
LightColorMapping)
mapBackgroundColors = ConsoleCodeMapping(ConsoleCodesInfo.BACKGROUND,
BaseColorMapping)
def __init__(self, state=None, escSymb=None):
SaveAttrOutput.__init__(self, state=state)
if escSymb is None:
self.escSymb = '\033'
else:
self.escSymb = escSymb
def setBold(self):
self.resetHalfbright()
super(ColorTerminalOutput, self).setBold()
def setHalfbright(self):
self.resetBold()
super(ColorTerminalOutput, self).setHalfbright()
def handleForeground(self, prevstate, curstate, attrs, tail_attrs):
"""Обработать изменение тона"""
cci = ConsoleCodesInfo
color = curstate.foreground
if color in self.mapColors:
attrs.append(self.mapColors[color])
elif color in self.mapLightColors:
if prevstate.bold == curstate.bold and not curstate.bold:
tail_attrs.append(cci.NORMAL)
attrs.append(cci.BOLD)
attrs.append(self.mapLightColors[color])
else:
attrs.append(cci.FOREGROUND_DEFAULT)
self.resetForeground()
def handleBackground(self, prevstate, curstate, attrs, tail_attrs):
"""Обработать изменение фона"""
color = curstate.background
if color in self.mapBackgroundColors:
attrs.append(self.mapBackgroundColors[color])
else:
attrs.append(ConsoleCodesInfo.BACKGROUND_DEFAULT)
def handleIntensity(self, prevstate, curstate, attrs, tail_attrs):
"""Обработать интенсивность"""
if curstate.bold and curstate.bold != prevstate.bold:
attrs.append(ConsoleCodesInfo.BOLD)
elif (curstate.halfbright and
prevstate.halfbright != curstate.halfbright):
attrs.append(ConsoleCodesInfo.HALFBRIGHT)
else:
attrs.append(ConsoleCodesInfo.NORMAL)
def handleUnderline(self, prevstate, curstate, attrs, tail_attrs):
"""Обработать подчеркивание"""
if curstate.underline:
attrs.append(ConsoleCodesInfo.UNDERLINE)
else:
attrs.append(ConsoleCodesInfo.NOUNDERLINE)
def handleInvert(self, prevstate, curstate, attrs, tail_attrs):
"""Обработать инверсию"""
if curstate.invert:
attrs.append(ConsoleCodesInfo.INVERT)
else:
attrs.append(ConsoleCodesInfo.NOINVERT)
def handleColor(self, prevstate, curstate, attrs, tail_attrs):
"""Обработать изменение цветов (фон и/или тон)"""
if prevstate.foreground != curstate.foreground:
self.handleForeground(prevstate, curstate, attrs, tail_attrs)
if prevstate.background != curstate.background:
self.handleBackground(prevstate, curstate, attrs, tail_attrs)
def _createAttrs(self, prevstate, curstate):
"""
Создать ESC последовательность для установки параметров текста
"""
attrs, tail_attrs = [], []
# получить интенсивность (полутон и жирность относятся к интенсивности)
intensity = lambda x: x & (TextState.Attributes.HALFBRIGHT |
TextState.Attributes.BOLD)
if (prevstate.attr != curstate.attr and
curstate.attr == TextState.Attributes.NONE and
curstate.foreground is TextState.Colors.DEFAULT and
curstate.background is TextState.Colors.DEFAULT):
attrs.append(ConsoleCodesInfo.RESET)
else:
if intensity(prevstate.attr) != intensity(curstate.attr):
self.handleIntensity(prevstate, curstate, attrs, tail_attrs)
if prevstate.underline != curstate.underline:
self.handleUnderline(prevstate, curstate, attrs, tail_attrs)
if prevstate.invert != curstate.invert:
self.handleInvert(prevstate, curstate, attrs, tail_attrs)
if (prevstate.foreground != curstate.foreground or
prevstate.background != curstate.background):
self.handleColor(prevstate, curstate, attrs, tail_attrs)
return attrs, tail_attrs
def _createEscCode(self, attrs):
"""
Создать ESC строку
"""
attrs = map(str, ['%s[' % self.escSymb] + attrs + ['m'])
return "%s%s%s" % (attrs[0], ";".join(attrs[1:-1]), attrs[-1])
def outputText(self, s):
"""
Задание параметров текста и вывод его
"""
if self.prev_state != self.current_state:
attr, tail_attrs = \
self._createAttrs(self.prev_state, self.current_state)
attr = self._createEscCode(attr)
if tail_attrs:
postattr = self._createEscCode(tail_attrs)
else:
postattr = ""
self.prev_state = self.current_state.clone()
else:
attr = ""
postattr = ""
return attr + s + postattr
def endText(self):
self.reset()
return self.outputText("")
def newLine(self):
return "\n"
def tab(self):
return "\t"
class ColorTerminal256Output(ColorTerminalOutput):
"""
Вывод на 256 цветный терминал
"""
mapLightColors = LightColorMapping.mapTS_Console
def handleForeground(self, prevstate, curstate, attrs, tail_attrs):
color = curstate.foreground
color256 = ConsoleColor256.rgbToConsole(color)
if not color256 and color in self.mapLightColors:
color256 = self.mapLightColors[color]
if color256:
attrs.extend([ConsoleCodesInfo.FOREGROUND256,
ConsoleCodesInfo.COLOR256,
color256])
else:
super(ColorTerminal256Output,
self).handleForeground(prevstate, curstate, attrs, tail_attrs)
def handleBackground(self, prevstate, curstate, attrs, tail_attrs):
color = curstate.background
color256 = ConsoleColor256.rgbToConsole(color)
if not color256 and color in self.mapLightColors:
color256 = self.mapLightColors[color]
if color256:
attrs.extend([ConsoleCodesInfo.BACKGROUND256,
ConsoleCodesInfo.COLOR256,
color256])
else:
super(ColorTerminal256Output,
self).handleBackground(prevstate, curstate, attrs, tail_attrs)
class ColorTerminal16Output(ColorTerminalOutput):
"""
Вывод на 16 цветный терминал с преобразованием RGB к ближайшему базовому
Bugs:
После преобразования текст и фон могут одинакового цвета
"""
def __init__(self, state=None, palette=None, escSymb=None):
ColorTerminalOutput.__init__(self, state=state, escSymb=escSymb)
self.palette = palette
def _handleNearestColors(self, color):
"""
Обработка преобразования к ближайшему цвету
"""
standardColors = TextState.normalColors + TextState.lightColors
if self.palette and color not in standardColors:
return self.palette.getBaseColorByRGB(color)
return color
def handleForeground(self, prevstate, curstate, attrs, tail_attrs):
"""
Добавить преобразование RGB к ближайшему базовому
"""
_curstate = curstate.clone()
_curstate.foreground = \
self._handleNearestColors(curstate.foreground)
super(ColorTerminal16Output, self).handleForeground(
prevstate, _curstate,
attrs, tail_attrs)
def handleBackground(self, prevstate, curstate, attrs, tail_attrs):
"""
Добавить преобразование RGB к ближайшему базовому
"""
mapHighNormal = dict(zip(TextState.lightColors,
TextState.normalColors))
_curstate = curstate.clone()
_curstate.background = \
self._handleNearestColors(curstate.background)
# преобразовать яркий цвет к обычному
_curstate.background = \
mapHighNormal.get(_curstate.background, _curstate.background)
super(ColorTerminal16Output, self).handleBackground(
prevstate, _curstate,
attrs, tail_attrs)
class SpanCssOutput(SaveAttrOutput):
"""
Форматирует текст для вывода в консоль
"""
def __init__(self, state=None, palette=SpanPalette()):
SaveAttrOutput.__init__(self, state=state)
self.palette = palette
def getStringColor(self, color, bold=False, halfbright=False,
background=False):
"""
Получить название цвета по номеру и состоянию текста
"""
if halfbright:
bright = SpanPalette.LOW_BRIGHT
elif bold:
bright = SpanPalette.HIGH_BRIGHT
else:
bright = SpanPalette.NORMAL_BRIGHT
if background:
return self.palette.getBackgroundColor(color)
else:
return self.palette.getTextColor(color, bright)
def getTags(self, prevstate, curstate):
"""
Создать tag span для указания параметров текста
"""
style = []
colorAttr = ["color", "background"]
if curstate.invert:
colorAttr = colorAttr[1], colorAttr[0]
if (prevstate.foreground != curstate.foreground or
prevstate.bold != curstate.bold or
curstate.invert or
prevstate.halfbright != curstate.halfbright):
sColor = self.getStringColor(curstate.foreground,
curstate.bold,
curstate.halfbright,
background=False)
style.append("%s:%s;" % (colorAttr[0], sColor))
if prevstate.background != curstate.background or curstate.invert:
sColor = self.getStringColor(curstate.background,
background=True)
style.append("%s:%s;" % (colorAttr[1], sColor))
if prevstate.underline != curstate.underline:
if curstate.underline:
style.append("text-decoration:underline;")
else:
style.append("text-decoration:none;")
if prevstate.bold != curstate.bold:
if curstate.bold:
style.append("font-weight:bold;")
else:
style.append("font-weight:normal;")
return '<span style="%s">' % "".join(style), '</span>'
def outputText(self, s):
if self.prev_state != self.current_state:
lattr, rattr = self.getTags(self.prev_state, self.current_state)
else:
lattr = rattr = ""
return lattr + XmlFormat.escaper(s) + rattr
def endText(self):
self.reset()
return ""
def newLine(self):
return "<br/>"
def tab(self):
return "&#9;"
class XmlOutput(SaveAttrOutput):
"""
Форматирует текст c описанием формата в XML для внутренней передачи
Bugs: игнорирует первоначальное состояние (state)
не экономное использование тэгов (например при выводе нескольких строк
"""
escaper = XmlFormat.escaper
def __init__(self, state=None):
super(XmlOutput, self).__init__(state=state)
self.clear_state = TextState()
def getXML(self, curstate, text):
"""
Создать управляющие тэги
:type curstate: TextState
:type text: str
"""
Tags, FontAttributes = XmlFormat.Tags, XmlFormat.FontAttributes
root = ET.Element("root")
tail = root
if (curstate.foreground != TextState.Colors.DEFAULT or
curstate.background != TextState.Colors.DEFAULT):
tail = ET.SubElement(tail, Tags.FONT)
sColor = str(curstate.foreground or "")
if sColor:
tail.attrib[FontAttributes.FOREGROUND] = sColor
sColor = str(curstate.background or "")
if sColor:
tail.attrib[FontAttributes.BACKGROUND] = sColor
if curstate.halfbright:
tail = ET.SubElement(tail, Tags.HALFBRIGHT)
if curstate.invert:
tail = ET.SubElement(tail, Tags.INVERT)
if curstate.underline:
tail = ET.SubElement(tail, Tags.UNDERLINE)
if curstate.bold:
tail = ET.SubElement(tail, Tags.BOLD)
tail.text = text
return root[0]
def xmlToString(self, xml):
"""
Получить строку xml не преобразовывая &, <, >
"""
def generator(root):
if root.attrib:
yield "<%s %s>" % (root.tag,
" ".join(['%s="%s"' % (k, v) for k, v in
root.attrib.items()]))
else:
yield "<%s>" % root.tag
if len(root):
for element in root:
for text in generator(element):
yield text
if root.text:
yield self.escaper(root.text)
yield "</%s>" % root.tag
return "".join(filter(None, list(generator(xml))))
def outputText(self, s):
if self.clear_state != self.current_state:
return self.xmlToString(self.getXML(self.current_state, s))
else:
return self.escaper(s)
def endText(self):
self.reset()
return ""
def newLine(self):
return "<br/>"
def tab(self):
return "<tab/>"
class BasePositionOutput(object):
"""
Объект составляющий ESC последовательности для управлением местом вывода
"""
def moveCursorUp(self, count=1):
"""
Переместить курсор вверх
"""
return ""
def moveCursorDown(self, count=1):
"""
Переместить курсор вниз
"""
return ""
def moveCursorRight(self, count=1):
"""
Переместить курсор вправо
"""
return ""
def moveCursorLeft(self, count=1):
"""
Переместить курсор влево
"""
return ""
def clearLine(self, whole_line=False):
"""
Очистить строку от курсора до конца или всю строку
"""
return ""
def savePosition(self):
"""
Сохранить положение курсора
"""
return ""
def restorePosition(self):
"""
Восстановить положение курсора
"""
return ""
class TerminalPositionOutput(BasePositionOutput):
"""
Управление позицией вывода текста в терминале
"""
class Codes(object):
UP = 'A'
DOWN = 'B'
RIGHT = 'C'
LEFT = 'D'
CLEAR_LINE = 'K'
CLEAR_FROM_CURSOR = '1'
CLEAR_WHOLE_LINE = '2'
SAVE_POSITION = 's'
RESTORE_POSITION = 'u'
def _createEscCode(self, attrs):
"""
Создать ESC строку
"""
return '\033[%s' % attrs
def _moveCursor(self, direct, count):
if int(count) > 1:
count = str(count)
else:
count = ""
return self._createEscCode("%s%s" % (count, direct))
def moveCursorDown(self, count=1):
return self._moveCursor(self.Codes.DOWN, count)
def moveCursorUp(self, count=1):
return self._moveCursor(self.Codes.UP, count)
def moveCursorRight(self, count=1):
return self._moveCursor(self.Codes.RIGHT, count)
def moveCursorLeft(self, count=1):
return self._moveCursor(self.Codes.LEFT, count)
def clearLine(self, whole_line=False):
if whole_line:
mode_code = self.Codes.CLEAR_WHOLE_LINE
else:
mode_code = self.Codes.CLEAR_FROM_CURSOR
return self._createEscCode("%s%s" % (mode_code, self.Codes.CLEAR_LINE))
def savePosition(self):
return self._createEscCode(self.Codes.SAVE_POSITION)
def restorePosition(self):
return self._createEscCode(self.Codes.RESTORE_POSITION)

@ -1,119 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2014-2016 Mir Calculate. 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.
from output import BaseOutput, BasePositionOutput
from calculate.lib.utils.text import _uu8
class Print(object):
"""
Упрощенное получение строки при помощи Output объектов """
def __init__(self, output=BaseOutput(),
position_controller=BasePositionOutput(),
printfunc=lambda x: x):
self.output = output
self.position_controller = position_controller
self.buffer = []
if isinstance(printfunc, file):
self.printfunc = printfunc.write
self.flush = printfunc.flush
else:
self.printfunc = printfunc
self.flush = lambda: None
@property
def bold(self):
self.buffer.append(self.output.setBold())
return self
@property
def underline(self):
self.buffer.append(self.output.setUnderline())
return self
def foreground(self, color):
self.buffer.append(self.output.setForeground(color))
return self
def background(self, color):
self.buffer.append(self.output.setBackground(color))
return self
@property
def invert(self):
self.buffer.append(self.output.setInvert())
return self
@property
def halflight(self):
self.buffer.append(self.output.setHalfbright())
return self
def up(self, count):
self.buffer.append(self.position_controller.moveCursorUp(count))
return self
def down(self, count):
self.buffer.append(self.position_controller.moveCursorDown(count))
return self
def right(self, count):
self.buffer.append(self.position_controller.moveCursorRight(count))
return self
def left(self, count):
self.buffer.append(self.position_controller.moveCursorLeft(count))
return self
@property
def clear_line(self):
self.buffer.append(self.position_controller.clearLine(whole_line=True))
return self
def __call__(self, s, *args, **kwargs):
if args or kwargs:
s = s.format(*args, **kwargs)
self.buffer.append(self.output.outputText(s))
self.buffer.append(self.output.endText())
try:
return self.printfunc("".join(_uu8(*filter(None, self.buffer))))
finally:
self.buffer = []
def clone(self):
obj = PrintClone(self.output.clone(),
self.position_controller,
self.printfunc)
obj.output.pushState()
return obj
class PrintClone(Print):
def __init__(self, output=BaseOutput(),
position_controller=BasePositionOutput(),
printfunc=lambda x: x):
super(PrintClone, self).__init__(output, position_controller, printfunc)
def __call__(self, s, *args, **kwargs):
s = s.format(*args, **kwargs)
self.buffer.append(self.output.outputText(s))
self.buffer.append(self.output.endText())
self.output.popState()
self.output.pushState()
try:
return self.printfunc("".join(filter(None, self.buffer)))
finally:
self.buffer = []

@ -1,631 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2008-2016 Mir Calculate. 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.
from random import choice
import os
import re
import math
from os import path
from calculate.lib.utils.dracut import Dracut
from calculate.lib.utils.system import SystemPath
import sys
import getpass
from types import StringType
import string
import glob
import pwd
import itertools
from calculate.lib.cl_lang import setLocalTranslate
_ = lambda x: x
setLocalTranslate('cl_lib3', sys.modules[__name__])
class CommonError(Exception):
pass
class _error(object):
# Здесь ошибки, если они есть
error = []
def getError(self):
"""Выдать ошибки"""
if not self.error:
return False
error = ""
for e in self.error:
error += e + "\n"
return error
def setError(self, error):
"""Установка ошибки"""
self.error.append(error)
return True
def clearErrors(self):
while self.error:
self.error.pop()
class _warning(object):
# Здесь предупреждения
warning = []
def getWarning(self):
"""Выдать ошибки"""
if not self.warning:
return False
warning = ""
for w in self.warning:
warning += w + "\n"
return warning
def setWarning(self, warning):
"""Установка ошибки"""
self.warning.append(warning)
return True
def clearWarnings(self):
while self.warning:
self.warning.pop()
def genpassword(passlen=9, chars=string.ascii_letters + string.digits):
"""Return random charset specified lenght (passlen)"""
return ''.join(map(lambda x: choice(chars),
xrange(0, passlen)))
def getpathenv():
"""Return path for run utilities"""
bindir = set(filter(path.exists,
['/sbin', '/bin', '/usr/sbin', '/usr/bin']))
env = os.environ
envPath = set(env.get('PATH', '').split(":")) | bindir
return ":".join(envPath)
from files import process, listDirectory
def cmpVersion(v1, v2):
"""Compare versions specified by tuple or string"""
if isinstance(v1, StringType):
v1 = getTupleVersion(v1)
if isinstance(v2, StringType):
v2 = getTupleVersion(v2)
return cmp((v1[0] + [0, ] * (len(v2[0]) - len(v1[0])), v1[1]),
(v2[0] + [0, ] * (len(v1[0]) - len(v2[0])), v2[1]))
def getTupleVersion(ver):
"""Get version specified by string as list:
Example:
2.6.30 [(2,6,30),('r',0)]
2.6.31-r1 [(2,6,31),('r',1)]
"""
suffix_value = {"pre": -2, "p": 0, "alpha": -4, "beta": -3,
"rc": -1}
def toTuple(v):
return map(lambda x: suffix_value[x] if x in suffix_value else x,
map(lambda x: int(x) if x.isdigit() else x,
re.findall("r\d+$|\d+|[a-zA-Z+]+",
v.replace('-SNAPSHOT', ''))))
vers, revision = re.search("(^.*?)(-r\d+)?$", ver, re.S).groups()
vers = toTuple(vers)
vers.extend([0] * (10 - len(vers)))
revision = toTuple(revision or "r0")
return [vers, revision]
def getInstalledVideo(prefix="/"):
"""Get installed video drivers"""
usrlib = SystemPath(prefix).usrlib
x11Drivers = "%s/xorg/modules/drivers" % usrlib
return map(lambda x: x[:-7],
filter(lambda x: x.endswith('_drv.so'),
listDirectory(x11Drivers)))
def getDistfilesVideo(prefix="/"):
"""Get video drivers from distfiles"""
distFiles = path.join(prefix, "usr/portage/distfiles")
driver_map = {'ati': 'fglrx',
'amd': 'fglrx',
'radeon': 'fglrx',
'nvidia-linux': 'nvidia',
'fglrx': 'fglrx'}
def driver_by_fn(fn):
fn = fn.lower()
for part in driver_map:
if part in fn:
return driver_map[part]
else:
return None
return list(set(
filter(None,
map(driver_by_fn,
listDirectory(distFiles)))))
def getAvailableVideo(prefix="/"):
"""Get available video drivers (installed and maybe installed)"""
return list(set(getInstalledVideo(prefix=prefix) +
getDistfilesVideo(prefix=prefix)))
def getPasswdUsers(minId=1000, maxId=65000, prefix="/",
datafile="etc/passwd"):
"""
Get users from passwd from minId to maxId
"""
retList = []
fileName = path.join(prefix, datafile)
if os.access(fileName, os.R_OK):
reNumb = re.compile("^\d+$")
lenData = 7
with open(fileName) as f:
userData = filter(lambda x: len(x) == lenData,
map(lambda x: x.rstrip().split(":"), f))
userData = filter(
lambda x: reNumb.match(x[2]) and minId <= int(x[2]) <= maxId,
userData)
sortUsers = map(lambda x: x[0], userData)
sortUsers.sort()
retList = ["root"] + sortUsers
return retList
def getSupportArch():
"""Get supported architectures by processor.
Is support processor x86_64 else only i686.
"""
if filter(lambda x: x.startswith('flags') and " lm " in x,
readLinesFile('/proc/cpuinfo')):
return ['i686', 'x86_64']
else:
return ['i686']
class CmdlineParams(object):
"""
Параметры опции загрузки ядра calculate=
"""
# названия параметров
Calculate = "calculate"
IsoscanFile = Dracut.IsoScanCmdParam
IOScheduler = "elevator"
# внутренние параметры calculate
Locale = "lang"
Keymap = "keymap"
Timezone = "timezone"
Resolution = "resolution"
Video = "video"
Composite = "composite"
Domain = "domain"
DomainPassword = "domain_pw"
Audio = "audio"
Clock = "clock"
def getValueFromCmdLine(option, num=None):
"""Get value of parameter from boot params
Parameters:
option param name
num number part of value parameter (, split)
"""
cmdLine = "/proc/cmdline"
calculateParam = option
names = (
CmdlineParams.Locale,
CmdlineParams.Keymap,
CmdlineParams.Timezone,
CmdlineParams.Resolution,
CmdlineParams.Video,
CmdlineParams.Composite,
CmdlineParams.Domain,
CmdlineParams.DomainPassword,
CmdlineParams.Audio,
CmdlineParams.Clock)
# try get timezone from kernel calculate param
try:
if num is None:
name = None
elif type(num) == str and not num.isdigit():
name = num
num = names.index(name)
else:
name = names[int(num)]
with open(cmdLine, "r") as f:
for param in f.read().split(" "):
parname, op, value = param.partition("=")
if parname == calculateParam and op == "=":
# new format
if name is None:
return value.strip()
if ":" in value:
params = dict(
map(lambda x: x.partition(':')[0::2],
filter(lambda x: x,
value.split(','))))
return params.get(name, "").strip()
# old format
else:
values = value.split(",")
if len(values) > num and values[num].strip():
return values[num].strip()
except (IOError, ValueError, IndexError):
return ""
def getValueFromConfig(config, name):
"""Get value of parameter from bash type file
Parameters:
config config file name
name param name
"""
reMatch = re.compile("^%s\s*=\s*(.*?)\s*$" % name, re.I)
val = None
try:
if path.exists(config):
for line in open(config, "r"):
match = reMatch.match(line)
if match:
val = match.groups()[0].strip()
for quotas in ('"', "'"):
if val.startswith(quotas) and val.endswith(quotas):
val = val.strip(quotas)
break
except Exception:
pass
return val
def getVideoFromXorgLog(prefix="/", available_drivers=()):
"""Get video driver from Xorg log"""
# Try analize Xorg.{DISPLAY}.log
display = os.environ.get('DISPLAY', ':0')
if display and available_drivers:
reDriver = re.compile('|'.join(map(lambda x: "%s_drv.so" % x,
available_drivers)))
display_number = re.search(r':(\d+)(\..*)?', display)
reDriverName = re.compile(r'([^/]+)_drv.so')
if display_number:
xorg_log_file = path.join(prefix, 'var/log/Xorg.%s.log' %
display_number.group(1))
if path.exists(xorg_log_file):
matchStrs = map(
lambda x: x.group(1),
filter(lambda x: x, map(
reDriverName.search,
filter(lambda x: "drv" in x and reDriver.search(x),
readLinesFile(xorg_log_file)))))
if matchStrs:
reUnload = re.compile('UnloadModule: "(%s)"' %'|'.join(x
for x in matchStrs))
matchUnload = [x.group(1) for x in
(reUnload.search(x) for x in
readLinesFile(xorg_log_file)) if x]
for drv in matchUnload:
if drv in matchStrs:
matchStrs.remove(drv)
if matchStrs:
return matchStrs[-1]
return ""
def getVideoFromXorgConf(prefix="/"):
"""Get video driver from xorg.conf"""
xorg_conf = path.join(prefix, 'etc/X11/xorg.conf')
# analize /etc/X11/xorg.conf
if path.exists(xorg_conf):
with open(xorg_conf) as f:
matchSect = re.search(r'Section "Device".*?EndSection',
f.read(), re.S)
if matchSect:
resDriver = re.search(r'\n\s*Driver\s*"([^"]+)"',
matchSect.group(0), re.S)
if resDriver:
return resDriver.group(1)
return ""
def getVideoFromCmdLine():
"""Get video driver from cmdline"""
videoVal = getValueFromCmdLine(CmdlineParams.Calculate,
CmdlineParams.Video)
videoVal = {'i915': 'intel'}.get(videoVal, videoVal)
return videoVal
def getVideoFromModules():
workedModules = map(lambda x: x[0],
filter(lambda x: x[1].isdigit() and int(x[1]) > 0,
map(lambda x: x.split()[:3:2],
readLinesFile('/proc/modules'))))
mapModules = {'nouveau': 'nouveau',
'radeon': 'radeon',
'i915': 'intel',
'fglrx': 'fglrx',
'nvidia': 'nvidia',
# 'amdgpu': 'amdgpu',
'via': 'via',
'vmware': 'vmware'}
for module, videodrv in mapModules.items():
if module in workedModules:
return videodrv
else:
return ""
def getVideoFromVendor(hr_video, available_drivers):
if "nvidia" in available_drivers:
defaultNvidia = "nvidia"
else:
defaultNvidia = "nv"
if "fglrx" in available_drivers:
defaultAti = "fglrx"
else:
defaultAti = "radeon"
defaultDriver = {
'nvidia': defaultNvidia,
'ati': defaultAti,
'intel': 'intel',
'via': 'via',
'vmware': 'vmware'}
if hr_video in defaultDriver and \
defaultDriver[hr_video] in available_drivers:
return defaultDriver[hr_video]
else:
return "other"
def getCompositeFromXorgconf(prefix="/"):
"""Get composite value from xorg.conf"""
xorgConfig = path.join(prefix,
"etc/X11/xorg.conf")
confLines = readLinesFile(xorgConfig)
flagStartExtensions = False
lineCompositeTmp = ""
lineComposite = ""
for line in confLines:
line = line.strip()
if flagStartExtensions:
if line.startswith('EndSection'):
lineComposite = lineCompositeTmp
break
elif line.startswith('Section'):
break
if line.startswith('Option') and '"Composite"' in line:
lineCompositeTmp = line
else:
if line.startswith('Section') and '"Extensions"' in line:
flagStartExtensions = True
if lineComposite:
listOpt = filter(lambda x: x.strip(), lineComposite.split('"'))
if len(listOpt) == 3:
ret = listOpt[2].lower()
if ret in ("on", "true", "yes", "1"):
return "on"
elif ret in ("off", "false", "no", "0"):
return "off"
return None
def getKernelUid(dev):
"""Get Kernel UID by UUID of device"""
blkidProcess = process('/sbin/blkid', '-c', '/dev/null', '-s', 'UUID',
'-o', 'value', dev)
res = blkidProcess.read().strip()
if res:
return res[:8]
else:
return dev.replace("/", "")
def dict_by_columns(i, sep, key, value):
"""
Получить словарь из файла, где каждая строка разделена sep символом
на колонки, key - номер колонки ключей, value номер колонки значений
"""
max_val = max(key, value)
return dict(
map(lambda x: (x[key], x[value]),
filter(lambda x: len(x) >= max_val,
map(lambda x: x.split(sep), i))))
from calculate.lib.utils.files import readLinesFile
def getUserGroups(userName, prefix="/"):
"""
Get user groups from /etc/groups
"""
return map(lambda x: x[0],
filter(lambda x: len(x) > 1 and userName in x[1].split(','),
map(lambda x: x.split(':')[0::3],
readLinesFile(path.join(prefix, 'etc/group')))))
def getUserPrimaryGroup(userName, prefix="/"):
"""
Получить основную группу пользователя
"""
passwd = path.join(prefix, 'etc/passwd')
group = path.join(prefix, 'etc/group')
sep_symb = ":"
userGidMap = dict_by_columns(readLinesFile(passwd), sep_symb, 0, 3)
gidGroupMap = dict_by_columns(readLinesFile(group), sep_symb, 2, 0)
if userName in userGidMap:
gid = userGidMap[userName]
return gidGroupMap.get(gid, None)
return None
def getGroups(prefix="/"):
"""
Get groups from etc/group
"""
return filter(None,
map(lambda x: x.split(':')[0],
readLinesFile(path.join(prefix, 'etc/group'))))
def getPagesInterval(count, offset, length):
"""
Получить интервал страниц (например 2 из 5, при (5,5,15)
"""
rt = max(0, length - offset - count)
lt = min(length, offset)
lt = int(math.ceil(float(lt) / count))
rt = int(math.ceil(float(rt) / count))
return lt + 1, rt + lt + 1
def mountEcryptfs(userName, userPwd, userDir):
"""
Подключить ecryptfs
Args:
userName: логин пользователя
userPwd: пароль пользователя
userDir: домашний каталог пользователя
"""
from calculate.lib.utils.files import (process, readLinesFile, STDOUT)
from calculate.lib.utils.mount import isMount
if ".Private" in isMount(userDir):
return True
# расшифровать passphrase
ecryptUserName = path.join('/home/.ecryptfs', userName)
wrappedPassphraseFile = path.join(ecryptUserName,
".ecryptfs/wrapped-passphrase")
p = process('/usr/bin/ecryptfs-unwrap-passphrase', wrappedPassphraseFile)
p.write(userPwd)
try:
if p.failed():
raise Exception
result = p.readlines()
if len(result) > 1 and "Passphrase:" in result[0] and result[1]:
passphrase = result[1]
else:
raise Exception
except Exception:
raise CommonError(_("Failed to unwrap the passphrase"))
# добавить passphrase в ключи ядра
p = process('/usr/bin/ecryptfs-add-passphrase', '--fnek', '-',
stderr=STDOUT)
p.write(passphrase)
if not p.success():
raise CommonError(p.read() + "Failed to add passphrase")
# получить сигнатуры шифрования содержимого файлов и имен файлов
try:
ecryptfs_sig, ecryptfs_fnek_sig = \
readLinesFile(path.join(ecryptUserName, ".ecryptfs/Private.sig"))
except ValueError:
raise CommonError(_("Failed to parse Private.sig"))
# подключить шифрованный раздел
mountProcess = process(
'/sbin/mount.ecryptfs',
path.join(ecryptUserName, '.Private'),
userDir,
'-o', 'ecryptfs_passthrough=n,' 'ecryptfs_cipher=aes,'
'ecryptfs_key_bytes=16,' 'no_sig_cache,'
'ecryptfs_enable_filename_crypto=y,'
'ecryptfs_sig={ecryptfs_sig},'
'ecryptfs_fnek_sig={ecryptfs_fnek_sig},'
'passphrase_passwd_fd=0'.format(
ecryptfs_sig=ecryptfs_sig,
ecryptfs_fnek_sig=ecryptfs_fnek_sig), stderr=STDOUT)
# отправить пароль через stdin
mountProcess.write("passphrase_passwd=" + userPwd)
return mountProcess.success()
def isBootstrapDataOnly(user_dir):
"""
Каталог содержит только сертификат, созданный командой cl-core
а так же настроенные симлинки на .face, skel
"""
from calculate.lib.utils.files import find
max_diff = 10
skel_files = set([x for x in find('/etc/skel', onefilesystem=True,
fullpath=False)
if not x.startswith(".calculate")])
user_data = find(user_dir, onefilesystem=True, fullpath=False)
user_files = itertools.islice((x for x in user_data
if not x.startswith(".calculate")),
len(skel_files)+max_diff)
user_files = set(user_files)
return not len(user_files - skel_files) > max_diff
def getPortageUidGid():
"""
Получить uid и gid пользователя portage
"""
data = pwd.getpwnam('portage')
if data:
return data.pw_uid, data.pw_gid
else:
return 250, 250
def getXorgConfContent(prefix="/"):
def readFile(fn):
with open(fn, 'r') as f:
return f.read()
return "\n".join(readFile(x) for x in
[path.join(prefix, "etc/X11/xorg.conf")] +
glob.glob(path.join(prefix,"etc/X11/xorg.conf.d/*"))
if path.isfile(x))
def get_fastlogin_domain_path(dv):
"""
Получить пути до ресурсов для определения нужно ли выполнять
шаблоны для доменного профиля пользователя
"""
from calculate.lib.cl_template import LayeredIni
overlay_suffix_path = 'profiles/templates'
template_paths = dv.Get('main.cl_template_path')
for template_path in template_paths:
if template_path.endswith(overlay_suffix_path):
template_path = template_path[:-len(overlay_suffix_path)-1]
yield os.path.join(template_path, ".git")
for envfile in (LayeredIni.IniPath.System,
LayeredIni.IniPath.Etc,
LayeredIni.IniPath.Local,
LayeredIni.IniPath.Remote):
yield envfile
for cenvfile in ("/etc/calculate/calculate.env",
"/var/calculate/calculate.env",
"/var/lib/calculate/calculate.env",
"/var/calculate/remote/calculate.env"):
yield cenvfile
yield "/var/log/emerge.log"

@ -1,75 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2014-2016 Mir Calculate. 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 dbus
from os import path
from xml.etree import ElementTree
def get_dbus_path_tree(bus_name, bus=None):
"""
Получить список путей объектов по имени шины
"""
if not bus:
bus = dbus.SessionBus()
def get_child(objpath):
obj = bus.get_object(bus_name, objpath)
yield objpath
if obj:
for node in ElementTree.fromstring(obj.Introspect()):
nodepath = path.join(objpath, node.get('name', ''))
if node.tag == 'node':
for child in get_child(nodepath):
yield child
try:
for x in get_child('/'):
yield x
except dbus.DBusException:
pass
def run_dbus_core(hostname, port):
if hostname in ("127.0.0.1", "localhost"):
try:
from ip import check_port
if not check_port(hostname, port):
bus = dbus.SystemBus()
DBUS_INTERFACE="org.calculate.CoreInterface"
DBUS_NAME="org.calculate.Core"
DBUS_OBJECT="/Core"
try:
remote_object = bus.get_object(DBUS_NAME, DBUS_OBJECT)
remote_object.Start(port)
except dbus.DBusException:
pass
except ImportError:
pass
def get_dbus_hostname():
try:
bus = dbus.SystemBus()
DBUS_INTERFACE="org.calculate.CoreInterface"
DBUS_NAME="org.calculate.Core"
DBUS_OBJECT="/Core"
try:
remote_object = bus.get_object(DBUS_NAME, DBUS_OBJECT)
return str(remote_object.ServerHostname())
except dbus.DBusException:
pass
except ImportError:
pass
return ""

@ -1,763 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2008-2016 Mir Calculate. 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 re
import os
from os import path
from calculate.lib.utils.tools import (Cachable, GenericFs, unique)
import files
from time import sleep
from calculate.lib.cl_lang import setLocalTranslate
setLocalTranslate('cl_lib3', sys.modules[__name__])
def getUUIDDict(revers=False, devs=()):
"""Get dict UUID -> dev"""
blkidProcess = files.process("/sbin/blkid", "-s", "UUID", "-c", "/dev/null",
*list(devs))
reSplit = re.compile('^([^:]+):.*UUID="([^"]+)"', re.S)
searched = (x.groups() for x in map(reSplit.search, blkidProcess) if x)
mapping = (("UUID=%s" % uuid, udev.get_devname(name=dev, fallback=dev))
for dev, uuid in searched)
if revers:
return {v: k for k, v in mapping}
else:
return {k: v for k, v in mapping}
def detectDeviceForPartition(dev):
"""Detect parent device for partition by udev and return property"""
prop = udev.get_device_info(name=dev)
if prop.get('DEVTYPE', '') != 'partition':
return ''
parentpath = path.dirname(prop.get('DEVPATH', ''))
if parentpath:
devProp = udev.get_device_info(path=parentpath)
return devProp.get('DEVNAME', '')
return None
def countPartitions(devname):
"""Count partition for specified device"""
syspath = udev.get_device_info(name=devname).get('DEVPATH', '')
if not syspath:
return 0
device_name = path.basename(syspath)
return len([x for x in sysfs.listdir(syspath) if x.startswith(device_name)])
class LvmCommand(object):
@property
def lvm_cmd(self):
return files.getProgPath('/sbin/lvm')
def get_physical_extent_size(self):
if not self.lvm_cmd:
return ""
pvdata = files.process(self.lvm_cmd, "lvmconfig", "--type", "full",
"allocation/physical_extent_size")
if pvdata.success():
return pvdata.read().strip().rpartition("=")[-1]
return ""
def get_pvdisplay_output(self, output):
if not self.lvm_cmd:
return ""
pvdata = files.process(self.lvm_cmd, "pvdisplay", "-C", "-o",
output, "--noh")
if pvdata.success():
return pvdata.read()
return ""
def vgscan(self):
if self.lvm_cmd:
return files.process(self.lvm_cmd, "vgscan").success()
return False
def vgchange(self):
failed = True
if self.lvm_cmd:
failed = files.process("vgchange", '-ay').failed()
failed |= files.process("vgchange", '--refresh').failed()
return not failed
def lvchange(self, groups):
failed = True
if self.lvm_cmd:
failed = False
for group in groups:
failed |= files.process("lvchange", '-ay', group).failed()
failed |= files.process("lvchange", '--refresh', group).failed()
return not failed
def execute(self, *command):
if self.lvm_cmd:
return files.process(self.lvm_cmd, *command).success()
return False
def double_execute(self, *command):
if not self.execute(*command):
sleep(2)
return self.execute(*command)
return False
def remove_lv(self, vg, lv):
return self.double_execute("lvremove", "%s/%s" % (vg, lv), "-f")
def remove_vg(self, vg):
return self.double_execute("vgremove", vg, "-f")
def remove_pv(self, pv):
return self.double_execute("pvremove", pv, "-ffy")
class Lvm(Cachable):
"""
LVM информация
"""
def __init__(self, commander):
super(Lvm, self).__init__()
self.commander = commander
@property
def groups(self):
return sorted({vg for vg, lv, pv in self.pvdisplay_full()})
@Cachable.methodcached()
def get_pesize(self):
return self.commander.get_physical_extent_size()
@Cachable.methodcached()
def pvdisplay_output(self, output):
return self.commander.get_pvdisplay_output(output)
def pvdisplay_full(self):
data = self.pvdisplay_output("vg_name,lv_name,pv_name").strip()
if data:
out = (x.split() for x in data.split("\n"))
return [tuple(y.strip() for y in x)
for x in out if x and len(x) == 3]
return []
def used_partitions(self, vg_eq, lv_eq):
return list(sorted({part for vg, lv, part in self.pvdisplay_full()
if vg == vg_eq and lv_eq == lv}))
def refresh(self):
"""Run command which refresh information about LVM"""
if not os.environ.get('EBUILD_PHASE'):
self.commander.vgscan()
self.commander.vgchange()
self.commander.lvchange(lvm.groups)
def remove_lv(self, vg, lv):
return self.commander.remove_lv(vg, lv)
def remove_vg(self, vg):
return self.commander.remove_vg(vg)
def remove_pv(self, pv):
return self.commander.remove_pv(pv)
def lspci(filtername=None, shortInfo=False):
"""Get hash of lspci, filtred by filtername. If shortInfo, then
type,vendor and name get only first word
pcidata(domain,bus,slot,func)
'type'
'vendor'
'name'"""
reData = re.compile(r'(\S+)\s"([^"]+)"\s+"([^"]+)"\s+"([^"]+)"', re.S)
if filtername:
if hasattr(filtername, '__call__'):
filterfunc = filtername
else:
filterfunc = lambda x: filtername in x
else:
filterfunc = lambda x: x
if shortInfo:
sfunc = lambda x: x.partition(" ")[0]
else:
sfunc = lambda x: x
lspciProg = files.checkUtils('/usr/sbin/lspci')
processLsPci = files.process(lspciProg, "-m")
retData = {}
for device in map(lambda x: x.groups(),
filter(lambda x: x,
map(reData.search,
filter(filterfunc,
processLsPci)))):
retData[device[0]] = {'type': sfunc(device[1]),
'vendor': sfunc(device[2]),
'name': sfunc(device[3])}
return retData
class UdevAdmNull(object):
def info_property(self, path=None, name=None):
return {}
def info_export(self):
return ""
def settle(self, timeout=15):
pass
def trigger(self, subsystem=None):
pass
@property
def broken(self):
return False
class UdevAdm(UdevAdmNull):
"""
Объект взаимодействия с udevadm
"""
def __init__(self):
self.first_run = True
@property
def udevadm_cmd(self):
return files.getProgPath('/sbin/udevadm')
@property
def broken(self):
return bool(not self.udevadm_cmd)
def info_property(self, path=None, name=None):
if self.first_run:
self.trigger("block")
self.settle()
self.first_run = False
if name is not None:
type_query = "--name"
value = name
else:
type_query = "--path"
value = path
udev_output = files.process(self.udevadm_cmd, "info", "--query",
"property",
type_query, value).read().split("\n")
return dict(x.partition("=")[0::2] for x in udev_output if "=" in x)
def info_export(self):
return files.process(self.udevadm_cmd, "info", "-e").read().strip()
def trigger(self, subsystem=None):
if subsystem:
files.process(self.udevadm_cmd,
"trigger", "--subsystem-match", subsystem).success()
else:
files.process(self.udevadm_cmd, "trigger").success()
def settle(self, timeout=15):
files.process(self.udevadm_cmd, "settle",
"--timeout=%d" % timeout).success()
class Udev(object):
"""
Объект возвращающий преобразованную или кэшированную информацию о системе
из udev
"""
def clear_cache(self):
self.path_cache = {}
self.name_cache = {}
self.udevadm = UdevAdm()
def __init__(self, udevadm=None):
self.path_cache = {}
self.name_cache = {}
if isinstance(udevadm, UdevAdm):
self.udevadm = udevadm
else:
self.udevadm = UdevAdm()
if self.udevadm.broken:
self.udevadm = UdevAdmNull()
def get_device_info(self, path=None, name=None):
"""Get device info by syspath of name"""
if name is not None:
cache = self.name_cache
value = devfs.realpath(name)
name = value
else:
cache = self.path_cache
value = sysfs.realpath(path)
path = value
if value not in cache:
data = self.udevadm.info_property(path, name)
devname = data.get('DEVNAME', '')
devpath = data.get('DEVPATH', '')
if devpath:
devpath = "/sys%s" % devpath
if devname:
self.name_cache[devname] = data
if devpath:
self.path_cache[devname] = data
return data
else:
# print "fromCache",keyCache
return cache[value]
def get_block_devices(self):
"""
Получить все устройства SUBSYSTEM=block устройства
"""
for block in sysfs.glob(sysfs.Path.Block, "*"):
yield block
blockname = path.basename(block)
for part in sysfs.glob(block, "%s*" % blockname):
yield part
#block_devices = {sysfs.realpath(x)[len(sysfs.base_dn):]: x
# for x in sysfs.listdir("/block/*", fullpath=True)}
#block_entries = (x for x in self.udevadm.info_export().split("\n\n")
# if "SUBSYSTEM=block" in x)
#for block in block_entries:
# for entry in (x[3:] for x in block.split("\n")
# if x.startswith("P:")):
# if "/block/" in entry:
# yield entry[entry.rindex("/block/"):]
# break
def syspath_to_devname(self, it, dropempty=True):
"""
Преобразовать sysfs имя устройства в /dev
"""
for x in it:
info = self.get_device_info(path=x)
if "DEVNAME" in info:
yield info['DEVNAME']
elif not dropempty:
yield ""
def devname_to_syspath(self, it, dropempty=True):
"""
Преобразовать /dev имя устройства в sysfs
"""
for x in it:
info = self.get_device_info(name=x)
if "DEVPATH" in info:
yield info['DEVPATH']
elif not dropempty:
yield ""
def get_devname(self, path=None, name=None, fallback=None):
if fallback is None:
if name:
fallback = name
else:
fallback = ""
return self.get_device_info(path, name).get("DEVNAME", fallback)
def get_syspath(self, path=None, name=None, fallback=None):
if fallback is None:
if path:
fallback = path
else:
fallback = ""
return self.get_device_info(path, name).get("DEVPATH", fallback)
def refresh(self, trigger_only=False):
"""
Run command which refresh information about device in udev
"""
if not trigger_only:
self.clear_cache()
files.quite_unlink("/etc/blkid.tab")
if not os.environ.get('EBUILD_PHASE'):
self.udevadm.trigger(subsystem="block")
self.udevadm.settle(15)
def is_cdrom(self, prop):
return prop.get("ID_CDROM", '') == "1"
def is_device(self, prop):
if "DEVPATH" not in prop:
return False
return (sysfs.exists(prop.get("DEVPATH", ''), "device") and
prop.get('DEVTYPE', "") == "disk")
def is_raid(self, prop):
return (prop.get("MD_LEVEL", "").startswith("raid") and
prop.get("DEVTYPE", "") == "disk")
def is_raid_partition(self, prop):
return (prop.get("MD_LEVEL", "").startswith("raid") and
prop.get("DEVTYPE", "") == "partition")
def is_lvm(self, prop):
return "DM_LV_NAME" in prop and "DM_VG_NAME" in prop
def is_partition(self, prop):
return prop.get("DEVTYPE") == "partition"
def is_block_device(self, prop):
return prop.get("SUBSYSTEM") == "block"
def get_device_type(self, path=None, name=None):
info = self.get_device_info(path, name)
syspath = info.get("DEVPATH", '')
if self.is_cdrom(info):
return "cdrom"
if self.is_device(info):
return "disk"
if self.is_partition(info):
return "%s-partition" % self.get_device_type(
os.path.dirname(syspath))
if self.is_raid(info):
for rd in raid.devices_syspath(syspath):
return "%s-%s" % (self.get_device_type(rd), info["MD_LEVEL"])
else:
return "loop"
if self.is_lvm(info):
for lvdev in lvm.used_partitions(info["DM_VG_NAME"],
info["DM_LV_NAME"]):
return "%s-lvm" % self.get_device_type(name=lvdev)
if self.is_block_device(info):
return "loop"
return ""
def get_partition_type(self, path=None, name=None):
info = self.get_device_info(path, name)
"""Get type of dos part table (primary,extended or logical)"""
if info.get('ID_PART_ENTRY_SCHEME') == 'dos':
partId = info.get('ID_PART_ENTRY_TYPE', '')
partNumber = info.get('ID_PART_ENTRY_NUMBER', '')
if partId and partNumber:
if partId == "0x5":
return "extended"
elif int(partNumber) > 4:
return "logical"
else:
return "primary"
return info.get('ID_PART_TABLE_TYPE', '')
def _get_disk_devices(self, path=None, name=None):
"""
Возвращает список базовых устройств (признак базовое ли устройство,
название устройства)
:param path:
:param name:
:return:
"""
info = udev.get_device_info(path=path, name=name)
syspath = info.get("DEVPATH", '')
if syspath:
# real device
if self.is_device(info):
yield True, info.get("DEVNAME", "")
# partition
elif self.is_partition(info):
for x in self._get_disk_devices(path=os.path.dirname(syspath)):
yield x
# md raid
elif udev.is_raid(info):
yield False, info.get("DEVNAME", "")
for rd in sorted(raid.devices_syspath(syspath)):
for x in self._get_disk_devices(path=rd):
yield x
# lvm
elif udev.is_lvm(info):
yield False, info.get("DEVNAME", "")
for lvdev in lvm.used_partitions(info["DM_VG_NAME"],
info["DM_LV_NAME"]):
for x in self._get_disk_devices(name=lvdev):
yield x
def get_disk_devices(self, path=None, name=None):
"""Get real parent device by partition,lvm,mdraid"""
return sorted({
dev
for realdevice, dev in self._get_disk_devices(path, name)
if realdevice
})
def get_all_base_devices(self, path=None, name=None):
"""
Получить все устройства (включая RAID и LVM) из которого состоит
указанное устройство, исключая loop устройства
"""
try:
devs = (dev
for realdevice, dev in self._get_disk_devices(path, name)
if not dev.startswith("/dev/loop"))
if not self.is_partition(self.get_device_info(path, name)):
next(devs)
return list(unique(devs))
except StopIteration:
return []
def humanreadableSize(size, compsize=0):
"""
Human readable size (1024 -> 1K and etc)
"""
try:
size = int(size)
except ValueError:
return ""
suffix = (((1024 ** 0), "", False),
((1024 ** 1), "K", False),
((1024 ** 2), "M", False),
((1024 ** 3), "G", True),
((1024 ** 4), "T", True),
((1024 ** 5), "P", True))
suffix = filter(lambda x: abs(size-compsize) > x[0], suffix)
if suffix:
suffix = suffix[-1]
printSize = int(size / (float(suffix[0]) / 10))
printSizeTail = printSize % 10
printSize /= 10
if suffix[2] and printSizeTail:
return "%d.%d%s" % (printSize, printSizeTail, suffix[1])
else:
return "%d%s" % (printSize, suffix[1])
return str(size)
def getPartitionSize(syspath=None, name=None, inBytes=False):
"""
Получить размер раздела
"""
dev = udev.get_syspath(path=syspath, name=name)
SECTORSIZE = 512
if dev and sysfs.exists(dev, "size"):
try:
size = int(sysfs.read(dev, "size").strip()) * SECTORSIZE
except ValueError:
return ""
if inBytes:
return str(size)
else:
return humanreadableSize(size)
return ""
class MdadmCommand(object):
@property
def mdadm_cmd(self):
return files.getProgPath('/sbin/mdadm')
def stop_raid(self, devraid):
if not self.mdadm_cmd:
return ""
return files.process(self.mdadm_cmd, "-S", devraid).success()
def zero_superblock(self, dev):
if not self.mdadm_cmd:
return ""
return files.process(self.mdadm_cmd, "--zero-superblock", dev).success()
class Raid(object):
def __init__(self, commander):
self.commander = commander
def devices_info(self, raid):
prop = udev.get_device_info(path=raid)
if udev.is_raid(prop):
for rdblock in sysfs.glob(raid, "md/dev-*", "block"):
yield udev.get_device_info(sysfs.syspath(rdblock))
def devices_syspath(self, raid):
"""
Получить (syspath) устройств из которых состоит raid
:param raid:
:return:
"""
for x in self.devices(raid, pathname="DEVPATH"):
yield x
def devices(self, raid, pathname="DEVNAME"):
"""
Получить устройства /dev из которых состоит RAID, не возвращает
список этих устройств для раздела сделанного для RAID (/dev/md0p1)
:param raid:
:param pathname:
:return:
"""
for device_info in self.devices_info(raid):
devname = device_info.get(pathname, "")
if devname:
yield devname
def remove_raid(self, raidname):
# итератор не подходит, так как после остановки RAID
# не получится узнать его диски
raidparts = list(self.devices(udev.get_syspath(name=raidname)))
failed = False
failed |= not (self.commander.stop_raid(raidname) or
self.commander.stop_raid(raidname))
for dev in raidparts:
failed |= not (self.commander.zero_superblock(dev) or
self.commander.zero_superblock(dev))
return not failed
def loadEfiVars():
"""
Load module with efivars
"""
import mount
modprobe = files.getProgPath('/sbin/modprobe')
if not sysfs.exists(sysfs.Path.Efi) and modprobe:
files.process(modprobe, 'efivars').success()
mount_cmd = files.getProgPath('/bin/mount')
mtab = mount.Mounts()
efivars_path = sysfs.syspath(sysfs.Path.Efivars)
if mtab.exists(efivars_path) and mtab.readonly(efivars_path):
files.process(mount_cmd, "-o", "rw,remount", efivars_path).success()
mtab.rebuildCache()
class DeviceFs(object):
"""
Файловая система, используемая для описания /sys, /dev
"""
def __init__(self, fs=None):
if isinstance(fs, GenericFs):
self.fs = fs
else:
self.fs = files.RealFs("/")
def pathjoin(self, *dns):
res = path.join("/", *(dn[1:] if dn.startswith("/") else dn
for dn in dns))
return res
def exists(self, *dn):
return self.fs.exists(self.pathjoin(*dn))
def read(self, *fn):
return self.fs.read(self.pathjoin(*fn))
def realpath(self, *fn):
return self.fs.realpath(self.pathjoin(*fn))
def write(self, *args):
fn = args[:-1]
data = args[-1]
self.fs.write(self.pathjoin(*fn), data)
def listdir(self, *dn, **kw):
fullpath = kw.get("fullpath", False)
return self.fs.listdir(self.pathjoin(*dn), fullpath=fullpath)
def glob(self, *dn):
for fn in self.fs.glob(self.pathjoin(*dn)):
yield fn
class Sysfs(DeviceFs):
"""
Объект взаимодействующий с sysfs
"""
base_dn = "/sys"
class Path(object):
Firmware = "firmware"
Efi = path.join(Firmware, "efi")
Efivars = path.join(Efi, "efivars")
ClassNet = "class/net"
Input = "class/input"
Block = "block"
Dmi = "class/dmi/id"
Drm = "class/drm"
Module = "module"
BlockScheduler = "queue/scheduler"
def syspath(self, *dns):
"""
Получить путь до указанного устройства.
:param dns:
:return:
"""
res = path.join("/", *(dn[1:] if dn.startswith("/") else dn
for dn in dns))
if res.startswith(self.base_dn):
return res
else:
return path.join(self.base_dn, res[1:])
def glob(self, *fn):
for fn in self.fs.glob(self.pathjoin(*fn)):
yield fn[len(self.base_dn):]
pathjoin = syspath
class Devfs(DeviceFs):
"""
Объект взаимодействующий с devfs
"""
base_dn = "/dev"
sysfs = Sysfs()
devfs = Devfs()
udev = Udev()
lvm = Lvm(LvmCommand())
raid = Raid(MdadmCommand())
class HwinfoError(Exception):
pass
class HwinfoTimeout(HwinfoError):
pass
class HwinfoNotExists(HwinfoError):
pass
class Hwinfo(object):
"""
Объект получения информации из hwinfo
"""
command_timeout = 20
@property
def hwinfo_cmd(self):
return files.getProgPath("/usr/sbin/hwinfo")
def framebuffer(self):
"""
Получить вывод hwinfo --framebuffer
"""
return self.run("--framebuffer")
def run(self, *command):
if not self.hwinfo_cmd:
raise HwinfoNotExists()
try:
return files.process(*[self.hwinfo_cmd] + list(command),
**{'timeout':self.command_timeout}).read()
except files.ProcessTimeout:
raise HwinfoTimeout()
def resolutions(self):
"""
Получить допустимые разрешения фреймбуффера
"""
re_modes = re.compile("^\s+Mode 0x[0-9a-f]+:\s+(\d+x\d+)\s",re.M)
return tuple(sorted(unique(re_modes.findall(self.framebuffer()))))

File diff suppressed because it is too large Load Diff

@ -1,555 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2008-2016 Mir Calculate. 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.
from files import (process, checkUtils, readFile, listDirectory,
getRunCommands, getProgPath, FilesError)
import device
import sys
import os
import re
import struct
import fcntl
import socket
import math
import ctypes
from os import path
import select
import time
from calculate.lib.cl_lang import setLocalTranslate
_ = lambda x: x
setLocalTranslate('cl_lib3', sys.modules[__name__])
PROCFS_NET_PATH = "/proc/net/dev"
# From linux/sockios.h
SIOCGIFINDEX = 0x8933
SIOCGIFFLAGS = 0x8913
SIOCSIFFLAGS = 0x8914
SIOCSIFHWADDR = 0x8924
SIOCSIFADDR = 0x8916
SIOCSIFNETMASK = 0x891C
SIOCETHTOOL = 0x8946
SIOCGIFADDR = 0x8915
SIOCGIFNETMASK = 0x891B
SIOCGIFHWADDR = 0x8927
# Resources allocated
IFF_RUNNING = 0x40
IFF_MASTER = 0x400
IFF_SLAVE = 0x800
# ip digit from 0|1-255|254 (template)
IP_DIG = "[%s-9]|(?:1[0-9]|[1-9])[0-9]|2[0-4][0-9]|25[0-%s]"
# ip net 0-32
IP_NET_SUFFIX = "[0-9]|[12][0-9]|3[012]"
# ip digs 1-254,0-254,0-255
IP_DIGS = {'dig1_254': IP_DIG % (1, 4), 'dig0_254': IP_DIG % (0, 4),
'dig0_255': IP_DIG % (0, 5), }
# ip addr 10.0.0.12
IP_ADDR = "(%(dig1_254)s)\.(%(dig0_254)s)\.(%(dig0_254)s)\.(%(dig1_254)s)" % \
IP_DIGS
IP_MASK = "(%(dig0_255)s)\.(%(dig0_255)s)\.(%(dig0_255)s)\.(%(dig0_255)s)" % \
IP_DIGS
# ip addr for net 10.0.0.0
IP_NET = "(%(dig1_254)s)\.(%(dig0_254)s)\.(%(dig0_254)s)\.(%(dig0_254)s)" % \
IP_DIGS
# ip and net 192.168.0.0/16
IP_ADDR_NET = "(%(ipaddr)s)/((%(ipnet)s))" % {'ipaddr': IP_NET,
'ipnet': IP_NET_SUFFIX}
reIp = re.compile("^{0}$".format(IP_ADDR))
reNetSuffix = re.compile("^{0}$".format(IP_NET_SUFFIX))
reNet = re.compile("^{0}$".format(IP_ADDR_NET))
reMask = re.compile("^{0}$".format(IP_MASK))
def checkIp(ip):
"""Check ip"""
return reIp.match(ip)
def checkNetSuffix(netSuffix):
"""Check net suffix"""
return reNetSuffix.match(netSuffix)
def checkNet(net):
"""Check net"""
if not reNet.match(net):
return False
ip, op, cidr = net.partition('/')
mask = strIpToIntIp(cidrToMask(int(cidr)))
return (strIpToIntIp(ip) & mask) == (strIpToIntIp(ip))
maskDigs = map(lambda x: str(x),
(0b10000000, 0b11000000, 0b11100000, 0b11110000,
0b11111000, 0b11111100, 0b11111110, 0b11111111))
def checkMask(mask):
"""Check net"""
if not mask:
return False
if mask.count('.') != 3:
return False
zero = False
for dig in mask.split('.'):
if zero or not dig in maskDigs:
if dig == "0":
zero = True
else:
return False
return True
def getIpAndMask(interface="eth0"):
"""Get ip and mask from interface"""
ifconfig = process('/sbin/ifconfig', interface)
res = re.search(r"inet addr:(\S+)\s.*Mask:(\S+)", ifconfig.read(), re.S)
if res:
return res.groups()
else:
return "", ""
def strIpToIntIp(addr):
"""Convert ip specified by string to integer"""
addr = addr.split('.')
return ((int(addr[0]) << 24) |
(int(addr[1]) << 16) |
(int(addr[2]) << 8) |
(int(addr[3])))
def intIpToStrIp(addr):
"""Convert ip specified by integer to string"""
return "{0}.{1}.{2}.{3}".format(
addr >> 24, (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff)
def numMaskToCidr(netmask):
"""
Convert integer mask to cidr
"""
neg_net = ctypes.c_uint32(~netmask).value
return 32 - int(math.log(neg_net, 2)) - 1 if neg_net else 32
def maskToCidr(mask):
"""Convert mask specified by str to net"""
mask = strIpToIntIp(mask)
return numMaskToCidr(mask)
def cidrToMask(cidr):
"""Convert net to mask specified by str"""
return intIpToStrIp((2 ** cidr - 1) << (32 - cidr))
def getIpNet(ip, mask=None, cidr=None):
"""Get net (xx.xx.xx.xx/xx) by ip address and mask"""
ip = strIpToIntIp(ip)
if not mask is None:
net = maskToCidr(mask)
else:
net = int(cidr)
mask = cidrToMask(net)
mask = strIpToIntIp(mask)
return "{ip}/{net}".format(ip=intIpToStrIp(ip & mask),
net=net)
def isIpInNet(checkip, *ipnets):
"""Check is ip in specified nets"""
return map(lambda x: x[0],
filter(lambda x: strIpToIntIp(checkip) & x[2] == strIpToIntIp(
x[1]) & x[2],
map(lambda x: (
x[0], x[1][0],
strIpToIntIp(cidrToMask(int(x[1][1])))),
map(lambda x: (x, x.partition('/')[0::2]),
ipnets))))
def isUsingNetworkManager():
try:
p = process("/usr/bin/nmcli", "general", "status")
return p.success()
except FilesError:
return False
def isNMDhcp(interface="eth0"):
p = process("/usr/bin/nmcli", "-g", "ipv4.method", "connection", "show", "eth0")
if p.success():
if "auto" in p.read():
return True
return False
def isDhcpIp(interface="eth0"):
"""Get ip by dhcp or static"""
# dhclients (dhcpcd, dhclient (dhcp), udhcpc (busybox)
if isUsingNetworkManager() and isNMDhcp(interface):
return True
commands = getRunCommands()
dhcpProgs = ("dhcpcd", "dhclient", "udhcpc")
if filter(lambda x: interface in x and any(prog in x for prog in dhcpProgs),
commands):
return True
else:
# если запущен демон dhcpcd
if filter(lambda x: "dhcpcd\x00-q" in x, commands):
curIp = getIp(interface)
dhcpcd = getProgPath('/sbin/dhcpcd')
leaseIp = \
map(lambda x: x.group(1),
filter(None,
map(re.compile('^ip_address=(.*)$').search,
process(dhcpcd, '-U', interface))))
if not curIp or leaseIp and leaseIp[0] == curIp:
return True
return False
def getRouteTable(onlyIface=()):
"""Get route table, exclude specifed iface"""
ipProg = checkUtils('/sbin/ip')
routes = process(ipProg, "route")
if onlyIface:
filterRe = re.compile("|".join(map(lambda x: r"dev %s" % x, onlyIface)))
routes = filter(filterRe.search, routes)
for line in routes:
network, op, line = line.partition(" ")
routeParams = map(lambda x: x.strip(), line.split())
# (network,{'via':value,'dev':value})
if network:
yield (network, dict(zip(routeParams[0::2], routeParams[1::2])))
def getInterfaces():
"""
Get available interfaces (discard which hasn't device)
"""
def filter_ethernet(x):
# exclude loopback
if device.sysfs.read(x, "type").strip() != "1":
return False
# physical device
if device.sysfs.exists(x, "device"):
return True
# exclude bridges
if device.sysfs.exists(x, "brport"):
return False
if device.sysfs.exists(x, "bridge"):
return False
# exclude bonding
if device.sysfs.exists(x, "bonding"):
return False
if device.sysfs.exists(x, "bonding_slave"):
return False
# exclude slave devices
if any(device.sysfs.glob(x, "lower_*")):
return False
return True
return sorted(path.basename(x)
for x in device.sysfs.listdir(
device.sysfs.Path.ClassNet, fullpath=True)
if filter_ethernet(x))
def getMaster(iface):
master_path = [device.sysfs.Path.ClassNet, iface, "master"]
if device.sysfs.exists(*master_path):
return path.split(device.sysfs.realpath(*master_path))[-1]
return None
def getIp(iface):
sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
ifreq = struct.pack('16sH14s', iface, socket.AF_INET, '\x00' * 14)
try:
res = fcntl.ioctl(sockfd, SIOCGIFADDR, ifreq)
except IOError:
return ""
finally:
sockfd.close()
ip = struct.unpack('16sH2x4s8x', res)[2]
return socket.inet_ntoa(ip)
def checkFlag(iface, flag):
sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
ifreq = struct.pack('16sH14s', iface, socket.AF_INET, '\x00' * 14)
try:
res = fcntl.ioctl(sockfd, SIOCGIFFLAGS, ifreq)
except IOError:
return False
finally:
sockfd.close()
return bool(struct.unpack('16sH2x4s8x', res)[1] & flag)
def getPlugged(iface):
return checkFlag(iface, IFF_RUNNING)
def isSlaveInterface(iface):
return checkFlag(iface, IFF_SLAVE)
def isMasterInterface(iface):
return checkFlag(iface, IFF_MASTER)
def getMask(iface):
"""
Get mask for interface
"""
sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
ifreq = struct.pack('16sH14s', iface, socket.AF_INET, '\x00' * 14)
try:
res = fcntl.ioctl(sockfd, SIOCGIFNETMASK, ifreq)
except IOError:
return 0
finally:
sockfd.close()
netmask = socket.ntohl(struct.unpack('16sH2xI8x', res)[2])
return numMaskToCidr(netmask)
def isWireless(iface):
"""
Является ли данное устройство беспроводным
"""
return device.sysfs.exists(device.sysfs.Path.ClassNet, iface, "wireless")
def getMac(iface):
"""
Get mac for interface
"""
sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
ifreq = struct.pack('16sH14s', iface, socket.AF_UNIX, '\x00' * 14)
try:
res = fcntl.ioctl(sockfd, SIOCGIFHWADDR, ifreq)
except IOError:
return ""
address = struct.unpack('16sH14s', res)[2]
mac = struct.unpack('6B8x', address)
sockfd.close()
return ":".join(['%02X' % i for i in mac])
def getOperState(iface):
"""
Get interface state up or down
"""
if device.sysfs.read(device.sysfs.Path.ClassNet, iface, "operstate") == "down":
return "down"
return "up"
def isOpenPort(ip, port):
"""
Test if an [ip:port] is open
"""
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((ip, int(port)))
s.shutdown(2)
return True
except Exception:
return False
class IPError(Exception):
"""Error received on work with ip"""
pass
class Pinger(object):
# ICMP parameters
ICMP_ECHO = 8 # Echo request (per RFC792)
ICMP_MAX_RECV = 2048 # Max size of incoming buffer
def checksum(self, source_string):
"""
A port of the functionality of in_cksum() from ping.c
Ideally this would act on the string as a series of 16-bit ints (host
packed), but this works.
Network data is big-endian, hosts are typically little-endian
"""
countTo = (int(len(source_string) / 2)) * 2
sum = 0
count = 0
# Handle bytes in pairs (decoding as short ints)
while count < countTo:
if sys.byteorder == "little":
loByte = ord(source_string[count])
hiByte = ord(source_string[count + 1])
else:
loByte = ord(source_string[count + 1])
hiByte = ord(source_string[count])
sum += hiByte * 256 + loByte
count += 2
# Handle last byte if applicable (odd-number of bytes)
# Endianness should be irrelevant in this case
if countTo < len(source_string): # Check for odd length
loByte = ord(source_string[len(source_string) - 1])
sum += loByte
# Truncate sum to 32 bits (a variance from ping.c,which
sum &= 0xffffffff
# uses signed ints, but overflow is unlikely in ping)
sum = (sum >> 16) + (sum & 0xffff) # Add high 16 bits to low 16 bits
sum += (sum >> 16) # Add carry from above (if any)
answer = ~sum & 0xffff # Invert and truncate to 16 bits
answer = socket.htons(answer)
return answer
def ping(self, destIP, timeout, numDataBytes):
"""
Returns either the delay (in ms) or None on timeout.
"""
delay = None
try: # One could use UDP here, but it's obscure
mySocket = socket.socket(socket.AF_INET, socket.SOCK_RAW,
socket.getprotobyname("icmp"))
except socket.error as e:
raise IPError(_("failed. (socket error: '%s')" % e.args[1]))
my_ID = os.getpid() & 0xFFFF
sentTime = self.send_one_ping(mySocket, destIP, my_ID, 0,
numDataBytes)
if sentTime is None:
mySocket.close()
return delay
recvTime, dataSize, iphSrcIP, icmpSeqNumber, iphTTL = \
self.receive_one_ping(mySocket, my_ID, timeout)
mySocket.close()
if recvTime:
delay = (recvTime - sentTime) * 1000
return (dataSize, socket.inet_ntoa(struct.pack("!I", iphSrcIP)),
iphTTL, delay)
else:
raise IPError(_("Request timed out"))
def send_one_ping(self, mySocket, destIP, myID, mySeqNumber, numDataBytes):
"""
Send one ping to the given >destIP<.
"""
try:
destIP = socket.gethostbyname(destIP)
except socket.gaierror as e:
raise IPError(e.strerror)
# Header is type (8), code (8), checksum (16), id (16), sequence (16)
myChecksum = 0
# Make a dummy heder with a 0 checksum.
header = struct.pack(
"!BBHHH", self.ICMP_ECHO, 0, myChecksum, myID, mySeqNumber
)
padBytes = []
startVal = 0x42
for i in range(startVal, startVal + numDataBytes):
padBytes += [(i & 0xff)] # Keep chars in the 0-255 range
data = bytes(padBytes)
# Calculate the checksum on the data and the dummy header.
# Checksum is in network order
myChecksum = self.checksum(header + data)
# Now that we have the right checksum, we put that in. It's just easier
# to make up a new header than to stuff it into the dummy.
header = struct.pack(
"!BBHHH", self.ICMP_ECHO, 0, myChecksum, myID, mySeqNumber
)
packet = header + data
sendTime = time.time()
try:
# Port number is irrelevant for ICMP
mySocket.sendto(packet, (destIP, 1))
except socket.error as e:
raise IPError("General failure (%s)" % (e.args[1]))
return sendTime
def receive_one_ping(self, mySocket, myID, timeout):
"""
Receive the ping from the socket. Timeout = in ms
"""
timeLeft = timeout / 1000.0
while True: # Loop while waiting for packet or timeout
startedSelect = time.time()
whatReady = select.select([mySocket], [], [], timeLeft)
howLongInSelect = (time.time() - startedSelect)
if isinstance(whatReady[0], list) and not whatReady[0]: # Timeout
return None, 0, 0, 0, 0
timeReceived = time.time()
recPacket, addr = mySocket.recvfrom(self.ICMP_MAX_RECV)
ipHeader = recPacket[:20]
iphVersion, iphTypeOfSvc, iphLength, \
iphID, iphFlags, iphTTL, iphProtocol, \
iphChecksum, iphSrcIP, iphDestIP = struct.unpack(
"!BBHHHBBHII", ipHeader
)
icmpHeader = recPacket[20:28]
icmpType, icmpCode, icmpChecksum, \
icmpPacketID, icmpSeqNumber = struct.unpack(
"!BBHHH", icmpHeader
)
if icmpPacketID == myID: # Our packet
dataSize = len(recPacket) - 28
return timeReceived, dataSize, iphSrcIP, icmpSeqNumber, iphTTL
timeLeft = timeLeft - howLongInSelect
if timeLeft <= 0:
return None, 0, 0, 0, 0
def check_port(target, port, timeout=5):
"""
Проверить порт (port) на хосте (target)
:param target: хост
:param port: порт
:param timeout: таймаут
:return: True - порт открыт, False - нет хоста, порт закрыт, firewall
"""
try:
targetIP = socket.gethostbyname(target)
except socket.gaierror:
return False
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(timeout)
try:
s.connect((targetIP, port))
s.close()
except (socket.error, socket.timeout):
return False
return True

@ -1,101 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2015-2016 Mir Calculate. 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 re
from os import path
from files import typeFile, process
from subprocess import Popen, PIPE
from calculate.lib.cl_lang import setLocalTranslate
setLocalTranslate('cl_lib3', sys.modules[__name__])
class InitrdFileError(Exception):
pass
class InitrdFile(object):
"""
Работа с initrd файлом
"""
re_kver_path = re.compile("/modules/([^/]+)/kernel")
def __init__(self, _file):
if not self.is_cpio(_file):
raise InitrdFileError("%s is not initrd file" % _file)
self._file = _file
def get_kernel_version(self):
for fn in self.get_names():
if "/modules/" in fn and "/kernel" in fn:
m = self.re_kver_path.search(fn)
if m:
return m.group(1)
else:
break
return ""
@staticmethod
def skipcpio_data(fn):
skipcpio_cmd = "/usr/lib/dracut/skipcpio"
skipcpio = Popen([skipcpio_cmd, fn], stdout=PIPE, stderr=PIPE,
close_fds=True)
buf = skipcpio.stdout.read(1024*1024)
skipcpio.terminate()
skipcpio.poll()
return buf
@staticmethod
def is_cpio(fn):
buf = InitrdFile.skipcpio_data(fn)
if not buf:
return False
buftype = typeFile(magic=0x4).getMTypeBuf
return "ASCII cpio archive" in buftype(buf)
def get_names(self):
if not path.exists(self._file):
# raise IOError
with open(self._file, 'r'):
pass
buftype = typeFile(magic=0x4).getMTypeBuf
rdtype = buftype(self.skipcpio_data(self._file))
if "LZ4" in rdtype:
arch_cmd = '/usr/bin/lz4'
elif "XZ" in rdtype:
arch_cmd = '/usr/bin/xz'
elif "Zstandard" in rdtype:
arch_cmd = "/usr/bin/zstd"
else:
arch_cmd = '/bin/gzip'
skipcpio_cmd = "/usr/lib/dracut/skipcpio"
skipcpio = Popen([skipcpio_cmd, self._file], stdout=PIPE, stderr=PIPE,
close_fds=True)
gz = Popen([arch_cmd, "-dc"], stdout=PIPE, stderr=PIPE,
stdin=skipcpio.stdout, close_fds=True)
cpio = Popen(["/bin/cpio", "-tf"], stdout=PIPE, stdin=gz.stdout,
stderr=PIPE, close_fds=True)
try:
for fn in cpio.stdout.xreadlines():
yield fn.rstrip()
finally:
cpio.terminate()
gz.terminate()
skipcpio.terminate()
def __iter__(self):
return iter(self.get_names())

@ -1,339 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2008-2017 Mir Calculate. 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 re
from calculate.lib.utils.tools import SingletonParam
import files
import device
import errno
from contextlib import contextmanager
from files import process, makeDirectory
from tools import traverse
import tempfile
_ = lambda x: x
from calculate.lib.cl_lang import setLocalTranslate
setLocalTranslate('cl_lib3', sys.modules[__name__])
def isMount(dn):
"""
Возвращает путь примонтированного dn
"""
def find_names(old_dn):
dn = os.path.abspath(old_dn)
yield dn
if dn.startswith('/dev'):
info = device.udev.get_device_info(name=dn)
if 'DM_VG_NAME' in info and 'DM_LV_NAME' in info:
yield '/dev/mapper/{vg}-{lv}'.format(vg=info['DM_VG_NAME'],
lv=info['DM_LV_NAME'])
def get_overlay_mounts(line):
mounts = line.split(' ')
yield mounts[1]
for dn in re.findall("(?:lowerdir=|upperdir=|workdir=)([^,]+)",
mounts[3]):
yield dn
find_data = set(find_names(dn))
ret = ""
for line in files.readLinesFile('/etc/mtab'):
if " overlay " not in line:
if " " in line:
mounts = set(line.split(' ')[:2])
if mounts & find_data:
ret = (mounts - find_data).pop()
else:
mounts = set(get_overlay_mounts(line))
dest = line.split(' ')[1]
if mounts & find_data:
if dest in find_data:
ret = "overlay"
else:
return dest
return ret
class MountHelperError(Exception):
pass
class MountHelperNotFound(MountHelperError):
pass
class MountHelper(object):
"""Data reader for fstab"""
data_file = '/etc/fstab'
NAME, DIR, TYPE, OPTS, FREQ, PASSNO = range(0, 6)
def __init__(self, data_file=None, devs=()):
from device import getUUIDDict
if data_file:
self.data_file = data_file
self.cache = []
self.rotateCache = []
self.dictUUID = getUUIDDict(devs=devs)
self.rebuildCache()
def _readdata(self):
with open(self.data_file, 'r') as f:
return f.read()
def rebuildCache(self):
"""Rebuild cache from fstab file"""
def setlen(ar):
return ar[:6] + [""] * (6 - len(ar))
self.cache = \
map(lambda x: setlen(map(lambda y: y.strip(), x.split())),
filter(lambda x: x.strip() and not x.lstrip().startswith("#"),
self._readdata().split('\n')))
for data in self.cache:
convertDev = lambda x: (os.path.realpath(x)
if x.startswith('/') else x)
data[0] = device.udev.get_device_info(
name=convertDev(self.dictUUID.get(data[0], data[0]))
).get('DEVNAME', data[0])
data[1] = data[1] if data[2] != "swap" else "swap"
self.rotateCache = zip(*self.cache)
def getBy(self, what=DIR, where=NAME, _in=None, eq=None,
contains=None, noteq=None, allentry=False):
"""Get data from fstab"""
if eq is not None:
filterfunc = lambda x: x[where] == eq
elif _in is not None:
filterfunc = lambda x: x[where] in _in
elif contains is not None:
filterfunc = lambda x: contains in x[where]
else:
filterfunc = lambda x: x[where] != noteq
res = map(lambda x: x[what], filter(filterfunc, self.cache))
if allentry:
return res
else:
return "" if not res else res[-1]
def getFields(self, *fields):
"""Get all data by specifie fields"""
return zip(*reduce(lambda x, y: x + [self.rotateCache[y]], fields, []))
def isReadonly(self, what=DIR, eq=None):
for data in filter(lambda x: x[what] == eq, self.cache):
opts = data[self.OPTS].split(',')
if "ro" in opts:
return True
elif "rw" in opts:
return False
raise MountHelperNotFound(eq)
def writable(self, dn):
return not self.isReadonly(eq=dn)
def readonly(self, dn):
return self.isReadonly(eq=dn)
def exists(self, dn):
return self.isExists(eq=dn) or self.isExists(what=self.NAME, eq=dn)
def isExists(self, what=DIR, eq=None, noteq=None):
"""Field with condition exist in fstab"""
if not eq is None:
filterfunc = lambda x: x[what] == eq
else:
filterfunc = lambda x: x[what] != noteq
return bool(filter(filterfunc, self.cache))
class FStab(MountHelper):
"""Data reader for fstab"""
__metaclass__ = SingletonParam
data_file = '/etc/fstab'
class Mounts(MountHelper):
"""Data reader for fstab"""
data_file = '/etc/mtab'
class DiskSpaceError(Exception):
pass
class DiskSpace(object):
def __init__(self):
self.df_cmd = files.getProgPath('/bin/df')
def get_free(self, dev=None, dn=None):
if dev:
mp = isMount(dev)
if not mp:
raise DiskSpaceError(_("Device %s must be mounted") % dev)
dn = dev
p = files.process(self.df_cmd, dn, "-B1")
if p.success():
data = p.read().strip()
lines = data.split('\n')
if len(lines) >= 2:
cols = filter(None, lines[1].split())
if len(cols) == 6:
return int(cols[3])
raise DiskSpaceError(_("Wrong df output:\n%s") % data)
else:
raise DiskSpaceError(str(p.readerr()))
def commonPath(*paths):
"""Return common path from list of paths"""
paths = map(lambda x: os.path.normpath(x).split('/'), paths)
res = map(lambda x: x[0],
filter(lambda x: filter(lambda y: x[0] == y, x[1:]), zip(*paths)))
return "/".join(res)
def childMounts(pathname):
"""Get all mount points which contain path"""
if pathname != "none":
absPath = os.path.abspath(pathname)
else:
absPath = pathname
mtabFile = '/etc/mtab'
if not os.access(mtabFile, os.R_OK):
return ""
with open(mtabFile) as f:
return reduce(lambda x, y: x + [y],
filter(lambda x: commonPath(absPath, x[0]) == absPath or \
commonPath(absPath, x[1]) == absPath,
map(lambda x: [x[0], x[1]],
map(lambda x: x.split(" "), f))),
[])
class MountError(Exception):
pass
def mount(source, target, fstype=None, options=None):
params = [source, target]
if options is not None:
params.insert(0, "-o%s"%",".join(traverse([options])))
if fstype is not None:
params.insert(0, "-t%s"%fstype)
p = process("/bin/mount", *params)
if p.success():
return True
else:
raise MountError(
_("Failed to mount {source} to {target}").format(
source=source,
target=target) +
_(":") + str(p.readerr()))
def umount(target):
p = process("/bin/umount", target)
if p.success():
return True
else:
raise MountError(
_("Failed to umount {target}").format(
target=target) +
_(":") + str(p.readerr()))
def fuser_mountpoint(mp):
"""
Проверить занята ли точка монтирования
"""
p = process("/bin/fuser","-m",mp)
return p.success()
def try_umount(target):
"""
Попытаться отключить точку монтирования
"""
try:
return umount(target)
except MountError:
return False
class BtrfsError(Exception):
pass
class Btrfs(object):
"""
Получение информации о btrfs
"""
check_path = "/run/calculate/mount"
def __init__(self, block):
self.block = block
if not os.path.exists(block):
raise BtrfsError(_("Device not found"))
@contextmanager
def mount(self):
dn = None
try:
makeDirectory(self.check_path)
dn = tempfile.mkdtemp(prefix="btrfscheck-", dir=self.check_path)
mount(self.block, dn, "btrfs")
yield dn
except KeyboardInterrupt:
raise
except MountError as e:
if "wrong fs type" in str(e):
raise BtrfsError(_("{} is not btrfs").format(self.block))
else:
raise BtrfsError(str(e))
finally:
if dn:
if os.path.ismount(dn):
umount(dn)
os.rmdir(dn)
def get_compression(self, rel_dn):
rel_dn = rel_dn.lstrip("/")
with self.mount() as dn:
try:
full_dn = os.path.join(dn, rel_dn)
if os.path.exists(full_dn):
return files.xattr.get(full_dn, "btrfs.compression")
else:
return ""
except files.XAttrError as e:
if "No such attr" in str(e):
return ""
raise BtrfsError(_("Failed to get btrfs compression"))
@property
def compression(self):
return self.get_compression("")
def set_compression(self, rel_dn, value):
rel_dn = rel_dn.lstrip("/")
with self.mount() as dn:
try:
full_dn = os.path.join(dn, rel_dn)
if not os.path.exists(full_dn):
makeDirectory(full_dn)
return files.xattr.set(full_dn, "btrfs.compression", value)
except (IOError, files.XAttrError) as e:
raise BtrfsError(_("Failed to set btrfs compression"))
@compression.setter
def compression(self, value):
self.set_compression("", value)

@ -1,438 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2008-2016 Mir Calculate. 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 types
from calculate.lib import cl_overriding
import re
import sys
from itertools import *
from calculate.lib.cl_lang import setLocalTranslate
_ = lambda x: x
setLocalTranslate('cl_lib3', sys.modules[__name__])
_u = lambda t: t.decode('UTF-8', 'replace') if isinstance(t, str) else t
_uu = lambda *tt: tuple(_u(t) for t in tt)
_u8 = lambda t: t.encode('UTF-8', 'replace') if isinstance(t, unicode) else t
_uu8 = lambda *tt: tuple(_u8(t) for t in tt)
def columnMatrix(*cols):
"""
Split columns text to matrix
"""
def splitter(s, wid):
"""Split string by width and \\n"""
for part in _u(s).split('\n'):
if wid > 0:
while part:
p, part = part[:wid], part[wid:]
yield p
else:
yield part
return izip_longest(
*starmap(splitter,
izip_longest(islice(cols, 0, None, 2),
islice(cols, 1, None, 2))),
fillvalue="")
def columnStr(*cols):
"""
Get string for write, data by columns
Parameters:
cols list(tuple(text,width))
Return:
string for display
Example: columnStr( "Some text", 10, "Next column", 20 )
"""
def generateStrings(*cols):
colWidth = list(islice(cols, 1, None, 2))
for line in map(lambda x: izip_longest(colWidth, x),
columnMatrix(*cols)):
yield " ".join(("{0:%d}" % (width or 1)).format(s) for \
width, s in line)
return "\n".join(generateStrings(*cols))
def justify(s, width):
"""
Выровнить текст по ширине
Параметры:
s выводимая строка
width ширина на которую надо выровнить строку
Возвращаямые параметры:
Выровненная строка
"""
# если подана строка без пробелов - прекратить обработку
if s.find(' ') == -1:
return s
pos = 0
# переводим в юникод для правильного вычисления длины
try:
s = s.decode('utf-8')
# пропуск если это не utf-8
except UnicodeEncodeError:
pass
# пока длина строки меньше указанной
while len(s) < width:
# находим очередной пробел
pos = s.find(' ', pos)
# если не найден искать сначала
if pos == -1:
pos = s.find(' ')
# вставить в позицию еще один пробел
s = s[:pos] + ' ' + s[pos:]
# оставить удвоенный пробел
pos += 3
# вернуть строку в utf8 если она пришла в utf8
return s.encode('utf-8')
class tableReport(object):
"""Display data in table"""
def __init__(self, title, headerList, dataList, colSpan=1):
# title
self.title = title
# elements list for first table row
self.headerList = headerList
# row list (each row is list)
self.dataList = map(lambda x: map(lambda y: str(y).expandtabs(),
map(lambda y: y or "", x)),
dataList)
# calculate columns list
self.columnsWidth = self.getColumsnWidth()
self.vertChar = colSpan * " " + "|" + colSpan * " "
self.crossChar = colSpan * "-" + "+" + colSpan * "-"
self.colSpan = colSpan
self.align = "<" + "^" * (len(headerList) - 2) + "<"
def setAutosize(self, colNum=-1, maxSize=None, forceMax=False):
"""
Set width for specified column
"""
if maxSize is None:
maxSize = get_term_size()[1]
if not maxSize:
maxSize = 80
spanSize = self.colSpan * 2 + 1
if colNum < 0:
colNum += len(self.columnsWidth)
currentSize = sum(map(lambda x: x + spanSize, self.columnsWidth)) + 1
if currentSize < maxSize and not forceMax:
return
excludeSize = sum(map(lambda x: x[1] + spanSize,
filter(lambda x: x[0] != colNum,
enumerate(
self.columnsWidth)))) + spanSize + 1
if maxSize - excludeSize > 5:
self.columnsWidth[colNum] = maxSize - excludeSize
def getColumsnWidth(self):
"""Находит максимальную ширину каждой колонки
результат список ширин колонок
"""
columnsWidth = []
for s in self.headerList:
columnsWidth.append(len(_u(s)))
lenCol = len(self.headerList)
maxLenCol = lenCol
# Вычисляем максимальное количество столбцов
for s in self.dataList:
lenS = len(s)
if maxLenCol < lenS:
maxLenCol = lenS
if maxLenCol > lenCol:
appCol = maxLenCol - lenCol
# Добавляем элементы в список ширин
for i in range(appCol):
columnsWidth.append(0)
# Вычисляем ширину столбцов
for e in self.dataList:
for i, s in enumerate(e):
lenS = len(_u(s))+1
if columnsWidth[i] < lenS:
columnsWidth[i] = lenS
return columnsWidth
def createFormatStr(self, listStr, offset=0):
"""Создает список (текст, ширина ...)"""
return chain(*izip_longest(listStr,
map(lambda x:x-offset, self.columnsWidth),
fillvalue=""))
def printFunc(self, s):
"""
Overriding print
"""
cl_overriding.printSUCCESS(s, printBR=False)
def line_printer(self, s):
"""
Вывод используется для отображения "линий" таблицы
"""
self.printFunc(s)
def head_printer(self, s):
"""
Вывод используется для отображения текста в шапке
"""
self.printFunc(s)
def body_printer(self, s):
"""
Вывод используется для отображения текста в таблице
"""
self.printFunc(s)
def default_printer(self, s):
self.printFunc(s)
def printReport(self, printRows=True):
"""Напечатать данные в табличном виде"""
if self.title:
self.printFunc(self.title)
listStrSep = []
for lenCol in self.columnsWidth:
listStrSep.append("-" * lenCol)
printData = [[self.crossChar, self.line_printer,
columnMatrix(*self.createFormatStr(listStrSep))],
[self.vertChar, self.head_printer,
columnMatrix(*self.createFormatStr(self.headerList))],
[self.crossChar, self.line_printer,
columnMatrix(*self.createFormatStr(listStrSep))]]
for s in self.dataList:
printData.append([self.vertChar, self.body_printer,
columnMatrix(*self.createFormatStr(s, 1))])
printData.append([self.crossChar, self.line_printer,
columnMatrix(*self.createFormatStr(listStrSep))])
last_col = len(self.headerList) - 1
for char, _print, row in printData:
for line in row:
self.line_printer(char[self.colSpan:])
for i, width, txt in izip(count(),
self.columnsWidth, line):
if _print == self.body_printer:
align = "<"
offset = " "
if width:
width -= 1
else:
align = "^"
offset = ""
_print((u"%s{0:%s%d}" % (offset, align, width)).format(txt))
if i < last_col:
self.line_printer(char)
self.line_printer(char[:-self.colSpan + 1])
self.default_printer('\n')
if printRows:
self.printFunc("(%s %s)" % (len(self.dataList), _("rows")))
class MultiReplace:
"""MultiReplace function object
Usage:
replacer = MultiReplace({'str':'efg','in':'asd'})
s = replacer("string")
"""
def __init__(self, repl_dict):
# string to string mapping; use a regular expression
keys = repl_dict.keys()
keys.sort(reverse=True) # lexical order
pattern = u"|".join([re.escape(key) for key in keys])
self.pattern = re.compile(pattern)
self.dict = repl_dict
def replace(self, s):
# apply replacement dictionary to string
def repl(match, get=self.dict.get):
item = match.group(0)
return get(item, item)
return self.pattern.sub(repl, s)
__call__ = replace
def str2dict(s):
"""Convert string to dictionary:
String format:
{'key1':'value1','key2':'val\'ue2'}
"""
value = r'(?:\\\\|\\\'|[^\'])'
pair = r"""
\s*' # begin key
(%(v)s*)
' # end key
\s*:\s* # delimeter key/value
' # begin value
(%(v)s*)
'\s* # end value
""" % {"v": value}
reDict = re.compile(pair, re.X)
reMatchDict = re.compile("""
^{ # begin dict
((%(v)s,)* # many pair with comma at end
%(v)s)? # pair without comma
}$ # end dict
""" % {'v': pair}, re.X)
if reMatchDict.match(s.strip()):
d = dict(reDict.findall(s))
replaceSlash = MultiReplace({'\\\\': '\\', '\\\'': '\''})
for i in d.keys():
d[i] = replaceSlash(d[i])
return d
else:
cl_overriding.printERROR(_("wrong dict value: %s" % s))
cl_overriding.exit(1)
def str2list(s):
"""Convert string to list:
String format:
['value1','val\'ue2']
"""
value = r'(?:\\\\|\\\'|[^\'])'
element = r"""
\s*' # begin value
(%(v)s*)
'\s* # end value
""" % {"v": value}
reList = re.compile(element, re.X)
reMatchList = re.compile("""
^\[ # begin dict
((%(v)s,)* # many elements with comma at end
%(v)s)? # element without comma
\]$ # end dict
""" % {'v': element}, re.X)
if reMatchList.match(s.strip()):
replaceSlash = MultiReplace({'\\\\': '\\', '\\\'': '\''})
return [replaceSlash(i) for i in reList.findall(s)]
else:
cl_overriding.printERROR(_("wrong list value: %s" % s))
cl_overriding.exit(1)
def list2str(list):
"""Convert list to string
Return string with escaped \ and '.
"""
replaceSlash = MultiReplace({'\\': '\\\\', '\'': '\\\''})
return "[%s]" % ','.join(["'%s'" % replaceSlash(str(i))
for i in list])
def dict2str(dict):
"""Convert dictionry to string
Return string with escaped \ and '.
"""
replaceSlash = MultiReplace({'\\': '\\\\', '\'': '\\\''})
return '{%s}' % ','.join(["'%s':'%s'" % (str(k), replaceSlash(str(v))) \
for (k, v) in dict.items()])
def convertStrListDict(val):
"""Convert data between string, list, dict"""
# if val is list
if isinstance(val, types.ListType):
return list2str(val)
# if val is dictionary
elif isinstance(val, types.DictType):
return dict2str(val)
# else it is string
else:
# detect dictionary
if re.match("^{.*}$", val):
return str2dict(val)
# detect list
elif re.match("^\[.*\]$", val):
return str2list(val)
# else is simple string
else:
return val
def formatListOr(lst):
"""Convert list to string like this: [1,2,3] -> 1,2 or 3"""
lststr = map(lambda x: str(x), lst)
return (" %s " % _("or")).join(filter(lambda x: x,
[", ".join(map(lambda x: x or "''",
lststr[:-1])),
"".join(lststr[-1:])]))
def get_term_size(fd=sys.stdout.fileno()):
"""
Get terminal size
"""
try:
import struct
import fcntl
import termios
except ImportError:
struct, fcntl, termios = None, None, None
return None, None
try:
s = struct.pack("HHHH", 0, 0, 0, 0)
data = fcntl.ioctl(fd, termios.TIOCGWINSZ, s)
height, width = struct.unpack('4H', data)[:2]
return height, width
except Exception:
return None, None
def simplify_profiles(profiles, drop_words=("amd64", "x86")):
"""
Упростить названия профилей (удалить одинаковое начало и конец в строках)
:param profiles: список названий профилей
:param drop_words: список слов из профилей, которые можно отбросить
если профиль один
:return: список упрощенных названий профилей
"""
if len(profiles) < 2:
if len(profiles) == 1:
return ["/".join(filter(lambda x: x not in drop_words,
profiles[0].split('/')))]
return profiles
matrix = [x.split('/') for x in profiles]
# удаляем одинаковые начальные и конечные поля
matrix = zip(*reversed(list(dropwhile(lambda x: x.count(x[0]) == len(x),
(reversed(list(dropwhile(
lambda x: x.count(x[0]) == len(x),
izip_longest(*matrix,
fillvalue="")))))))))
return ["/".join(filter(None, x)) for x in matrix]

@ -1,649 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2014-2016 Mir Calculate. 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.
from itertools import tee
from functools import total_ordering, wraps
import warnings
from contextlib import contextmanager
import re
import sys
from types import StringTypes
import os
import fcntl
from os import path
import time
from abc import ABCMeta, abstractmethod
from types import GeneratorType
from math import sqrt
try:
import json as json_module
except ImportError:
json_module = None
class SavableIterator:
"""
Итератор с возможность сохранять и восстанавливать состояние
"""
def __init__(self, seq):
self.seq = iter(seq)
self.back = None
def __iter__(self):
return self
def save(self):
self.seq, self.back = tee(self.seq)
return self
def restore(self):
if self.back:
self.seq, self.back = tee(self.back)
return self
def next(self):
return self.seq.next()
@contextmanager
def ignore(exception):
try:
yield
except exception:
pass
class AddonError(Exception):
"""
Исключение с добавочным сообщением
"""
def __init__(self, msg, addon=None):
self.message = msg
self.addon = addon
Exception.__init__(self, msg)
class FalseObject(object):
"""
Объект-заглушка при любом сравнении возвращает False
"""
def __lt__(self, other):
return False
def __nonzero__(self):
return False
__gt__ = __ge__ = __le__ = __eq__ = __ne__ = __lt__
class Sizes(object):
K = KiB = kibibyte = 1024
kB = kilobyte = 1000
M = MiB = mibibyte = K * 1024
Mb = megabyte = kB * 1000
G = GiB = gibibyte = M * 1024
Gb = gigabyte = Mb * 1000
T = TiB = tibibyte = G * 1024
Tb = terabyte = Gb * 1000
Sector = 512
def __getattr__(self, name):
if name.startswith('from_'):
return lambda x: x * getattr(Sizes, name[5:])
elif name.startswith('to_'):
return lambda x: x / getattr(Sizes, name[3:])
else:
raise AttributeError
def _from(self, name, count):
return count * getattr(Sizes, name)
def _to(self, name, count):
return count / getattr(Sizes, name)
def imap_regexp(re_compiled, l, whole=False):
"""
Обработать список регулярным выражением и вернуть полученные группы
"""
if whole:
retfunc = lambda x: x.group()
else:
retfunc = lambda x: x.groups()
return (retfunc(x) for x in (re_compiled.search(x) for x in l) if x)
def cached(each_instance=False):
"""
Кэширование результата
"""
def cache_wrapper(func):
value = {}
def wrapper(*args, **kwargs):
if each_instance:
if args[0] not in value:
value[args[0]] = func(*args, **kwargs)
return value[args[0]]
else:
if None not in value:
value[None] = func(*args, **kwargs)
return value[None]
return wrapper
if each_instance in (True, False):
return cache_wrapper
else:
return cache_wrapper(each_instance)
class Singleton(type):
"""
Существует только один экземпляр данного класса
"""
_instances = {}
def __call__(cls, *args, **kw):
if cls not in cls._instances:
create_obj = getattr(super(Singleton, cls), '__call__')
cls._instances[cls] = create_obj(*args, **kw)
return cls._instances[cls]
class SingletonParam(type):
"""
Параметризированный Singleton: может быть создано множество объектов одного
и того же класса, но только если у них разные параметры инициализации
"""
def __init__(cls, name, bases, dicts):
super(SingletonParam, cls).__init__(name, bases, dicts)
cls.instance = {}
def __call__(cls, *args, **kw):
if hasattr(cls, "param_id"):
keyarg = cls.param_id(*args, **kw)
else:
keyarg = args[0] if args else ""
if keyarg not in cls.instance:
create_obj = getattr(super(SingletonParam, cls), '__call__')
cls.instance[keyarg] = create_obj(*args, **kw)
return cls.instance[keyarg]
def classificate(iterator):
"""
Классифицировать элементы по признаку (first первый last последний)
"""
class Mark:
def __init__(self, first=False, last=False):
self.first = first
self.last = last
def __repr__(self):
return "Mark(first=%s,last=%s)" % (self.first, self.last)
iterator = iter(iterator)
obj = iterator.next()
try:
obj_next = iterator.next()
yield Mark(first=True, last=False), obj
try:
while True:
obj = obj_next
obj_next = iterator.next()
yield Mark(), obj
except StopIteration:
yield Mark(first=False, last=True), obj
except StopIteration:
yield Mark(first=True, last=True), obj
def debug(prefix="DEBUG {name}({args},{kw})={ret}"):
"""
Декоратор для вывода отладочных сообщений при вызове функции
:param prefix:
:return:
"""
def debug_decor(f):
@wraps(f)
def wrapper(*args, **kw):
ret = f(*args, **kw)
print prefix.format(name=f.func_name, args=args, kw=kw, ret=ret)
return ret
return wrapper
return debug_decor
class ClassPropertyDescriptor(object):
def __init__(self, fget, fset=None):
self.fget = fget
self.fset = fset
def __get__(self, obj, klass=None):
if klass is None:
klass = type(obj)
return self.fget.__get__(obj, klass)()
def __set__(self, obj, value):
if not self.fset:
raise AttributeError("can't set attribute")
type_ = type(obj)
return self.fset.__get__(obj, type_)(value)
def setter(self, func):
if not isinstance(func, (classmethod, staticmethod)):
func = classmethod(func)
self.fset = func
return self
def classproperty(func):
"""
Классовое свойство
:param func:
:return:
"""
if not isinstance(func, (classmethod, staticmethod)):
func = classmethod(func)
return ClassPropertyDescriptor(func)
class LockError(Exception):
pass
class Locker(object):
locker_directory = '/run/calculate/locker'
def __init__(self, name=None, fn=None, timeout=None):
if name is not None:
if not path.exists(self.locker_directory):
os.makedirs(self.locker_directory)
self.fn = path.join(self.locker_directory, name)
else:
dn = path.dirname(fn)
if not path.exists(dn):
os.makedirs(dn)
self.fn = fn
self.lockf = None
if timeout is None:
self.timeout = 10
else:
self.timeout = 0
def lockfile(self, fn, timeout=None):
"""
Блокировка файла с таймаутом
"""
if timeout is None:
timeout = self.timeout
for i in range(0, timeout * 40 or 1):
try:
self.lockf = os.open(fn, os.O_CREAT | os.O_RDWR)
fcntl.flock(self.lockf, fcntl.LOCK_EX | fcntl.LOCK_NB)
return True
except IOError, e:
if e.errno != os.errno.EAGAIN:
raise e
os.close(self.lockf)
self.lockf = None
except OSError, e:
if e.errno != os.errno.EEXIST:
raise e
time.sleep(0.25)
else:
raise LockError("Lock timeout of %s" % fn)
def acquire(self):
if self.lockf is None:
self.lockfile(self.fn)
return True
def release(self):
if self.lockf is not None:
os.close(self.lockf)
self.lockf = None
return True
def remove(self):
self.release()
if path.exists(self.fn):
try:
os.remove(self.fn)
except (OSError, IOError):
pass
def __enter__(self):
""" Activated when used in the with statement.
Should automatically acquire a lock to be used in the with block.
"""
self.acquire()
return self
def __exit__(self, type, value, traceback):
""" Activated at the end of the with statement.
It automatically releases the lock if it isn't locked.
"""
self.release()
def __del__(self):
""" Make sure that the FileLock instance doesn't leave a lockfile
lying around.
"""
self.release()
class Signal(object):
def __init__(self):
self.subscribers = []
def connect(self, subscriber, unique=True):
if not unique or subscriber not in self.subscribers:
self.subscribers.append(subscriber)
def emit(self, *args, **kwargs):
for subscriber in self.subscribers:
subscriber(*args, **kwargs)
def disconnect(self, subscriber, removeall=False):
while True:
if subscriber in self.subscribers:
self.subscribers.remove(subscriber)
if removeall:
continue
break
def max_default(iterable, key=lambda x: x, default=None):
try:
return max(iterable, key=key)
except ValueError:
return default
def traverse(o, tree_types=(list, tuple, GeneratorType)):
"""
Вернуть последовательно все элемены списка, "распаковов" встроенные
[1, 2, [1,2,[4,6,7]], 7] -> (1,2,1,2,4,6,7,7)
:param o:
:param tree_types: типы, которые будут распакованы
:return:
"""
if isinstance(o, tree_types):
for value in o:
for subvalue in traverse(value, tree_types):
yield subvalue
else:
yield o
@total_ordering
class ReverseKey(object):
"""
Объект для создания ключей обратной сортировки
s = [("A","B"), ("A","C"), ("B","A")]
sorted(s, key=lambda x: (x[0], ReverseKey(x[1]))
"""
__slots__ = ['data']
def __init__(self, data):
self.data = data
def __eq__(self, o):
return o.data == self.data
def __lt__(self, o):
return o.data < self.data
def unique(iterable):
"""
Исключить из списка дублирующие элементы, не меняя порядка
"""
seen = {}
for x in iterable:
if x in seen:
continue
seen[x] = 1
yield x
def sorteduniqresult(f):
@wraps(f)
def wrapper(*args, **kw):
for x in sorted(set(f(*args, **kw))):
yield x
return wrapper
def repeater(*args):
"""
Циклы для повторяющихся действий с ожидаением
:param args:
:return:
"""
yield 0
for i, x in enumerate(args):
if x:
time.sleep(x)
yield i+1
def iterate_list(l):
"""
Перебрать последовательность (исключая строки) поэлемено,
или же возвращает последовательность из одно элемента
l1 = 1
l2 = [1,2]
for x in iterate_list(l1):
print x
:param l: Список, кортеж, или прочее
:return:
"""
if not isinstance(l, StringTypes):
for x in l:
yield x
else:
yield l
def has_any(a, b):
"""
Содержат ли два списка хотя бы один одинаковый элемент
:param a:
:param b:
:return:
"""
return any(x in a for x in b)
def deprecated(func):
"""This is a decorator which can be used to mark functions
as deprecated. It will result in a warning being emmitted
when the function is used."""
@wraps(func)
def new_func(*args, **kwargs):
warnings.simplefilter('always', DeprecationWarning) #turn off filter
warnings.warn("Call to deprecated function {}.".format(func.__name__), category=DeprecationWarning, stacklevel=2)
warnings.simplefilter('default', DeprecationWarning) #reset filter
return func(*args, **kwargs)
return new_func
def unpack_single_opts(args):
"""
Преобразовать одиночные аргументы
"""
re_single = re.compile("^-[A-Za-z0-9]")
re_right = re.compile("^-[A-Za-z0-9]+$")
for arg in args:
if re_single.search(arg):
if not re_right.match(arg):
sys.stderr.write("Wrong argument %s" % arg)
for letter in arg[1:]:
yield "-%s" % letter
else:
yield arg
drop_slash = lambda x: x[1:] if x.startswith('/') else x
class Cachable(object):
"""
Объект содержащий кэширующие методы
"""
def __init__(self):
self.clear_method_cache()
def clear_method_cache(self):
self._method_cache = {}
@staticmethod
def methodcached(key=lambda *args, **kw: hash(args)):
"""
Кэширование результата по параметрам метода, по ключевой функции
можно выбрать какие параметры играют роль
"""
def decorator(f):
funcname = f.func_name
@wraps(f)
def wrapper(self, *args, **kw):
keyval = key(*args, **kw)
assert isinstance(self, Cachable)
if funcname not in self._method_cache:
self._method_cache[funcname] = {}
cache = self._method_cache[funcname]
if keyval not in cache:
cache[keyval] = f(self, *args, **kw)
return cache[keyval]
@wraps(f)
def null_wrapper(self):
assert isinstance(self, Cachable)
if funcname not in self._method_cache:
self._method_cache[funcname] = f(self)
return self._method_cache[funcname]
if len(f.func_code.co_varnames) > 1:
return wrapper
else:
return null_wrapper
return decorator
def firstelement(i, fallback=''):
for x in i:
return x
return fallback
class GenericFs(object):
"""
Объект обобщения файловых систем
"""
__metaclass__ = ABCMeta
@abstractmethod
def exists(self, fn):
pass
@abstractmethod
def read(self, fn):
pass
@abstractmethod
def glob(self, fn):
pass
@abstractmethod
def realpath(self, fn):
pass
@abstractmethod
def write(self, fn, data):
pass
@abstractmethod
def listdir(self, dn, fullpath=False):
pass
class Brokenable(object):
@property
def broken(self):
return False
@staticmethod
def broken_result(res=None):
def decor(f):
@wraps(f)
def wrapper(self, *args, **kw):
if self.broken:
return res
return f(self, *args, **kw)
return wrapper
return decor
def get_best_nearest_resolution(preferred_res, support_res, aspect_only=False):
"""
Получить наилучшее ближайшее к preferred_res разрешенение из support_res.
Разрешение берётся ближайшее по размеру диагонали, с применением "штрафного"
коэфициента по разнице аспектов
"""
width, height = map(int, preferred_res.split('x'))
gep = sqrt(height ** 2 + width ** 2)
k = float(width) / float(height)
s = float(width) * float(height)
support_res_int = (tuple(map(int, x.split("x"))) for x in support_res)
support_res_int = ((x, y, sqrt(y*y + x*x), abs(x/float(y)-k))
for x, y in support_res_int)
if not aspect_only:
support_res_int = [(x,y,g,dasp)
for x,y,g,dasp in support_res_int if x <= width and y <= height]
if not support_res_int:
return None
keyfunc = lambda x,y,g, dasp: g -g * dasp
bestRes = max(support_res_int, key=lambda x:keyfunc(*x) )
return "%sx%s" % bestRes[0:2]
def get_traceback_caller(e1, e2, frame):
"""
Получить модуль и строку вызвавшую эту функции с ошибкой
"""
while frame.tb_next:
frame = frame.tb_next
module, part = os.path.split(frame.tb_frame.f_back.f_code.co_filename)
if part.endswith('.py'):
part = part[:-3]
fallbackmod = part
modname = [part]
while module and module != '/' and not module.endswith('site-packages'):
module, part = os.path.split(module)
modname.insert(0, part)
if module.endswith('site-packages'):
modname = ".".join(modname)
else:
modname = fallbackmod
return modname, frame.tb_frame.f_back.f_lineno

@ -1,541 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Mir Calculate. 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 os
from calculate.lib.utils.files import readFile
from collections import Mapping, deque
from os import path
import json
from functools import wraps
import fnmatch
import tools
class VFSError(Exception):
pass
class VFSErrorNotFound(VFSError):
pass
class VFSErrorExists(VFSError):
pass
class VFSErrorBadsymlink(VFSError):
pass
class VFSErrorCycliclink(VFSErrorBadsymlink):
pass
class VFSErrorNotImplemented(VFSError):
pass
class VFSErrorRestoreFailed(VFSError):
pass
class VFS(tools.GenericFs):
"""
Виртуальная файловая система
"""
def __init__(self, vfs=None):
if isinstance(vfs, VFS):
self.root = vfs.root
else:
self.root = VFSRoot()
def get(self, fn):
r = self.root
for dn in (x for x in fn.split('/') if x):
r = r[dn]
return r
def walk(self, sort=False):
deq = deque((self.root,))
if sort:
sortfunc = lambda z: sorted(z, key=lambda y: y.name, reverse=True)
else:
sortfunc = iter
while deq:
d = deq.pop()
if isinstance(d, VFSDirectory):
for x in sortfunc(d):
deq.append(x)
yield d
def read(self, fn):
return self.get(fn).read()
def write(self, fn, data):
return self.get(fn).write(data)
def listdir(self, fn, fullpath=False):
return self.get(fn).listdir(fullpath=fullpath)
def lexists(self, fn):
try:
self.get(fn)
return True
except VFSErrorNotFound:
return False
def dereflink(self, obj):
objs = [obj]
while isinstance(obj, VFSLink):
obj = obj.deref()
if obj in objs:
raise VFSErrorCycliclink(obj.fullname())
return obj
def _glob(self, pathname):
paths = deque(x for x in pathname.split("/") if x)
pat = paths.popleft()
SEPARATOR = (None, None)
deq = deque([("/", self.root), SEPARATOR])
while deq:
dirname, d = deq.popleft()
if d is None:
if paths:
pat = paths.popleft()
deq.append(SEPARATOR)
continue
else:
break
if d.isdir():
dirname = path.join(dirname, d.name)
for x in d:
if fnmatch.fnmatch(x.name, pat):
if not paths:
yield (path.join(dirname, x.name), x)
else:
deq.append((dirname, x))
def glob(self, pathname):
return (x for x, y in self._glob(pathname))
def realglob(self, pathname):
return [y.fullname() for x, y in self._glob(pathname)]
def exists(self, fn):
try:
obj = self.get(fn)
if isinstance(obj, VFSLink):
obj = self.dereflink(obj)
return True
except (VFSErrorNotFound, VFSErrorBadsymlink):
return False
def realpath(self, fn):
bestpath = fn
obj = None
try:
obj = self.get(fn)
if isinstance(obj, VFSLink):
obj = self.dereflink(obj)
return obj.fullname()
except VFSErrorBadsymlink:
if isinstance(obj, VFSLink):
bestpath = obj.symlink()
except VFSErrorNotFound:
pass
return path.normpath(bestpath)
# serialization
def dump(self):
return self.root.dump()
def restore(self, dumpdata):
self.root = self.root.restore(dumpdata)
class SafeVFS(VFS):
"""
Виртуатльная файловая система, не возвращающая ошибок при обращении к
отсутстующему файлу
"""
def exists(self, fn):
return self.lexists(fn)
def read(self, fn):
try:
return super(SafeVFS, self).read(fn)
except VFSError:
return ""
def listdir(self, fn, fullpath=False):
try:
return super(SafeVFS, self).listdir(fn, fullpath)
except VFSError:
return []
class VFSObject(object):
def __init__(self, name):
self.parent = None
self.name = name
def dump(self):
raise NotImplemented()
def fullname(self):
if self.parent:
dirname = self.parent.fullname()
dirname = "" if dirname == "/" else dirname
return "%s/%s" % (dirname, self.name)
else:
return self.name
def read(self):
raise NotImplemented()
def write(self, data):
raise NotImplemented()
def listdir(self, fullpath=False):
raise NotImplemented()
def isdir(self):
return False
@classmethod
def restore(cls, data):
for clsobj in (VFSFile, VFSLink, VFSDirectory, VFSRoot):
obj = clsobj.restore(data)
if obj is not None:
return obj
raise VFSErrorRestoreFailed(data)
class VFSDirectory(VFSObject, Mapping):
def __init__(self, name, *args):
super(VFSDirectory, self).__init__(name)
self.children = {}
for o in args:
self.append(o)
def dump(self):
return ['d', self.name, [x.dump() for x in self]]
def isdir(self):
return True
@classmethod
def restore(cls, data):
if data[0] == "d":
objtype, name, objdata = data
d = cls(name)
for x in objdata:
d.append(VFSObject.restore(x))
return d
def __contains__(self, item):
return item.name in self.children
def append(self, obj):
if obj in self:
raise VFSErrorExists("{dirn}/{name}".format(name=obj.name,
dirn=self.fullname()))
self.children[obj.name] = obj
obj.parent = self
def __iter__(self):
return iter(self.children.values())
def __len__(self):
return len(self.children)
def __getitem__(self, item):
if item == ".":
return self
if item == "..":
return self.parent
try:
return self.children[item]
except KeyError:
raise VFSErrorNotFound(item)
def listdir(self, fullpath=False):
if fullpath:
dirname = self.fullname()
else:
dirname = ""
return [path.join(dirname, x.name) for x in self.children.values()]
def read(self):
raise VFSErrorNotImplemented("Directory does not support reading")
def write(self, data):
raise VFSErrorNotImplemented("Directory does not support writing")
def __eq__(self, obj):
return self is obj
def __ne__(self, obj):
return self is not obj
class VFSRoot(VFSDirectory):
def __init__(self, *args):
super(VFSRoot, self).__init__("/", *args)
def __getitem__(self, item):
if item == "..":
return self
return super(VFSRoot, self).__getitem__(item)
def dump(self):
return ['/', self.name, [x.dump() for x in self]]
@classmethod
def restore(cls, data):
if data[0] == "/":
objtype, name, objdata = data
d = cls()
for x in objdata:
d.append(VFSObject.restore(x))
return d
class VFSFile(VFSObject):
def __init__(self, name, data=""):
super(VFSFile, self).__init__(name)
self.data = data
def dump(self):
return ['r', self.name, self.read()]
@classmethod
def restore(cls, data):
if data[0] == "r":
objtype, name, objdata = data
return cls(name, objdata)
def read(self):
return self.data
def write(self, data):
self.data = data
def listdir(self, fullpath=False):
raise VFSErrorNotImplemented("File does not support listing")
def __getitem__(self, item):
raise VFSErrorNotImplemented("File does not support listing")
def detect_cyclic(f):
@wraps(f)
def wrapper(self, *args, **kw):
if self.deref_start:
raise VFSErrorCycliclink(self.fullname())
self.deref_start = True
try:
return f(self, *args, **kw)
finally:
self.deref_start = False
return wrapper
class VFSLink(VFSObject):
def __init__(self, name, target):
super(VFSLink, self).__init__(name)
self.name = name
self._target = target
self.deref_start = False
@classmethod
def restore(cls, data):
if data[0] == "l":
objtype, name, objdata = data
return cls(name, objdata)
def dump(self):
return ['l', self.name, self.symlink()]
def symlink(self):
return self._target
def get_root(self):
r = self.parent
while r.parent:
r = r.parent
return r
def deref(self):
r = self.parent
if r is None:
raise VFSErrorBadsymlink(self._target)
if self._target.startswith("/"):
r = self.get_root()
for dn in (x for x in self._target.split('/') if x):
try:
r = r[dn]
except VFSErrorNotFound:
raise VFSErrorBadsymlink(self._target)
return r
@detect_cyclic
def _isdir(self):
return self.deref().isdir()
def isdir(self):
try:
return self._isdir()
except VFSErrorBadsymlink:
return False
def __iter__(self):
if self.isdir():
return iter(self.deref())
else:
raise VFSErrorNotImplemented("Symlink is not directory")
@detect_cyclic
def read(self):
return self.deref().read()
@detect_cyclic
def write(self, data):
return self.deref().write(data)
@detect_cyclic
def listdir(self, fullpath=False):
return self.deref().listdir()
@detect_cyclic
def __getitem__(self, item):
return self.deref().__getitem__(item)
class VFSImporter(object):
"""
Имортёр файловой системы
"""
def __init__(self, dn, prefix="/"):
self.importdn = dn
self.prefix = prefix
def importfs(self):
vfs = VFS()
dn = self.importdn
r = vfs.root
for dns in (x for x in path.relpath(dn, self.prefix).split('/') if x):
d = VFSDirectory(dns)
r.append(d)
r = d
for root, dnames, fnames in os.walk(dn):
vroot = path.join("/", path.relpath(root, self.prefix))
r = vfs.get(vroot)
remove_dirs = deque()
remove_files = deque()
for dn in dnames:
fdn = path.join(root, dn)
if path.islink(fdn):
self.import_link(r, dn, fdn)
else:
self.import_directory(r, dn, fdn)
if not self.filter_dirs(root, dn):
remove_dirs.append(dn)
for fn in fnames:
ffn = path.join(root, fn)
if path.islink(ffn):
self.import_link(r, fn, ffn)
else:
self.import_file(r, fn, ffn)
if not self.filter_dirs(root, dn):
remove_dirs.append(dn)
while remove_dirs:
dnames.remove(remove_dirs.popleft())
while remove_files:
fnames.remove(remove_files.popleft())
return vfs
def filter_dirs(self, root, dn):
return True
def filter_files(self, root, fn):
return True
def import_link(self, dirobj, fn, ffn):
target = os.readlink(ffn)
dirobj.append(VFSLink(fn, target))
def import_file(self, dirobj, fn, ffn):
dirobj.append(VFSFile(fn))
def import_directory(self, dirobj, dn, fdn):
dirobj.append(VFSDirectory(dn))
class VFSDevfsImporter(VFSImporter):
"""
Импортер /dev
"""
def __init__(self, dn="/dev", prefix="/"):
super(VFSDevfsImporter, self).__init__(dn, prefix)
def filter_dirs(self, root, dn):
if dn == "shm":
return False
return True
class VFSSysfsImporter(VFSImporter):
"""
Импортёр /sys
"""
def __init__(self, dn="/sys", prefix="/"):
super(VFSSysfsImporter, self).__init__(dn, prefix)
def import_file(self, dirobj, fn, ffn):
if fn in ("version", "vendor", "model", "board_name",
"board_vendor", "refcnt", "boardname", "size",
"removable",
"type", "operstate"):
dirobj.append(VFSFile(fn, readFile(ffn)))
else:
dirobj.append(VFSFile(fn))
class VFSJsonKeeper(object):
"""
Сохранение и загрузыка виртуальной файловой системы
"""
@staticmethod
def save(f, vfs):
json.dump(vfs.dump(), f)
@staticmethod
def load(f):
vfs = VFS()
vfs.restore(json.load(f))
return vfs
VFSKeeper = VFSJsonKeeper

@ -1,44 +0,0 @@
#-*- coding: utf-8 -*-
# Copyright 2011-2016 Mir Calculate. 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
from calculate.lib.datavars import ReadonlyVariable
import X11
import locale
import env
import hardware
import linux
import net
import user
import system
import action
from calculate.lib.cl_lang import setLocalTranslate
setLocalTranslate('cl_lib3',sys.modules[__name__])
section = "main"
class VariableClName(ReadonlyVariable):
"""
Package name
"""
value = "calculate-core"
class VariableClVer(ReadonlyVariable):
"""
Package version
"""
value = "3.6.7"

@ -1,354 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2008-2016 Mir Calculate. 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
from os import path
import re
import platform
from calculate.lib.datavars import (Variable, SimpleDataVars, ReadonlyVariable,
VariableError, VariableInterface)
from calculate.lib.utils.portage import (isPkgInstalled, searchProfile,
RepositorySubstituting, RepositoryPath)
from calculate.lib.utils.files import readFile, pathJoin
from calculate.lib.configparser import ConfigParser
from calculate.lib.utils.tools import unique, drop_slash
import env
_ = lambda x: x
from calculate.lib.cl_lang import setLocalTranslate
setLocalTranslate('cl_lib3', sys.modules[__name__])
class Linux(VariableInterface):
dictLinuxName = {"CLD": "Calculate Linux Desktop",
"CLDX": "Calculate Linux Desktop",
"CLDG": "Calculate Linux Desktop",
"CDS": "Calculate Directory Server",
"CLS": "Calculate Linux Scratch",
"CSS": "Calculate Scratch Server",
"CMC": "Calculate Media Center",
"Gentoo": "Gentoo"}
dictLinuxSubName = {"CLD": "KDE", "CLDX": "XFCE", "CLDG": "GNOME"}
dictNameSystem = {'CDS': 'server',
'CLD': 'desktop',
'CLDG': 'desktop',
'CLDX': 'desktop',
'CLS': 'desktop',
'CMC': 'desktop',
'CSS': 'server'}
def getFullNameByShort(self, shortname):
"""Get full distributive name by short name"""
subname = self.dictLinuxSubName.get(shortname, '')
if subname:
subname = " %s" % subname
return "%s%s" % (self.dictLinuxName.get(shortname, ''), subname)
def getShortnameByMakeprofile(self, systemroot):
"""Get shortname by symlink of make.profile"""
makeprofile = self.Get('cl_make_profile')
if path.exists(makeprofile):
link = os.readlink(makeprofile)
re_make_profile_link = re.compile(
'/calculate/(desktop|server)/(%s)/' %
"|".join(self.dictLinuxName.keys()), re.S)
shortname_search = re_make_profile_link.search(link)
if shortname_search:
return shortname_search.groups()[1]
return None
def getShortnameByIni(self, systemroot):
"""Get shortname by calculate.ini file"""
inifile = path.join(systemroot, 'etc/calculate/calculate.ini')
if path.exists(inifile):
with open(inifile) as f:
data = f.readlins()
short_name_list = filter(
None, map(lambda x: (len(x.split("=")) == 2 and
x.split("=")[0] == "calculate" and
x.split("=")[1].strip()), data))
if short_name_list:
return short_name_list[0]
def detectOtherShortname(self, systemroot):
"""Detect other system. Now only Gentoo."""
gentoo_file = path.join(systemroot, "etc/gentoo-release")
if path.exists(gentoo_file):
return "Gentoo"
if all(map(lambda x: path.lexists(path.join(systemroot, x)),
['bin', 'var', 'lib', 'etc'])):
return "Linux"
return None
def getVersionFromOverlay(self, systemroot):
"""
Get version from overlay calculate-release
"""
overlay_path = drop_slash(RepositoryPath.Calculate)
release_file = 'profiles/calculate-release'
return readFile(path.join(systemroot, overlay_path, release_file))
def getVersionFromMetapackage(self, systemroot, shortname):
"""Get version from meta package"""
for pkg in ("app-misc/calculate-meta", "app-misc/%s-meta" % shortname):
calc_meta = isPkgInstalled(pkg, systemroot)
if calc_meta:
return calc_meta[0]['PV']
return None
def getVersionFromCalculateIni(self, systemroot):
"""Get version from calculate ini"""
pathname = path.join(systemroot,
'etc/calculate/calculate.ini')
if path.exists(pathname):
with open(pathname) as f:
data = f.readlines()
ver_list = filter(
None, map(lambda x:
(len(x.split("=")) == 2 and
x.split("=")[0] == "linuxver" and
x.split("=")[1].strip()), data))
if ver_list:
re_ver = re.compile("^(\d+\.)*\d$", re.S)
re_res = filter(re_ver.search, ver_list)
if re_res:
return re_res[0]
def getVersionFromGentooFiles(self, systemroot):
"""Get version from gentoo files"""
gentoo_file = path.join(systemroot, "etc/gentoo-release")
re_ver = re.compile("^(\d+\.)*\d+$", re.S)
if path.exists(gentoo_file):
gentoo_link = self.Get('cl_make_profile')
if path.islink(gentoo_link):
vers = filter(re_ver.search,
os.readlink(gentoo_link).split('/'))
if vers:
return vers[-1]
def getVersionFromUname(self):
"""Get version from uname"""
re_ver = re.search("^(\d+\.)*\d", platform.release(), re.S)
if re_ver:
return re_ver.group()
class VariableOsLinuxShortname(Variable, Linux):
"""
Short system name (Example:CLD)
"""
systemRoot = "/"
def get(self):
return (self.getShortnameByMakeprofile(self.systemRoot) or
self.getShortnameByIni(self.systemRoot) or
self.detectOtherShortname(self.systemRoot) or
"Linux")
class VariableOsLinuxName(Variable, Linux):
"""
Full system name
"""
source_variable = "os_linux_shortname"
def get(self):
linux_short_name = self.Get(self.source_variable)
return self.dictLinuxName.get(linux_short_name, "Linux")
class VariableOsLinuxSubname(Variable, Linux):
"""
Subname of linux (KDE, GNOME, XFCE and etc)
"""
source_variable = "os_linux_shortname"
def get(self):
linux_short_name = self.Get(self.source_variable)
return self.dictLinuxSubName.get(linux_short_name, "")
class VariableOsLinuxSystem(Variable, Linux):
"""
System of linux (desktop or server)
"""
source_variable = "os_linux_shortname"
def get(self):
short_name = self.Get(self.source_variable)
return self.dictNameSystem.get(short_name, "")
class VariableOsLinuxVer(Variable, Linux):
"""
Version of system (get by metapackage,calculate.ini,gentoo files or 0)
"""
systemRoot = "/"
def get(self):
"""Get system version"""
linux_short_name = self.Get("os_linux_shortname")
return (self.getVersionFromOverlay(self.systemRoot) or
self.getVersionFromMetapackage(
self.systemRoot, linux_short_name) or
self.getVersionFromCalculateIni(self.systemRoot) or
self.getVersionFromGentooFiles(self.systemRoot) or
self.getVersionFromUname() or "0")
class VariableOsLinuxBuild(Variable, Linux):
"""
Build of system
"""
class VariableOsLinuxFilesnum(Variable, Linux):
systemRoot = "/"
def get(self):
"""Depricated variable which contains count files.
Get files count. Count files discard because it to long
operation. This value is already count by calculate-builder and
is placed in calculate3.env"""
return str(0)
# return str(countFiles(self.systemRoot))
class VariableOsLinuxFiles(Variable, Linux):
systemRoot = "/"
def get(self):
"""Get files count. Count files discard because it to long
operation. This value is already count by calculate-builder and
is placed in calculate.env"""
return self.Get('os_linux_filesnum')
# return str(countFiles(self.systemRoot))
class VariableOsLinuxPkglist(Variable):
type = "list"
def generate_shortnames(self, make_profile):
repos = RepositorySubstituting(self)
for fn in searchProfile(make_profile, "calculate.env",
repository_sub=repos):
config = ConfigParser(strict=False)
config.read(fn, encoding="utf-8")
value = SimpleDataVars.unserialize("string",
config.get('main',
'os_linux_shortname',
raw=True,
fallback=''))
yield value.encode('utf-8')
def get(self):
make_profile = self.Get('cl_make_profile')
if path.exists(make_profile):
return list(unique(filter(None,
self.generate_shortnames(make_profile))))
else:
return []
class VariableClProfileSystem(ReadonlyVariable):
"""
Профиль системы (симлинк /etc/make.profile')
"""
def get(self):
try:
make_profile = self.Get('cl_make_profile')
return path.normpath(
path.join(path.dirname(make_profile),
os.readlink(make_profile)))
except:
raise VariableError(_("Failed to determine the system profile"))
class VariableClProfileName(ReadonlyVariable):
source_var = "cl_profile_system"
def get(self):
make_profile = self.Get(self.source_var)
distro, o, name = make_profile.partition('/profiles/')
if not o:
return make_profile
distro = path.basename(distro)
return "%s:%s" % (distro, name)
class LinuxDataVars(SimpleDataVars):
class VariableOsArchMachine(ReadonlyVariable):
systemRoot = '/'
def get(self):
if path.exists(path.join(self.systemRoot, 'lib64')):
return 'x86_64'
else:
return 'i686'
def variables(self):
os_arch_machine = self.VariableOsArchMachine(systemRoot=self.systemRoot)
return [env.VariableClMakeProfile(systemRoot=self.systemRoot),
VariableClProfileName(systemRoot=self.systemRoot),
VariableClProfileSystem(systemRoot=self.systemRoot),
env.VariableClChrootPath(),
os_arch_machine,
VariableOsLinuxShortname(systemRoot=self.systemRoot),
VariableOsLinuxName(systemRoot=self.systemRoot),
VariableOsLinuxSubname(systemRoot=self.systemRoot),
VariableOsLinuxFilesnum(systemRoot=self.systemRoot),
env.VariableClEmergeConfig(systemRoot=self.systemRoot),
VariableOsLinuxFiles(systemRoot=self.systemRoot),
VariableOsLinuxSystem(systemRoot=self.systemRoot),
VariableOsLinuxVer(systemRoot=self.systemRoot),
VariableOsLinuxBuild(systemRoot=self.systemRoot)]
def __init__(self, systemRoot="/", cache=None):
self.systemRoot = systemRoot
SimpleDataVars.__init__(self, *self.variables())
self.init_variables()
if cache is None:
makeprofile = self.Get('cl_make_profile')
if os.path.exists(makeprofile):
inifile = path.join(systemRoot, os.path.dirname(makeprofile),
os.readlink(makeprofile))
self.flIniFileFrom(inifile, system_root=systemRoot)
self.flCalculateEnv(systemRoot)
else:
self.cache = cache
def flCalculateEnv(self, systemRoot):
env_data = dict(env._envData)
for inifile in (env_data['system'],
env_data['custom']):
self.flIniFileFrom(path.join(systemRoot, drop_slash(inifile)),
system_root=systemRoot)
def init_variables(self):
self['cl_chroot_path'] = self.systemRoot
return True
def change_root(self, dn, new_root='/'):
if dn.startswith(self.systemRoot):
return pathJoin(new_root, dn[len(self.systemRoot):])
return dn
Loading…
Cancel
Save