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

421 lines
16 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 .cl_xml import xpath, firstChild
from copy import deepcopy
import time
import fcntl
from .utils.common import _error
from .cl_template import FormatFactory, TemplatesError
from .cl_lang import setLocalTranslate
_ = lambda x: x
setLocalTranslate('cl_lib3', sys.modules[__name__])
class iniParser(_error):
"""Класс для работы с ini файлами
"""
def __init__(self, iniFile=None, text=None):
# название ini файла
self.iniFile = iniFile or ""
# права создаваемого ini-файла
self.mode = 0o640
# Cоответствует ли формат файла нужному
self.checkIni = None
self.FD = None
self.readOnly = False
self.text = text
self.locked = False
self.formatFactory = FormatFactory(self)
def joinText(self, iniObj, xmlNewDoc):
"""Объединяет два документа"""
newRootNode = xmlNewDoc.getroottree().getroot()
newBodyNode = xpath.Evaluate('child::body', newRootNode)[0]
newImportBodyNode = deepcopy(newBodyNode)#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, readonly=False):
"""
Блокировка файла с таймаутом
"""
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 as e:
# dirty hack for lock ini.env on cifs
if e.errno == errno.EBADF and readonly:
return False
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, readonly=True)
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.formatFactory.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.formatFactory.getClassObj("samba")
# создаем объект
# и записываем в него содержимое ini-файла
objIni = classObj(textIni, self)
# создаем текст из строки заголовка и
# словаря переменных области
txtConfig = objIni.createTxtConfig(strHeader, dictVar)
# создаем объект и записываем в него текст
objIniAdd = classObj(txtConfig, self)
# объединяем объекты для получения результирующего текста
objIni.join(objIniAdd)
# получаем текст
txtConfig = objIni.getConfig()
# записываем его в ini файл
if not self.writeIniFile(txtConfig):
return False
return True
def isEmptyFile(self, textIni):
"""Если файл пустой или содержит только комментарии - False
иначе - True
"""
if textIni.strip():
if [x for x in [y[0].split(";")[0] for y in [z.split("#") for z
in textIni.splitlines()]] if x.strip()]:
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:
tmp = self.formatFactory.getClassObj(
"plasma")
# objIni = self.formatFactory.getClassObj(
# "plasma")(textIni, self)
objIni = tmp(textIni, self)
except Exception as e:
self.setError(_("Incorrect file format") + _(": ") + \
self.iniFile)
self.checkIni = False
return self.checkIni
allAreas = objIni.docObj.getAllAreas()
for xmlArea in allAreas:
parentNode = xmlArea.getparent()
if parentNode is not None and parentNode.tag == "area":
self.checkIni = "plasma"
break
if self.checkIni == "samba":
objIni = self.formatFactory.getClassObj(
"samba")(textIni, self)
xmlBody = objIni.docObj.getNodeBody()
if firstChild(xmlBody) is None:
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.formatFactory.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.formatFactory.getClassObj("samba")
# создаем объект и записываем в него содержимое ini-файла
objIni = classObj(textIni, self)
# получаем ноду body
xmlBody = objIni.docObj.getNodeBody()
flagFound, xmlBody = self.getLastNode(objIni, xmlBody, strHeader,
formatPlasma)
if flagFound and xmlBody is not None:
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 range(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.formatFactory.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.formatFactory.getClassObj("samba")
# создаем объект типа samba и записываем в него содержимое ini-файла
objIni = classObj(textIni, self)
# получаем ноду body
xmlBody = objIni.docObj.getNodeBody()
flagFound, xmlBody = self.getLastNode(objIni, xmlBody, strHeader,
formatPlasma)
if flagFound and xmlBody is not None:
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.formatFactory.getClassObj("samba")(textIni, self)
elif nameFomat == "plasma":
# создаем объект типа plasma и записываем в него содержимое
# ini-файла
objIni = self.formatFactory.getClassObj("plasma")(textIni, self)
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.getparent()
while parentNode != objIni.docObj.body:
nameSect = objIni.docObj.getNameArea(parentNode)
if nameSect:
namesSect.append(nameSect)
parentNode = parentNode.getparent()
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