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.
1694 lines
60 KiB
1694 lines
60 KiB
#-*- coding: utf-8 -*-
|
|
|
|
# Copyright 2008-2013 Calculate Ltd. http://www.calculate-linux.org
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import re
|
|
import sys
|
|
import importlib
|
|
from utils.text import convertStrListDict, _toUNICODE, formatListOr
|
|
from utils.files import pathJoin,readFile
|
|
from utils.portage import searchProfile
|
|
from os import path
|
|
import os
|
|
from collections import OrderedDict
|
|
import time
|
|
from threading import RLock
|
|
import pyinotify
|
|
from itertools import *
|
|
import operator
|
|
import traceback
|
|
from types import StringTypes
|
|
|
|
def addStdConfig(fileLst,prefix='/'):
|
|
stdPath = path.join(prefix,
|
|
"var/lib/layman/calculate/profiles/calculate.env")
|
|
if not stdPath in map(os.path.abspath,fileLst):
|
|
return [stdPath] + fileLst
|
|
return fileLst
|
|
|
|
def itemgetter(*args,**kwargs):
|
|
"""
|
|
itemgetter with alwaysTuple param, which used for force tuple elements
|
|
"""
|
|
alwaysTuple = kwargs.get('alwaysTuple',False)
|
|
if alwaysTuple:
|
|
if len(args)==1:
|
|
return lambda x:(x[args[0]],)
|
|
return operator.itemgetter(*args)
|
|
|
|
class CriticalError(Exception):
|
|
"""Critical error"""
|
|
|
|
class DataVarsError(Exception):
|
|
"""Exception of getting variable values"""
|
|
|
|
class VariableError(Exception):
|
|
"""Exception of sended by Variable"""
|
|
|
|
class PasswordError(VariableError):
|
|
"""Exception of password change error"""
|
|
|
|
class CommonVariableError(VariableError):
|
|
"""Exception for command line"""
|
|
|
|
import cl_template
|
|
|
|
def makePath(dirs,mode=755):
|
|
if not path.exists(dirs):
|
|
os.makedirs(dirs,mode)
|
|
|
|
from calculate.lib.cl_lang import setLocalTranslate
|
|
setLocalTranslate('cl_lib3',sys.modules[__name__])
|
|
|
|
READONLY = "r"
|
|
WRITEABLE = "w"
|
|
|
|
class Variable:
|
|
"""
|
|
Class of variable. Need for creating calculate utilities variable
|
|
add fill method described by class with fill methods.
|
|
|
|
Instance attributes:
|
|
wasSet : boolean
|
|
variable value was set by _Set method
|
|
reqVars : List
|
|
list of variable which use this variable in fill method
|
|
invalid : boolean
|
|
invalid value
|
|
section : string
|
|
section of variable (main,install,builder and etc)
|
|
"""
|
|
# label of variable
|
|
label = None
|
|
# opts for cmdline
|
|
opt = None
|
|
# metavalues
|
|
metavalue = None
|
|
# help information
|
|
help = None
|
|
# writeable variable
|
|
mode = WRITEABLE
|
|
# hidden variable
|
|
hide = True
|
|
# default value
|
|
value = None
|
|
# variable section
|
|
section = "main"
|
|
# variable name
|
|
name = ""
|
|
# gui label
|
|
label = None
|
|
# syntax for opts
|
|
syntax = ""
|
|
# opt for console
|
|
opt = None
|
|
# help for console
|
|
help = None
|
|
# variable type: string, multichoice, list, number, choiceedit, table
|
|
# flag, yesno, password
|
|
type = "string"
|
|
# metavalue for opt
|
|
metavalue = None
|
|
# source column with data
|
|
source = None
|
|
# gui elelemnt type
|
|
element = None
|
|
# force check after variables in list
|
|
check_after = []
|
|
# fill value is true
|
|
untrusted = False
|
|
NONE,CHECK,UNCOMPAT,HUMAN = range(4)
|
|
# при установке значения переменной устанавливает значение
|
|
# указанной переменной
|
|
alias = ""
|
|
|
|
def __init__(self,parent=None,**kwargs):
|
|
"""
|
|
Initialize instance
|
|
"""
|
|
self.modeGet = Variable.NONE
|
|
self.wasSet = False # variable is user or ini set
|
|
self.reqVars = [] # varaibles which used this value for fill
|
|
self.reqCheck = [] # variables which used this value for check
|
|
self.reqUncompat = [] # varaibles which used for uncompat
|
|
self.variableType = type
|
|
self.parent = parent
|
|
self.invalid = True # self.value is None or self.name in self.parent.iniCache
|
|
self.name = self.getVariableName()
|
|
self.processInvalidate = False
|
|
# производится чтение из ini
|
|
self.bIniRead = False
|
|
self.init()
|
|
for key,val in kwargs.items():
|
|
setattr(self,key,val)
|
|
|
|
def init(self):
|
|
"""
|
|
Overridible init using for palce translatable strings
|
|
"""
|
|
pass
|
|
|
|
@classmethod
|
|
def getVariableName(cls):
|
|
return re.sub("(.)([A-Z])","\\1_\\2",cls.__name__[8:]).lower()
|
|
|
|
def getCharType(self):
|
|
"""
|
|
Get symbol of type
|
|
"""
|
|
mapType = OrderedDict(table='t',list='l',choice='c',number='i',
|
|
bool='b')
|
|
for k in mapType.keys():
|
|
if k in self.type:
|
|
return mapType[k]
|
|
else:
|
|
return "s"
|
|
|
|
def Get(self,varname=None,humanreadable=False):
|
|
"""Get value of other value"""
|
|
if not varname or varname == self.name:
|
|
return self._get()
|
|
|
|
varObj = self.parent.getInfo(varname)
|
|
#varObj.modeGet = Variable.UNCOMPAT
|
|
if not "." in varname:
|
|
varname = "%s.%s"%(self.section,varname)
|
|
res = self.parent.Get(varname,humanreadable)
|
|
|
|
# build dependence for check and uncompatible
|
|
if varObj:
|
|
if self.modeGet == Variable.NONE:
|
|
if not self in varObj.reqVars:
|
|
varObj.reqVars.append(self)
|
|
if self.modeGet == Variable.CHECK:
|
|
if not self in varObj.reqCheck:
|
|
varObj.reqCheck.append(self)
|
|
elif self.modeGet == Variable.UNCOMPAT:
|
|
if not varObj in self.reqUncompat:
|
|
self.reqUncompat.append(varObj)
|
|
return res
|
|
|
|
def Choice(self,varname):
|
|
"""Choice from other value"""
|
|
return self.parent.Choice(varname)
|
|
|
|
def Select(self,selField,**kwargs):
|
|
"""Select by datavars"""
|
|
return self.parent.Select(selField,zipVars=self.ZipVars,**kwargs)
|
|
|
|
def getRemoteInfo(self, envFile):
|
|
"""getRemoteInfo by datavars"""
|
|
return self.parent.getRemoteInfo(envFile)
|
|
|
|
def ZipVars(self,*argvVarNames):
|
|
"""
|
|
Get zipped values of variables specified by list 'argvVarNames'
|
|
"""
|
|
return zip(*map(self.Get,argvVarNames))
|
|
|
|
def get(self):
|
|
"""
|
|
Overridable
|
|
Calculate value method
|
|
"""
|
|
if self.__class__.value:
|
|
return self.__class__.value
|
|
else:
|
|
return [] if "list" in self.type else ""
|
|
|
|
def humanReadable(self):
|
|
"""Return human readable value"""
|
|
val = self.Get()
|
|
if "choice" in self.type:
|
|
choiceVal, commentVal = self._choice()
|
|
if commentVal:
|
|
valDict = dict(zip(choiceVal,commentVal))
|
|
if "list" in self.type:
|
|
return map(lambda x:valDict.get(x,x),val)
|
|
else:
|
|
return valDict.get(val,val)
|
|
return val
|
|
|
|
def _human(self):
|
|
# TODO: may be rest to NONE
|
|
try:
|
|
oldModeGet = self.modeGet
|
|
self.modeGet = Variable.HUMAN
|
|
return self.humanReadable()
|
|
finally:
|
|
self.modeGet = oldModeGet
|
|
|
|
|
|
def getHumanReadableAuto(self):
|
|
"""
|
|
Return humanreadable values for readonly variables
|
|
"""
|
|
if self.mode == READONLY:
|
|
return self._human()
|
|
else:
|
|
return self._get()
|
|
|
|
def iniread(self):
|
|
"""
|
|
Read from ini file
|
|
"""
|
|
if self.bIniRead:
|
|
return False
|
|
self.bIniRead = True
|
|
try:
|
|
value = self.parent.iniCache[self.name]['value']
|
|
section = self.parent.iniCache[self.name]['section']
|
|
value = self.parent.unserialize(self.type,
|
|
value.encode('utf-8'))
|
|
if self.mode == READONLY:
|
|
raise VariableError(
|
|
_("Attempt to rewrite readonly variable %s")%
|
|
self.name)
|
|
else:
|
|
try:
|
|
self._set(value)
|
|
except Exception as e:
|
|
return False
|
|
return True
|
|
finally:
|
|
self.bIniRead = False
|
|
|
|
def _get(self):
|
|
"""
|
|
Standard inner method for getting value of variable.
|
|
"""
|
|
# variable need refill
|
|
if self.invalid:
|
|
# get net value
|
|
if self.name in self.parent.iniCache and \
|
|
self.iniread():
|
|
return self.value
|
|
else:
|
|
try:
|
|
oldModeGet = self.modeGet
|
|
self.modeGet = Variable.NONE
|
|
value = self.get()
|
|
finally:
|
|
self.modeGet = oldModeGet
|
|
# if value not equal previous value
|
|
if value != self.value:
|
|
# invalidate depended variables
|
|
self.invalidate()
|
|
# mark as filled
|
|
self.invalid = False
|
|
if hasattr(self.value,"close"):
|
|
self.value.close()
|
|
self.value = value
|
|
return self.value
|
|
|
|
def choice(self):
|
|
"""
|
|
Overridable
|
|
|
|
Generation list of allowable values
|
|
"""
|
|
return []
|
|
|
|
def _choice(self):
|
|
"""
|
|
Convert choice created by variable choice to two tuple (values,comments)
|
|
"""
|
|
res = self.choice()
|
|
if res and type(res[0]) in (tuple,list):
|
|
return zip(*res)
|
|
else:
|
|
return (res,None)
|
|
|
|
def check(self,value):
|
|
"""
|
|
Overridable
|
|
|
|
Check method, for wrong value need raise VariableError
|
|
"""
|
|
pass
|
|
|
|
def uncompatible(self):
|
|
"""
|
|
Overridable
|
|
|
|
Using for check uncompatibility using variable with value of
|
|
other variables.
|
|
"""
|
|
return ""
|
|
|
|
def _uncompatible(self):
|
|
"""
|
|
Full check uncompatible
|
|
"""
|
|
try:
|
|
self.modeGet = Variable.UNCOMPAT
|
|
self.reqUncompat = []
|
|
return self.uncompatible()
|
|
finally:
|
|
self.modeGet = Variable.NONE
|
|
|
|
def set(self,value):
|
|
"""
|
|
Overridable
|
|
|
|
Using for replace value before set
|
|
"""
|
|
def convert(value):
|
|
if value and value.lower() != "auto":
|
|
return "on" if self.isTrue(value) else "off"
|
|
else:
|
|
return ""
|
|
if self.type == "bool":
|
|
value = "on" if self.isTrue(value) else "off"
|
|
if "bool" in self.type and "list" in self.type:
|
|
return map(convert,value)
|
|
if "int" in self.type:
|
|
return str(value)
|
|
return value
|
|
|
|
def raiseNothingValue(self):
|
|
"""
|
|
Using for override error message about nothing value.
|
|
"""
|
|
pass
|
|
|
|
def checkType(self,value):
|
|
"""Check value for type"""
|
|
if "int" in self.type:
|
|
if value and not value.isdigit():
|
|
raise VariableError(
|
|
_("The value of variable '{varname}' must be integer"
|
|
).format(varname=self.label or self.name))
|
|
if "list" in self.type:
|
|
if not type(value) in (list,tuple):
|
|
raise VariableError(
|
|
_("The value for variable '{varname}' may be {vartype} only").format(
|
|
varname=self.label or self.name,
|
|
vartype="list"))
|
|
error = _("Values for variable '{varname}' may be {vartype} only")
|
|
else:
|
|
value = repeat(value,1)
|
|
error = _("The value for variable '{varname}' may be {vartype} only")
|
|
if "string" in self.type:
|
|
value, valuecopy = tee(value,2)
|
|
for v in (x for x in valuecopy if not type(x) in (str,unicode)):
|
|
raise VariableError(error.format(
|
|
varname=self.label or self.name,
|
|
vartype="string"))
|
|
return
|
|
if "choice" in self.type:
|
|
choiceVal = self.choice()
|
|
tipChoice = map(lambda x:'"%s" [%s]'%(x[1],x[0]) \
|
|
if type(x) in (list,tuple) else str(x), choiceVal)
|
|
if choiceVal and type(choiceVal[0]) in (tuple,list):
|
|
choiceVal = [x[0] for x in choiceVal]
|
|
if not "list-list" in self.type:
|
|
value = repeat(value,1)
|
|
for recval in value:
|
|
for val in recval:
|
|
name = self.label or self.name
|
|
if val == "list" and "choice" in self.type:
|
|
if "edit" in self.type:
|
|
if "list" in self.type:
|
|
error = _("Values for variable '{varname}' may "
|
|
"be {vartype}")
|
|
else:
|
|
error = _("The value for variable '{varname}' may be "
|
|
"{vartype}")
|
|
self.raiseWrongChoice(name, tipChoice, val, error)
|
|
if "choice" in self.type and not "choiceedit" in self.type:
|
|
if choiceVal and val and not val in choiceVal or \
|
|
val is None:
|
|
if not choiceVal:
|
|
self.raiseNothingValue()
|
|
self.raiseWrongChoice(name,tipChoice,val,error)
|
|
|
|
def raiseWrongChoice(self,name,choiceVal,value,error):
|
|
raise VariableError(error.format(
|
|
varname=name,
|
|
vartype=formatListOr(choiceVal)))
|
|
|
|
def setValue(self,value,force=False):
|
|
"""
|
|
Standard action for set value
|
|
"""
|
|
self.value = self.set(value)
|
|
self.wasSet = True
|
|
self.invalid = False
|
|
# run check
|
|
if not force:
|
|
self._check()
|
|
|
|
def _set(self,value,force=False):
|
|
"""
|
|
Standard inner method for setting value for variable.
|
|
"""
|
|
# runc check choice
|
|
if type(value) == int:
|
|
value = str(value)
|
|
if not force:
|
|
try:
|
|
self.modeGet = Variable.CHECK
|
|
self.checkType(value)
|
|
finally:
|
|
self.modeGet = Variable.NONE
|
|
# if value change
|
|
if value != self.value or type(value) in (list,tuple):
|
|
self.invalidate(True)
|
|
if hasattr(self.value,"close"):
|
|
self.value.close()
|
|
# run set value
|
|
self.setValue(value,force)
|
|
|
|
def _check(self,value=None):
|
|
"""
|
|
Full check value
|
|
"""
|
|
if value is None:
|
|
value = self.Get(self.name)
|
|
try:
|
|
self.modeGet = Variable.CHECK
|
|
self.untrusted = True
|
|
self.check(value)
|
|
self.untrusted = False
|
|
finally:
|
|
self.modeGet = Variable.NONE
|
|
|
|
def invalidate(self,force=False):
|
|
"""
|
|
Invalidate value of variable (drop filled value).
|
|
|
|
force : boolean=False
|
|
Drop value also for after manual set
|
|
"""
|
|
# fix possible infinite recursion on invalidating
|
|
if not self.processInvalidate and (force or not self.wasSet):
|
|
try:
|
|
# mark variable that in invalidation now
|
|
self.processInvalidate = True
|
|
self.wasSet = False
|
|
self.invalid = True
|
|
for var in self.reqVars:
|
|
var.invalidate()
|
|
for var in self.reqCheck:
|
|
var.untrusted = True
|
|
self.reqVars = []
|
|
self.reqCheck = []
|
|
finally:
|
|
# unmark process invalidate
|
|
self.processInvalidate = False
|
|
|
|
@classmethod
|
|
def isTrue(self,value):
|
|
if type(value) == bool:
|
|
return value
|
|
if value.lower() in ('yes','on','true'):
|
|
return True
|
|
return False
|
|
|
|
class TableVariable(Variable):
|
|
"""
|
|
Table type variable
|
|
"""
|
|
type = "table"
|
|
|
|
def set(self,value):
|
|
return value
|
|
|
|
def get(self,hr=False):
|
|
"""Get table data"""
|
|
for varname,value in ifilter(lambda x:type(x[1]) != list,
|
|
imap(lambda x:(x,self.Get(x)),
|
|
self.source)):
|
|
raise VariableError(
|
|
_("Source variable %s does not contain a list")%varname)
|
|
return map(list,
|
|
izip_longest(
|
|
*map(lambda x:self.Get(x,humanreadable=hr),
|
|
self.source),fillvalue='')) or [[]]
|
|
|
|
def getHumanReadableAuto(self):
|
|
"""
|
|
Return humanreadable values for readonly variables
|
|
"""
|
|
return self.get(hr=None)
|
|
|
|
def humanReadable(self):
|
|
return self.get(hr=True)
|
|
|
|
def __getWritableColumns(self,includeFirst=False):
|
|
"""
|
|
Get data for writable columns exclude index column
|
|
(Example: (1,'os_disk_mount)
|
|
"""
|
|
if includeFirst:
|
|
offset = 0
|
|
else:
|
|
offset = 1
|
|
return filter(lambda x:self.parent.getInfo(x[1]).mode == WRITEABLE,
|
|
enumerate(self.source[offset:]))
|
|
|
|
def check(self,value):
|
|
"""
|
|
Check table value - check all writeable columns.
|
|
"""
|
|
writeCols = self.__getWritableColumns(includeFirst=True)
|
|
error = []
|
|
if not any(value):
|
|
value = [[]]*len(writeCols)
|
|
else:
|
|
value = zip(*map(itemgetter(*map(itemgetter(0),
|
|
writeCols),alwaysTuple=True),value))
|
|
for colInfo, values in \
|
|
zip(writeCols,value):
|
|
try:
|
|
self.parent.Check(colInfo[1],values)
|
|
except VariableError as e:
|
|
error.append(e)
|
|
if error:
|
|
raise VariableError(error)
|
|
|
|
def __isIndexWritable(self):
|
|
return self.parent.getInfo(self.source[0]).mode == WRITEABLE
|
|
|
|
def checkType(self,value):
|
|
"""Check table value type"""
|
|
# check type value (list and each element is list)
|
|
if not type(value) in (list,tuple) or \
|
|
any(i for i in value if not type(i) in (tuple,list)):
|
|
raise VariableError(
|
|
_("The value for {varname} may be '{vartype}' only").format(
|
|
varname=self.label or self.name,
|
|
vartype="table"))
|
|
# check len all entries
|
|
writeLen = len(self.__getWritableColumns())+1
|
|
for item in value:
|
|
if item and not len(item) in (writeLen,len(self.source)):
|
|
raise VariableError(
|
|
_("Wrong entry '{entry}' for table variable '{varname}'").
|
|
format(entry=str(item),
|
|
varname=(self.label or self.name).lower())+"\n"+
|
|
_("Each entry must contains %d field(s)")%writeLen)
|
|
|
|
# check rewrite readonly index
|
|
if not self.__isIndexWritable():
|
|
indexValues = self.Get(self.source[0])
|
|
for item in value:
|
|
if item and not item[0] in indexValues:
|
|
self.raiseReadonlyIndexError(self.source[0],
|
|
self.name,
|
|
item[0])
|
|
|
|
def raiseReadonlyIndexError(self,fieldname="",variablename="",
|
|
value=""):
|
|
"""
|
|
Overriddible method for spcifing behavior on rewrite
|
|
readonly index
|
|
"""
|
|
raise DataVarsError(
|
|
_("Attempt to rewrite a readonly index field "
|
|
"{fieldname} in variable {variablename}").format(
|
|
fieldname=fieldname,variablename=variablename))
|
|
|
|
def setValue(self,value,force=False):
|
|
value = self.set(value)
|
|
self.untrusted = True
|
|
oldvalue = self.Get(self.name)
|
|
# get writable columns
|
|
writeCols = self.__getWritableColumns()
|
|
# get slicer
|
|
_slice = itemgetter(*map(itemgetter(0),writeCols),
|
|
alwaysTuple=True)
|
|
# create dict for writable columns
|
|
# if table not empty
|
|
if any(oldvalue):
|
|
oldvalue = OrderedDict(map(lambda x:(x[0],_slice(x[1:])),
|
|
oldvalue))
|
|
else:
|
|
oldvalue = OrderedDict()
|
|
# get new dict
|
|
if any(value):
|
|
if len(value[0]) == len(self.source):
|
|
newval = OrderedDict(map(lambda x:(x[0],_slice(x[1:])),
|
|
value))
|
|
else:
|
|
newval = OrderedDict(map(lambda x:(x[0],x[1:]),value))
|
|
else:
|
|
newval = OrderedDict()
|
|
# if table with writable index field then replace all table
|
|
error = []
|
|
if self.__isIndexWritable():
|
|
if not force and any(value):
|
|
self.parent.Check(self.source[0],zip(*value)[0])
|
|
oldvalue = newval
|
|
try:
|
|
self.parent.Set(self.source[0],oldvalue.keys())
|
|
except VariableError,e:
|
|
error.append(e)
|
|
# update entry by index field
|
|
else:
|
|
oldvalue.update(newval)
|
|
oldvalValues = zip(*oldvalue.values())
|
|
for col,vals in zip(map(lambda x:x[1],writeCols),
|
|
oldvalValues):
|
|
try:
|
|
self.parent.Set(col,list(vals))
|
|
except VariableError,e:
|
|
error.append(e)
|
|
for num,varname in writeCols[len(oldvalValues):]:
|
|
self.parent.Invalidate(varname,True)
|
|
if error:
|
|
raise VariableError(error)
|
|
else:
|
|
self.untrusted = False
|
|
|
|
class ReadonlyVariable(Variable):
|
|
"""
|
|
Alias for readonly variables
|
|
"""
|
|
mode = READONLY
|
|
|
|
class ActionVariable(ReadonlyVariable):
|
|
nonchroot = False
|
|
postinst = False
|
|
|
|
def get(self):
|
|
if self.nonchroot and self.Get('cl_chroot_status') == "on":
|
|
return "off"
|
|
if self.postinst and not self.Get('cl_ebuild_phase') in \
|
|
('','postinst','prerm'):
|
|
return "off"
|
|
return self.action(self.Get('cl_action'))
|
|
|
|
class FieldValue:
|
|
"""
|
|
Table column variable
|
|
"""
|
|
type = "list"
|
|
source_variable = ""
|
|
column = 0
|
|
|
|
def get(self):
|
|
sourceVar = self.Get(self.source_variable)
|
|
if any(sourceVar):
|
|
return zip(*sourceVar)[self.column]
|
|
else:
|
|
return []
|
|
|
|
class SourceReadonlyVariable(ReadonlyVariable):
|
|
"""
|
|
Source readonly variable (Using for table)
|
|
Example:
|
|
|
|
['os_location_source',...,'os_location_size'(this)]
|
|
"""
|
|
indexField = ""
|
|
|
|
def get(self):
|
|
return map(lambda x:x or "",
|
|
map(self.getMap().get,
|
|
self.Get(self.indexField)))
|
|
|
|
def humanReadable(self):
|
|
return map(lambda x:x or "",
|
|
map(self.getMapHumanReadable().get,
|
|
self.Get(self.indexField)))
|
|
|
|
def getMap(self):
|
|
return {}
|
|
|
|
def getMapHumanReadable(self):
|
|
return self.getMap()
|
|
|
|
class ReadonlyTableVariable(TableVariable):
|
|
"""
|
|
Alias for readonly table
|
|
"""
|
|
mode = READONLY
|
|
|
|
class FileVariable(Variable):
|
|
"""
|
|
Test variable
|
|
"""
|
|
filename = ""
|
|
directory = ""
|
|
recursive = False
|
|
wm = None
|
|
|
|
def init(self):
|
|
if self.wm is None:
|
|
self.wm = pyinotify.WatchManager()
|
|
if self.filename:
|
|
self.mask = pyinotify.IN_MODIFY
|
|
else:
|
|
self.mask = pyinotify.IN_CREATE | pyinotify.IN_DELETE
|
|
self.notifier = pyinotify.Notifier(self.wm,timeout=1)
|
|
self.wdd = self.wm.add_watch(self.filename or self.directory,
|
|
self.mask,
|
|
rec=self.recursive)
|
|
|
|
def close(self):
|
|
self.notifier.stop()
|
|
|
|
def refresh(self):
|
|
if self.notifier.check_events():
|
|
self.notifier.read_events()
|
|
self.parent.Invalidate(self.name)
|
|
|
|
class SimpleDataVars:
|
|
"""
|
|
Simple datavars for temporary link var objs.
|
|
"""
|
|
systemRoot = "/"
|
|
def __init__(self,*objs):
|
|
self.allVars = {}
|
|
self.cache = {}
|
|
for obj in objs:
|
|
obj.parent = self
|
|
self.allVars[obj.getVariableName()] = obj
|
|
|
|
def Get(self,varname,humanreadable=False):
|
|
varname = varname.rpartition(".")[2]
|
|
if not varname in self.cache:
|
|
if self.allVars.get(varname) is not None:
|
|
self.cache[varname] = self.allVars.get(varname).get()
|
|
else:
|
|
raise DataVarsError(_("Failed to get value %s")%varname)
|
|
return self.cache[varname]
|
|
|
|
def getInfo(self,varname):
|
|
return self.allVars.get(varname, None)
|
|
|
|
def flIniFileFrom(self,iniFile):
|
|
"""
|
|
Read variable values from ini files.
|
|
Return False if ini file is invalid.
|
|
"""
|
|
if path.isdir(iniFile):
|
|
config = cl_template.iniParser(
|
|
text=reduce(lambda x, y: x + readFile(y),
|
|
addStdConfig(
|
|
searchProfile(iniFile, 'calculate.env'),
|
|
prefix=self.systemRoot), ""))
|
|
else:
|
|
config = cl_template.iniParser(iniFile)
|
|
importVars = {}
|
|
for varname, varobj in self.allVars.items():
|
|
if varobj.section not in importVars:
|
|
importVars[varobj.section] = config.getAreaVars(varobj.section)
|
|
if not varname in self.cache:
|
|
val = self.unserialize(varobj.type or "string",
|
|
importVars[varobj.section].get(varname,'').encode('utf-8'))
|
|
if val or varname in importVars[varobj.section]:
|
|
self.cache[varname] = val
|
|
|
|
@staticmethod
|
|
def serialize(varType, value):
|
|
"""
|
|
Serialize to string for ini file
|
|
"""
|
|
fixEmpty = lambda x: str(x) or "''"
|
|
fixSpace = lambda x: "'%s'" % str(x) if " " in str(x) else str(x)
|
|
isListOrTuple = lambda x: type(x) in (list, tuple)
|
|
if isListOrTuple(value):
|
|
if "list" in varType:
|
|
return ",".join(map(fixEmpty, value))
|
|
elif "table" in varType:
|
|
return ",".join(map(lambda x: ":".join(map(fixEmpty, x)),
|
|
value))
|
|
return fixEmpty(value)
|
|
|
|
@staticmethod
|
|
def unserialize(varType, value):
|
|
"""
|
|
Unserialize form string for varname
|
|
"""
|
|
fixEmpty = lambda x: "" if x == "''" or x == '""' else x.strip()
|
|
|
|
def getList(delimeter=','):
|
|
def wrapper(val):
|
|
if val == "":
|
|
return []
|
|
return map(fixEmpty, val.split(delimeter))
|
|
|
|
return wrapper
|
|
|
|
if "list" in varType:
|
|
return getList()(value)
|
|
if "table" in varType:
|
|
return map(getList(':'), value.split(','))
|
|
return fixEmpty(value).strip("'").strip('"')
|
|
|
|
|
|
class DataVars(SimpleDataVars):
|
|
"""
|
|
Class contains variables, perform creating variable objects by
|
|
class variable describer and variable filler.
|
|
|
|
Instance attributes:
|
|
moduleList : Dict
|
|
dictionary has section name with modules which describe variable
|
|
for this sectin
|
|
loadVariables : Dict
|
|
contains all initialized variables load from varObj and fillObj
|
|
requestVariables : List
|
|
contains var object which already run Get method. Use for detect
|
|
dependence of variables.
|
|
"""
|
|
defaultData = 'calculate.lib.variables'
|
|
reRightVar = re.compile(r'^(?:([a-z_]+)\.)?([a-z_0-9]+)$')
|
|
l = RLock()
|
|
|
|
def __init__(self):
|
|
self.requestVariables = []
|
|
self.loadVariables = {}
|
|
self.allVars = {}
|
|
self.untrustedVariables = set()
|
|
self.refresh = []
|
|
self.groups = []
|
|
self.mapVarGroup = OrderedDict()
|
|
self.briefData = {}
|
|
self.filledVars = {}
|
|
self.defaultModule = "main"
|
|
self.importedModules = []
|
|
self.iniCache = {}
|
|
|
|
def isModuleInstalled(self,module):
|
|
try:
|
|
importlib.import_module("calculate.%s.datavars"%module)
|
|
return True
|
|
except (ImportError) as e:
|
|
return False
|
|
|
|
def importData(self,data=None):
|
|
"""
|
|
Depricated function for support 3.0 utilities
|
|
"""
|
|
section = self.importVariables(data)
|
|
if data:
|
|
self.defaultModule = section
|
|
|
|
def importVariables(self,data=None):
|
|
"""
|
|
Import modules for variables. 'section' is section name, 'modList'
|
|
contains tuple with modules which discribe variable names and fill
|
|
methods
|
|
"""
|
|
# try import vars and fill modules specified by modList
|
|
if not data:
|
|
data = self.defaultData
|
|
try:
|
|
varModule = importlib.import_module(data)
|
|
except (ImportError,AttributeError),e:
|
|
if not self.raiseModuleError(data,e):
|
|
return False
|
|
# get all Variable classes from module
|
|
# variable of class VariableClAction will be named cl_action
|
|
if hasattr(varModule,"section"):
|
|
section = varModule.section
|
|
else:
|
|
section = "main"
|
|
for varMod in map(lambda x:getattr(varModule,x),
|
|
filter(lambda x:not x.startswith("_") and \
|
|
not "Variable" in x,
|
|
dir(varModule)))+[varModule]:
|
|
for classname in \
|
|
ifilterfalse(("ReadonlyVariable",
|
|
"Variable","VariableError").__contains__,
|
|
ifilter(lambda x:x.startswith("Variable"),
|
|
dir(varMod))):
|
|
varObj = getattr(varMod,classname)
|
|
varName = varObj.getVariableName()
|
|
if not varName in self.allVars:
|
|
self.allVars[varName] = (section,varObj)
|
|
self.importedModules.append(section)
|
|
return section
|
|
|
|
def raiseVariableNotFound(self,varname,*args,**kwargs):
|
|
raise DataVarsError(_("Variable %s not found")%varname)
|
|
|
|
def raiseModuleError(self,module,error,*args,**kwargs):
|
|
raise DataVarsError("\n".join([
|
|
_("Failed to import module %s")%module,
|
|
_("error") + ": " +str(error)]))
|
|
|
|
def loadVariable(self,varname,section=None):
|
|
"""
|
|
Initialize variable 'varname' from fillObj and varObj.
|
|
"""
|
|
if not self.allVars:
|
|
self.importVariables()
|
|
if section and not section in self.importedModules:
|
|
self.importVariables("calculate.%s.variables"%section)
|
|
self.flIniFile(onlySection=section)
|
|
if not varname in self.loadVariables:
|
|
if varname in self.allVars.keys():
|
|
section,varObj = self.allVars[varname]
|
|
newVar = varObj(parent=self,section=section)
|
|
if hasattr(newVar,"refresh"):
|
|
self.refresh.append(newVar)
|
|
self.loadVariables[varname] = newVar
|
|
else:
|
|
# this code using for raise exception or
|
|
# if replace raiseVariableNotFound method
|
|
# then return default value
|
|
self.loadVariables[varname] = \
|
|
self.raiseVariableNotFound(
|
|
varname,parent=self,section="main")
|
|
|
|
def exists(self,varName):
|
|
try:
|
|
self.Get(varName)
|
|
return True
|
|
except:
|
|
return False
|
|
|
|
def defined(self,varName):
|
|
"""
|
|
Return exists or not varName
|
|
"""
|
|
return varName in self.allVars.keys()
|
|
|
|
def getIniVar(self,fullVarName):
|
|
"""
|
|
Get variable values from ini files. Variable specified by
|
|
'fullVarName' in format: section 'dot' variable. If specified
|
|
only variable then section will be 'main'
|
|
|
|
Example:
|
|
self.getIniVar('install.os_install_net_settings')
|
|
self.getIniVar('cl_action')
|
|
"""
|
|
calculateIniFiles = self.Get('main.cl_env_path')
|
|
section, dot, varname = fullVarName.rpartition('.')
|
|
if not section:
|
|
section = "main"
|
|
retVal = ""
|
|
for iniFile in calculateIniFiles:
|
|
if path.exists(iniFile):
|
|
config = cl_template.iniParser(iniFile)
|
|
data = config.getVar(section,varname,checkExistVar=True)
|
|
if data is False:
|
|
return False
|
|
existsVar, value = data
|
|
if existsVar:
|
|
retVal = value
|
|
return retVal.encode('UTF-8')
|
|
|
|
def getRemoteInfo(self, envFile):
|
|
"""
|
|
Get information variables from envFile
|
|
Return dict:
|
|
section : { variable1 : value,
|
|
variable2 : value }
|
|
section2 : { variable : value }
|
|
"""
|
|
optionsInfo = {}
|
|
config = cl_template.iniParser(envFile)
|
|
allsect = config.getAllSectionNames()
|
|
if allsect:
|
|
for section in allsect:
|
|
allvars = config.getAreaVars(section)
|
|
if allvars == False:
|
|
return False
|
|
# options (dict of variables:value)
|
|
options = {}
|
|
for varName, value in allvars.items():
|
|
varName = varName.encode("UTF-8")
|
|
value=convertStrListDict(value.encode("UTF-8"))
|
|
options[varName] = value
|
|
# if options exists add section to return dict as key section
|
|
if options:
|
|
optionsInfo[section] = options
|
|
return optionsInfo
|
|
|
|
def flIniFile(self,raiseOnError=False,onlySection=None):
|
|
"""
|
|
Read variable values from ini files.
|
|
Return False if ini file is invalid.
|
|
"""
|
|
if not self.iniCache:
|
|
# get initialized section names
|
|
try:
|
|
make_profile = self.Get('main.cl_make_profile')
|
|
except VariableError as e:
|
|
action = self.Get('cl_action')
|
|
if not action or action in ['update_profile']:
|
|
return True
|
|
raise CriticalError(str(e))
|
|
if os.path.exists(make_profile):
|
|
profiles = [('profile',
|
|
path.join('/etc',os.readlink(make_profile)))]
|
|
else:
|
|
profiles = []
|
|
for iniName,iniFile in profiles+\
|
|
self.Get('main.cl_env_data'):
|
|
if path.exists(iniFile) or iniName == 'profile':
|
|
if iniName == 'profile':
|
|
config = cl_template.iniParser(
|
|
text=reduce(lambda x,y:x+readFile(y),
|
|
addStdConfig(
|
|
searchProfile(iniFile,'calculate.env')),""))
|
|
else:
|
|
config = cl_template.iniParser(iniFile)
|
|
iniSections = config.getAllSectionNames()
|
|
if not iniSections:
|
|
continue
|
|
if onlySection:
|
|
iniSections = tuple(set(iniSections)&
|
|
set([unicode(onlySection)]))
|
|
for section in iniSections:
|
|
importVars = config.getAreaVars(section)
|
|
if self.allVars == False:
|
|
continue
|
|
for key,value in importVars.items():
|
|
key = _toUNICODE(key).encode('utf-8')
|
|
self.iniCache[key] = {'value':value,
|
|
'section':section}
|
|
if iniName != "profile":
|
|
self.filledVars[key] = iniName
|
|
return True
|
|
|
|
def Get(self,varname,humanreadable=False):
|
|
"""Threading safety Get"""
|
|
DataVars.l.acquire()
|
|
try:
|
|
var = self.__Get(varname,humanreadable)
|
|
finally:
|
|
DataVars.l.release()
|
|
return var
|
|
|
|
def GetBool(self,varname):
|
|
return Variable.isTrue(self.Get(varname))
|
|
|
|
def GetInteger(self,varname):
|
|
"""
|
|
Получить целочисленное значение
|
|
"""
|
|
try:
|
|
return int(self.Get(varname))
|
|
except ValueError:
|
|
return 0
|
|
|
|
def isFromIni(self,varname):
|
|
"""
|
|
Check what value get from ini file
|
|
"""
|
|
section,varname = self.splitVarname(varname)
|
|
return self.filledVars[varname] if varname in self.filledVars else ""
|
|
|
|
def splitVarname(self,varname):
|
|
res = self.reRightVar.search(varname)
|
|
if res:
|
|
if res.group(1):
|
|
return res.groups()
|
|
else:
|
|
return (self.defaultModule,res.group(2))
|
|
else:
|
|
raise DataVarsError(_("Wrong variable name %s")%varname)
|
|
|
|
def __Get(self, varname, humanreadable=False):
|
|
"""
|
|
Get value of variable 'varname'
|
|
"""
|
|
try:
|
|
#tm = time.time()
|
|
# load variable if it isn't loaded
|
|
section,varname = self.splitVarname(varname)
|
|
if not varname in self.loadVariables or \
|
|
not section in self.importedModules:
|
|
self.loadVariable(varname,section=section)
|
|
varObj = self.loadVariables[varname]
|
|
|
|
# check section
|
|
if not varObj.section in (section,"main"):
|
|
self.raiseVariableNotFound(varname,parent=self,section=section)
|
|
|
|
# if for this variable already use Get method
|
|
if varObj.invalid and varObj in self.requestVariables:
|
|
varnames = "-".join(map(lambda x:x.name,
|
|
self.requestVariables+[varObj]))
|
|
raise DataVarsError(
|
|
_("Loop dependence of variables '%s'")%varnames)
|
|
# add this variable for list of requested variables
|
|
if not humanreadable:
|
|
self.requestVariables.append(varObj)
|
|
if humanreadable is None:
|
|
res = varObj.getHumanReadableAuto()
|
|
elif humanreadable is True:
|
|
res = varObj._human()
|
|
else:
|
|
res = varObj._get()
|
|
if not humanreadable:
|
|
if self.requestVariables:
|
|
self.requestVariables.pop()
|
|
return res
|
|
except BaseException,e:
|
|
while(len(self.requestVariables)):
|
|
self.requestVariables.pop()
|
|
raise
|
|
|
|
def Check(self,varname,value):
|
|
"""
|
|
Check varialbe value
|
|
"""
|
|
# load variable if it isn't loaded
|
|
if not varname in self.loadVariables:
|
|
self.loadVariable(varname)
|
|
varObj = self.loadVariables[varname]
|
|
varObj.checkType(value)
|
|
varObj._check(value)
|
|
|
|
def Choice(self, varname):
|
|
"""
|
|
Get choice value of variable 'varname'
|
|
"""
|
|
# load variable if it isn't loaded
|
|
if not varname in self.loadVariables:
|
|
self.loadVariable(varname)
|
|
varObj = self.loadVariables[varname]
|
|
return varObj._choice()[0]
|
|
|
|
def Uncompatible(self, varname):
|
|
"""
|
|
Check on compatible change value of this variable
|
|
|
|
Empty string if variable changable or string with error
|
|
"""
|
|
# load variable if it isn't loaded
|
|
if not varname in self.loadVariables:
|
|
self.loadVariable(varname)
|
|
varObj = self.loadVariables[varname]
|
|
return varObj._uncompatible()
|
|
|
|
def getRequired(self,varname):
|
|
"""
|
|
Generate list variables using in uncompatible check
|
|
"""
|
|
added = []
|
|
def genReqs(varObj):
|
|
if not varObj in added:
|
|
yield varObj
|
|
for var in varObj.reqVars:
|
|
for dep in genReqs(var):
|
|
yield dep
|
|
if hasattr(varObj,"source") and varObj.source:
|
|
for varsource in imap(self.getInfo,varObj.source):
|
|
for dep in genReqs(varsource):
|
|
yield dep
|
|
for dep in genReqs(self.getInfo(varname)):
|
|
added.append(dep)
|
|
return added
|
|
|
|
def ChoiceAndComments(self,varname):
|
|
if not varname in self.loadVariables:
|
|
self.loadVariable(varname)
|
|
varObj = self.loadVariables[varname]
|
|
return varObj._choice()
|
|
|
|
def getInfo(self, varname):
|
|
"""
|
|
Get info (label and other for variable)
|
|
"""
|
|
section,varname = self.splitVarname(varname)
|
|
if not varname in self.loadVariables:
|
|
self.loadVariable(varname,section=section or None)
|
|
return self.loadVariables[varname]
|
|
|
|
def Set(self, varname, varvalue,force=False):
|
|
"""
|
|
Set value 'varvalue' for variable 'varname'
|
|
"""
|
|
varObj = self.getInfo(varname)
|
|
if force or varObj.mode != READONLY:
|
|
varObj._set(varvalue,force)
|
|
if varObj.alias:
|
|
self.Set(varObj.alias,varObj.value,force)
|
|
else:
|
|
raise DataVarsError(
|
|
_("Attempt to rewrite readonly variable %s")%
|
|
varname)
|
|
|
|
def Invalidate(self,varname,force=False,onlySet=False):
|
|
"""
|
|
Invalidate value of variable. Force - invalidate also for set values
|
|
"""
|
|
if not varname in self.loadVariables:
|
|
self.loadVariable(varname)
|
|
varObj = self.loadVariables[varname]
|
|
if not onlySet or varObj.wasSet:
|
|
varObj.invalidate(force or onlySet)
|
|
|
|
def getEnvFileByLocation(self,location):
|
|
"""
|
|
Get env file path by location alias. This path append to value
|
|
of variable 'cl_chroot_path'.
|
|
Return False if alias not found.
|
|
"""
|
|
calculateIniFile = self.Select('cl_env_path',
|
|
where='cl_env_location',
|
|
eq=location,limit=1)
|
|
if calculateIniFile:
|
|
return pathJoin(self.Get('main.cl_chroot_path'),calculateIniFile)
|
|
else:
|
|
return False
|
|
|
|
def Write(self, varname, val=None, force=False, location='system',
|
|
header=False):
|
|
"""
|
|
Write variable 'varname' to ini file. If set 'val', then to
|
|
variable before write perform value changing. 'force' use for
|
|
change value of read-only variables. 'location' must be ('system',
|
|
'local', 'remote') use for specify type of ini file location ini.
|
|
'header' use for specify section in ini file. If header not
|
|
specified then use default for this value section.
|
|
"""
|
|
# set value to variable if specified value
|
|
if not val is None:
|
|
self.Set(varname,val,force=force)
|
|
else:
|
|
if header:
|
|
vname = "%s.%s"%(header,varname)
|
|
val = self.Get(vname)
|
|
# get path to target ini file
|
|
iniFile = self.getEnvFileByLocation(location)
|
|
if iniFile:
|
|
# make parent directories
|
|
makePath(path.dirname(iniFile))
|
|
# get variable section if not specified header
|
|
if not header:
|
|
header = self.loadVariables[varname].section
|
|
# write value to ini file
|
|
config = cl_template.iniParser(iniFile)
|
|
val = self.serialize(self.getInfo(varname).type,
|
|
val)
|
|
writeVal = convertStrListDict(val)
|
|
if writeVal != config.getVar(header,varname):
|
|
if not config.setVar(header,{varname: writeVal}):
|
|
raise DataVarsError(
|
|
_("Unable to write variable {varname} into "
|
|
"'{location}'").format(varname=varname,
|
|
location=location))
|
|
return True
|
|
else:
|
|
raise DataVarsError(_("Unable to find alias '%s' for the "
|
|
"template variables path")%location)
|
|
return False
|
|
|
|
def Delete(self, varname, location='system', header=False):
|
|
"""
|
|
Delete variable 'varname' from ini file and refresh value.
|
|
'location' must be ('system', 'local', 'remote') use for specify
|
|
type of ini file location ini. 'header' use for specify section in
|
|
ini file. If header not specified then use default for this value
|
|
section.
|
|
"""
|
|
# get path to target ini file
|
|
iniFile = self.getEnvFileByLocation(location)
|
|
if iniFile:
|
|
# make parent directories
|
|
makePath(path.dirname(iniFile))
|
|
# get variable section if not specified header
|
|
if not header:
|
|
if not varname in self.loadVariables:
|
|
self.loadVariable(varname)
|
|
header = self.loadVariables[varname].section
|
|
# write value to ini file
|
|
config = cl_template.iniParser(iniFile)
|
|
allVars = config.getAreaVars(header)
|
|
if allVars is False:
|
|
return False
|
|
if varname in allVars.keys():
|
|
retDelVar = config.delVar(header,varname)
|
|
# if success delete value and this section empty
|
|
# of ini file then remove it
|
|
return retDelVar and (config.getAreaVars(header) or
|
|
config.delArea(header))
|
|
return True
|
|
else:
|
|
raise DataVarsError(_("Unable to find alias '%s' for the "
|
|
"template variables path")%location)
|
|
return False
|
|
|
|
def Select(self,selField,where="os_disk_dev",eq=None,ne=None,
|
|
_in=None,_notin=None,like=None,notlike=None,func=None,
|
|
sort=None, sortkey=None, limit=None,zipVars=None):
|
|
"""Select value from table variables"""
|
|
if zipVars is None:
|
|
zipVars = self.ZipVars
|
|
if func:
|
|
if func.func_code.co_argcount > 1:
|
|
filterFunc = lambda x:func(*x[:func.func_code.co_argcount])
|
|
else:
|
|
filterFunc = func
|
|
elif eq != None:
|
|
filterFunc = lambda x:x[0] == eq
|
|
elif ne != None:
|
|
filterFunc = lambda x:x[0] != ne
|
|
elif _in != None:
|
|
filterFunc = lambda x:x[0] in _in
|
|
elif _notin != None:
|
|
filterFunc = lambda x:not x[0] in _notin
|
|
elif like != None:
|
|
filterFunc = lambda x:re.compile(like).search(x[0])
|
|
elif notlike != None:
|
|
filterFunc = lambda x:not re.compile(notlike).search(x[0])
|
|
else:
|
|
filterFunc = lambda x:x
|
|
|
|
if not type(where) in (tuple,list):
|
|
where = [where]
|
|
woffset = len(where)
|
|
if type(selField) in (tuple,list):
|
|
count = len(selField)+woffset
|
|
mapFunc = lambda x:x[woffset:]
|
|
res = filter(filterFunc,
|
|
zipVars(*(where+selField)))
|
|
else:
|
|
count = 1+woffset
|
|
fields = where+[selField]
|
|
mapFunc = lambda x:x[woffset]
|
|
res = filter(filterFunc,
|
|
zipVars(*fields))
|
|
if sort:
|
|
if "/" in sort:
|
|
sort,sortkey = sort.split('/')
|
|
sortkey = itemgetter(*map(int,sortkey.split(',')))
|
|
res.sort(key=sortkey,reverse=True if sort == "DESC" else False)
|
|
if limit == 1:
|
|
if not any(res):
|
|
res = [[""]*(count)]
|
|
return mapFunc(res[0])
|
|
else:
|
|
return map(mapFunc,res[:limit])
|
|
|
|
def processRefresh(self):
|
|
for refreshVar in self.refresh:
|
|
if not refreshVar.invalid:
|
|
refreshVar.refresh()
|
|
|
|
def ZipVars(self,*argvVarNames):
|
|
"""
|
|
Get zipped values of variables specified by list 'argvVarNames'
|
|
"""
|
|
return zip(*map(self.Get,argvVarNames))
|
|
|
|
def close(self):
|
|
for varObj in self.loadVariables.values():
|
|
if hasattr(varObj,"close"):
|
|
varObj.close()
|
|
elif hasattr(varObj,"value") and hasattr(varObj.value,"close"):
|
|
varObj.value.close()
|
|
|
|
def addGroup(self,groupName,normal=(),expert=(),brief=(),next_label=None,
|
|
brief_force=(), hide=(), expert_label=None,image='',
|
|
custom_buttons=()):
|
|
"""
|
|
Добавить группу переменных для метода
|
|
|
|
groupName - название группы
|
|
normal - регулярные настройки
|
|
expert - расширенные настройки
|
|
hide - кортеж имен переменных, которые необходимо скрыть в brief
|
|
image - изображение
|
|
brief - кортеж имен переменных, значение которых необходимо отобразите
|
|
в выводе-подтверждении
|
|
brief_force - кортеж имен переменных, значение которых необходимо
|
|
отобразите в выводе-подтверждении, даже если параметр является
|
|
несовместимым
|
|
expert_label - текст вместо стандартной надписи для отображения
|
|
расширенных параметров
|
|
next_label - название кнопки для запуска действия или перехода к
|
|
следующей группе
|
|
custom_buttons - список кортежей (название кнопки, текст на кнопке,
|
|
значение (метод), элемент ("button")
|
|
"""
|
|
default_next_label = _("Next")
|
|
default_expert_label = _("Click for advanced settings")
|
|
self.groups.append(
|
|
{'name':groupName,
|
|
'normal':normal,
|
|
'expert':expert,
|
|
'hide':hide,
|
|
'image':image,
|
|
'brief':brief,
|
|
'custom_buttons':custom_buttons,
|
|
'brief_force':brief_force,
|
|
'expert_label':expert_label or default_expert_label,
|
|
'next_label':next_label or default_next_label})
|
|
lastIndex = len(self.groups)-1
|
|
for var in chain(normal,expert):
|
|
self.mapVarGroup[var] = lastIndex
|
|
return self
|
|
|
|
def getGroups(self):
|
|
"""
|
|
Get groups variables
|
|
"""
|
|
return self.groups
|
|
|
|
def clearGroups(self):
|
|
"""
|
|
Clear group (using for drop translate)
|
|
"""
|
|
self.groups = []
|
|
self.mapVarGroup = OrderedDict()
|
|
|
|
def addBrief(self,next_label=None,text=None,image=""):
|
|
self.briefData["next"] = next_label
|
|
self.briefData["help"] = text
|
|
self.briefData["image"] = image
|
|
|
|
def getBrief(self):
|
|
return self.briefData
|
|
|
|
def _dependSort(self,inlist):
|
|
"""
|
|
Sort variable use "check_after" attribute
|
|
"""
|
|
moved = []
|
|
inlist = list(inlist)
|
|
while(inlist):
|
|
for i,el in enumerate(islice(inlist,1,None)):
|
|
if el.name in inlist[0].check_after:
|
|
inlist.insert(0,inlist.pop(i+1))
|
|
if inlist[0].name in moved:
|
|
raise VariableError("Loop depends for %s on check"%
|
|
(",".join(moved)))
|
|
moved.append(inlist[0].name)
|
|
break
|
|
else:
|
|
yield inlist.pop(0)
|
|
if inlist:
|
|
moved = [inlist[0].name]
|
|
|
|
def _nocheckSort(self,inlist):
|
|
"""
|
|
Place check variable without check (standard check method) at first
|
|
"""
|
|
afterlist = []
|
|
for i in inlist:
|
|
if not i.source and \
|
|
not i.check_after and \
|
|
Variable.check.__func__ == i.check.__func__ and \
|
|
Variable.uncompatible.__func__ == i.uncompatible.__func__:
|
|
yield i
|
|
else:
|
|
afterlist.append(i)
|
|
for i in afterlist:
|
|
yield i
|
|
|
|
def fixWsdlTableValue(self,varObj,val):
|
|
"""
|
|
Fix data get by wsdl (list-list) type
|
|
"""
|
|
if varObj.type == "table":
|
|
multiLists = filter(lambda x:"list-list" in x[1],
|
|
enumerate(
|
|
[self.getInfo(x).type for x in varObj.source]))
|
|
if multiLists:
|
|
val = list(izip_longest(*val,fillvalue=""))
|
|
for col,typecol in multiLists:
|
|
if col < len(val):
|
|
val[col] = map(lambda x:x.split(',')
|
|
if type(x) in StringTypes else x,
|
|
val[col])
|
|
val = list(izip_longest(*val,fillvalue=""))
|
|
return val
|
|
|
|
def plainList(self,*elements):
|
|
"""
|
|
Make plain list from many dimensions
|
|
"""
|
|
for w in elements:
|
|
if type(w.message) in (list,tuple):
|
|
for i in self.plainList(*w.message):
|
|
yield i
|
|
else:
|
|
yield w
|
|
|
|
def printGroup(self,info):
|
|
"""
|
|
Вывести группу переменных
|
|
"""
|
|
print "=DEBUG.SET="
|
|
for vname in sorted(filter(lambda x:x.lower() == x,
|
|
info._type_info.keys())):
|
|
print "|", vname,"=",getattr(info,vname)
|
|
print "=DEBUG.DATAVARS="
|
|
for vname in self.mapVarGroup.keys():
|
|
print "|", vname, "=", self.Get(vname)
|
|
print "=DEBUG.END="
|
|
|
|
def checkGroups(self,info,allvars=False):
|
|
"""
|
|
Check variables in group or all in info
|
|
"""
|
|
def compare_choice(part_s, *options):
|
|
lower_unicode = lambda x: _toUNICODE(x).lower()
|
|
part_s = lower_unicode(part_s)
|
|
options = [lower_unicode(x) for x in options]
|
|
return any(x == part_s for x in options)
|
|
|
|
if not self.groups:
|
|
return []
|
|
errors = []
|
|
if self.groups:
|
|
keys = self.mapVarGroup.keys()
|
|
else:
|
|
keys = sorted(filter(lambda x:x.lower() == x,
|
|
info._type_info.keys()))
|
|
varsByKeys = map(self.getInfo, keys)
|
|
groupVars = []
|
|
|
|
# invalidate default variables
|
|
if hasattr(info,"Default") and info.Default:
|
|
default = info.Default
|
|
for varname in default:
|
|
self.Invalidate(varname,onlySet=True)
|
|
else:
|
|
default = []
|
|
# look over all vars
|
|
for var,varObj in map(lambda x:(x.name,x),
|
|
self._nocheckSort(self._dependSort(varsByKeys))):
|
|
# if info skipped and send None
|
|
if info is None:
|
|
val = None
|
|
else:
|
|
# if variable not exists in info data - skip it
|
|
if not hasattr(info,var):
|
|
continue
|
|
# get value of variable from info
|
|
val = getattr(info,var)
|
|
varSendFromClient = not val is None and not var in default
|
|
varGroupUntrusted = varObj.untrusted and var in groupVars
|
|
varAllUntrusted = varObj.untrusted and allvars
|
|
# if group variable is not defined and
|
|
# variable send from client
|
|
# datavars has groups of variables
|
|
if not groupVars and varSendFromClient and self.groups:
|
|
# get normal and expert variables from group
|
|
groupIndex = self.mapVarGroup[var]
|
|
groupVars = list(self.groups[groupIndex]["normal"]) + \
|
|
list(self.groups[groupIndex]["expert"])
|
|
if varSendFromClient or varGroupUntrusted or varAllUntrusted \
|
|
or not info:
|
|
try:
|
|
uncomperr = self.Uncompatible(var)
|
|
# get value for final or group check
|
|
if val is None:
|
|
if not uncomperr:
|
|
self.Check(var,self.Get(var))
|
|
else:
|
|
val = self.fixWsdlTableValue(varObj,val)
|
|
if varObj.type in ("choice","choice-list"):
|
|
choicedata = self.ChoiceAndComments(var)
|
|
if any(choicedata):
|
|
if choicedata[1] is None:
|
|
choicedata = [choicedata[0],
|
|
choicedata[0]]
|
|
choicedata = zip(*choicedata)
|
|
if varObj.type == "choice":
|
|
vals = [val]
|
|
else:
|
|
vals = val
|
|
res = []
|
|
for val in vals:
|
|
if not val in map(lambda x:x[0],choicedata):
|
|
result = filter(lambda x:compare_choice(val, x[0], x[1]),
|
|
choicedata)
|
|
if len(result) > 1:
|
|
raise VariableError(_("Ambiguous choice:%s")%
|
|
formatListOr(map(
|
|
lambda x:'"%s"(%s)'%(x[1],x[0]) if x[0] != x[1] else x[0],
|
|
result)))
|
|
elif result:
|
|
val = result[0][0]
|
|
res.append(val)
|
|
if varObj.type == "choice":
|
|
val = res[0]
|
|
else:
|
|
val = res
|
|
self.Set(var,val)
|
|
# raise error for atempt set uncompatible variable
|
|
if uncomperr:
|
|
raise VariableError(uncomperr)
|
|
except (VariableError,DataVarsError) as e:
|
|
# assemble all variable errors
|
|
messages = e.message if type(e.message) == list \
|
|
else [e]
|
|
mess = "\n".join(map(lambda x:str(x),self.plainList(*messages)))
|
|
mapError = {PasswordError:'pwderror',
|
|
CommonVariableError:'commonerror'}
|
|
for k,v in mapError.items():
|
|
if isinstance(e,k) or \
|
|
type(e.message) == list and \
|
|
all(isinstance(x,k) for x in e.message):
|
|
typeError = v
|
|
break
|
|
else:
|
|
typeError = 'error'
|
|
errors.append({'type':typeError, 'field':var,
|
|
'field_obj': self.getInfo(var) if var else "",
|
|
'message':mess})
|
|
except BaseException as e:
|
|
for i in apply(traceback.format_exception, sys.exc_info()):
|
|
print i,
|
|
errors.append({'type':'error', 'field':var,
|
|
'message':str(e)})
|
|
return errors
|
|
|
|
def printDependence(self):
|
|
for key,val in self.loadVariables.items():
|
|
if val.reqVars:
|
|
print key,"->",[x.name for x in val.reqVars]
|
|
|
|
def printVars(self,filt=lambda x:x):
|
|
for i in sorted(filter(filt,self.allVars.keys())):
|
|
print "{0:<40} {1}".format(i,self.Get(i,True))
|
|
|
|
def reinit(self):
|
|
"""
|
|
Using for update variable translatable elements (example: label)
|
|
"""
|
|
for key,var in self.loadVariables.items():
|
|
var.init()
|
|
|
|
def printWrong(self,filt=lambda x:x):
|
|
print "!!!WRONG VARS!!!"
|
|
for i in sorted(filter(filt,self.allVars.keys())):
|
|
if self.getInfo(i).untrusted:
|
|
print "{0:<40} {1}".format(i,self.Get(i,True))
|