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

412 lines
15 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# -*- 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 contextlib import contextmanager
import sys
import os
import errno
from calculate.lib.cl_xml import xpath
import time
import fcntl
from utils.common import _error
from cl_template import templateFormat, TemplatesError
from calculate.lib.cl_lang import setLocalTranslate
_ = lambda x: x
setLocalTranslate('cl_lib3', sys.modules[__name__])
class iniParser(_error, templateFormat):
"""Класс для работы с ini файлами
"""
def __init__(self, iniFile=None, text=None):
# название ini файла
self.iniFile = iniFile or ""
# права создаваемого ini-файла
self.mode = 0640
# Cоответствует ли формат файла нужному
self.checkIni = None
self.FD = None
self.readOnly = False
self.text = text
self.locked = False
def joinText(self, iniObj, xmlNewDoc):
"""Объединяет два документа"""
newRootNode = xmlNewDoc.documentElement
newBodyNode = xpath.Evaluate('child::body', newRootNode)[0]
newImportBodyNode = iniObj.doc.importNode(newBodyNode, True)
iniObj.docObj.joinBody(iniObj.docObj.body, newImportBodyNode)
# iniObj.docObj.insertBRtoBody(iniObj.docObj.body)
def setMode(self, mode):
"""установка прав создаваемого ini-файла"""
self.mode = mode
def lockfile(self, fd, fn, timeout=5):
"""
Блокировка файла с таймаутом
"""
if self.locked:
return True
for i in range(0, timeout):
try:
fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
return True
except IOError, e:
if e.errno != errno.EAGAIN:
raise e
time.sleep(1)
else:
raise TemplatesError(_("Lock timeout of %s") % fn)
@property
def iniFile_lock(self):
return self.iniFile + ".lock~"
def wait_global_lock(self, timeout=10):
lockfn = self.iniFile_lock
for i in range(0, timeout):
if self.locked or not os.path.exists(lockfn):
break
time.sleep(1)
@contextmanager
def lock(self):
lockfn = self.iniFile_lock
lockf = open(lockfn, "w+")
self.lockfile(lockf.fileno(), lockfn)
self.locked = True
try:
yield
finally:
self.locked = False
os.unlink(lockfn)
def _open(self, mode):
self.wait_global_lock()
return open(self.iniFile, mode)
def openIniFile(self):
if not self.text is None:
return self.text
if not os.access(self.iniFile, os.R_OK):
return ""
self.FD = self._open("r")
self.lockfile(self.FD.fileno(), self.iniFile)
textIni = self.FD.read()
return textIni
def openRWIniFile(self):
if not os.access(self.iniFile, os.R_OK):
return ""
try:
self.FD = self._open("r+")
except (IOError, OSError):
self.FD = self._open("r")
self.readOnly = True
self.lockfile(self.FD.fileno(), self.iniFile)
textIni = self.FD.read()
return textIni
def writeIniFile(self, txtConfig):
if self.readOnly:
self.setError(_("Failed to write to file")
+ _(": ") + self.iniFile)
return False
if not os.path.exists(self.iniFile):
try:
# Создание файла
self.FD = self._open("w+")
self.lockfile(self.FD.fileno(), self.iniFile)
os.chmod(self.iniFile, self.mode)
except Exception:
self.setError(_("Failed to create the file") +
_(": ") + self.iniFile)
return False
if not self.FD:
self.setError(_("Failed to write to file")
+ _(": ") + self.iniFile)
return False
self.FD.truncate(0)
self.FD.seek(0)
self.FD.write(txtConfig)
self.FD.close()
self.FD = None
return True
def setVar(self, strHeader, dictVar):
"""Заменяет или добавляет область и переменные
Добавляет область в ini-файл или объединяет с существующей
strHeader - имя области
dictVar - словарь переменных
"""
textIni = self.openRWIniFile()
nameFomat = self.checkIniFile(textIni)
if not nameFomat:
return False
if type(strHeader) in (tuple, list):
# формат plasma
classObj = self.getClassObj("plasma")
else:
if nameFomat == "plasma":
self.setError(_("Trying to write a variable of 'samba' "
"format to file %s ('plasma' format)") \
% self.iniFile)
return False
# формат samba
classObj = self.getClassObj("samba")
# создаем объект
# и записываем в него содержимое ini-файла
objIni = classObj(textIni)
# создаем текст из строки заголовка и
# словаря переменных области
txtConfig = objIni.createTxtConfig(strHeader, dictVar)
# создаем объект и записываем в него текст
objIniAdd = classObj(txtConfig)
# объединяем объекты для получения результирующего текста
objIni.join(objIniAdd)
# получаем текст
txtConfig = objIni.getConfig().encode("UTF-8")
# записываем его в ini файл
if not self.writeIniFile(txtConfig):
return False
return True
def isEmptyFile(self, textIni):
"""Если файл пустой или содержит только комментарии - False
иначе - True
"""
if textIni.strip():
if filter(lambda x: x.strip(),
map(lambda x: x[0].split(";")[0],
map(lambda x: x.split("#"),
textIni.splitlines()))):
return False
else:
return True
else:
return True
def checkIniFile(self, textIni):
"""Проверка на правильность формата файла"""
if self.checkIni is None:
# Ошибка
if textIni is False:
self.checkIni = False
return False
self.checkIni = "samba"
# В файле есть данные
if not self.isEmptyFile(textIni):
try:
objIni = self.getClassObj("plasma")(textIni)
except Exception:
self.setError(_("Incorrect file format") + _(": ") + \
self.iniFile)
self.checkIni = False
return self.checkIni
allAreas = objIni.docObj.getAllAreas()
for xmlArea in allAreas:
parentNode = xmlArea.parentNode
if parentNode and parentNode.tagName == "area":
self.checkIni = "plasma"
break
if self.checkIni == "samba":
objIni = self.getClassObj("samba")(textIni)
xmlBody = objIni.docObj.getNodeBody()
if not xmlBody.firstChild:
self.checkIni = False
return self.checkIni
def delVar(self, strHeader, nameVar):
"""Удаляем переменную из ini файла"""
delNameVar = "!%s" % nameVar
dictVar = {delNameVar: "del"}
res = self.setVar(strHeader, dictVar)
return res
def delArea(self, strHeader):
"""Удаляем область из ini файла"""
if type(strHeader) in (tuple, list):
# Формат plasma
delStrHeader = list(strHeader[:])
delStrHeader[-1] = "!%s" % delStrHeader[-1]
else:
# Формат samba
delStrHeader = "!%s" % strHeader
dictVar = {"del": "del"}
res = self.setVar(delStrHeader, dictVar)
return res
def getVar(self, strHeader, nameVar, checkExistVar=False):
"""Получаем значение переменной из ini-файла"""
textIni = self.openIniFile()
if self.FD:
self.FD.close()
self.FD = None
nameFomat = self.checkIniFile(textIni)
if not nameFomat:
return False
formatPlasma = False
if type(strHeader) in (tuple, list):
# формат plasma
classObj = self.getClassObj("plasma")
formatPlasma = True
else:
if nameFomat == "plasma":
self.setError(_("Trying to fetch a variable of 'samba' "
"format from file %s ('plasma' format)") \
% self.iniFile)
return False
# формат samba
classObj = self.getClassObj("samba")
# создаем объект и записываем в него содержимое ini-файла
objIni = classObj(textIni)
# получаем ноду body
xmlBody = objIni.docObj.getNodeBody()
flagFound, xmlBody = self.getLastNode(objIni, xmlBody, strHeader,
formatPlasma)
if flagFound and xmlBody:
if formatPlasma:
strHeader = strHeader[-1]
# находим в области переменную
res = objIni.docObj.getAreaFieldValues(strHeader, nameVar, xmlBody)
else:
res = False
if checkExistVar:
if res is False:
return False, ""
else:
return True, res
else:
if res is False:
return ""
else:
return res
def getLastNode(self, objIni, xmlBody, strHeader, formatPlasma):
"""Ищет область в XML в которой область с переменными"""
flagFound = True
if not strHeader:
flagFound = False
return flagFound, xmlBody
lenStrHeader = len(strHeader)
if formatPlasma and lenStrHeader > 0:
xmlAreas = [xmlBody]
for i in xrange(lenStrHeader - 1):
flagFound = False
for xmlArea in xmlAreas:
xmlAreas = objIni.docObj.getArea(strHeader[i], xmlArea)
if xmlAreas:
flagFound = True
break
if xmlAreas:
xmlBody = xmlAreas[0]
return flagFound, xmlBody
def getAreaVars(self, strHeader):
"""Получаем все переменнные области из ini-файла"""
textIni = self.openIniFile()
if self.FD:
self.FD.close()
self.FD = None
nameFomat = self.checkIniFile(textIni)
if not nameFomat:
return False
formatPlasma = False
if type(strHeader) in (tuple, list):
# формат plasma
classObj = self.getClassObj("plasma")
formatPlasma = True
else:
if nameFomat == "plasma":
self.setError(_("Trying to fetch a variable of 'samba' "
"format from file %s ('plasma' format)") \
% self.iniFile)
return False
# формат samba
classObj = self.getClassObj("samba")
# создаем объект типа samba и записываем в него содержимое ini-файла
objIni = classObj(textIni)
# получаем ноду body
xmlBody = objIni.docObj.getNodeBody()
flagFound, xmlBody = self.getLastNode(objIni, xmlBody, strHeader,
formatPlasma)
if flagFound and xmlBody:
if formatPlasma:
strHeader = strHeader[-1]
# если находим область то выдаем словарем все переменные иначе False
res = objIni.docObj.getAreaFields(strHeader, xmlBody, allVars=True)
else:
res = False
if res is False:
return {}
else:
return res
def getAllSectionNames(self):
"""Получаем все имена секций определенных в ini файле
Если формат ini файла plasma то имя секции -
имена нескольких секций через запятую
"""
textIni = self.openIniFile()
if self.FD:
self.FD.close()
self.FD = None
nameFomat = self.checkIniFile(textIni)
if not nameFomat:
return False
if nameFomat == "samba":
# создаем объект типа samba и записываем в него содержимое ini-файла
objIni = self.getClassObj("samba")(textIni)
elif nameFomat == "plasma":
# создаем объект типа plasma и записываем в него содержимое
# ini-файла
objIni = self.getClassObj("plasma")(textIni)
else:
return []
xmlNodes = objIni.docObj.getAllAreas()
# Имена секций ini файла
namesSection = []
if nameFomat == "plasma":
for xmlNode in xmlNodes:
nSect = objIni.docObj.getNameArea(xmlNode)
if nSect:
namesSect = [nSect]
parentNode = xmlNode.parentNode
while parentNode != objIni.docObj.body:
nameSect = objIni.docObj.getNameArea(parentNode)
if nameSect:
namesSect.append(nameSect)
parentNode = parentNode.parentNode
else:
return []
namesSection.append(",".join(reversed(namesSect)))
elif nameFomat == "samba":
# получаем ноду body
for xmlNode in xmlNodes:
nSect = objIni.docObj.getNameArea(xmlNode)
if nSect:
namesSection.append(nSect)
return namesSection