The Namespace filler class is not inherited from the CalculateIni class.

packages
Иванов Денис 4 years ago
parent f879ea0ae2
commit f8f1bad832

@ -12,6 +12,10 @@ class CalculateIniParser:
remove = 2
def __init__(self):
self.operations = {"=": self.Define.assign,
"+=": self.Define.append,
"-=": self.Define.remove}
lbrack = Literal("[")
rbrack = Literal("]")
@ -64,48 +68,23 @@ class CalculateIniParser:
разбора.'''
error_line = line(location, string).strip()
if error_line:
self.parse_error(error_line, lineno(location, string),
col(location, string))
return (error_line, lineno(location, string),
col(location, string))
def parse(self, data):
for tokens, start, end in self.ini_section_parser.scanString(data):
if tokens.getName() == "error":
continue
print('error tokens: {}'.format(tokens))
yield {'error': tokens}
section, defkeys = tokens
section_list = section.asList()
if section_list[-1] == []:
self.clear_section(section_list[:-1])
continue
yield {'clear_section': (section_list[:-1], )}
self.start_section(section.asList())
for defkey in defkeys:
if defkey.getName() == "error":
continue
mapOp = {"=": self.Define.assign,
"+=": self.Define.append,
"-=": self.Define.remove}
self.define_key(section.asList(),
defkey[0], defkey[2],
mapOp[defkey[1]])
def start_section(self, section):
"""Начало секции"""
pass
def clear_section(self, section):
'''Метод для очистки секций.'''
pass
def define_key(self, section, defkey, defval, deftype):
'''Метод для определения ключа.
['section','block'], 'varname', 'varval', Define.assign'''
pass
def parse_error(self, line, lineno, col):
'''Метод для обработки ошибок, обнаруженных в ini-файле.
line - пример строки;
lineno - номер строки;
col - номер символа.'''
pass
yield {'define_key': (section.asList(), defkey[0], defkey[2],
self.operations[defkey[1]])}

@ -41,19 +41,19 @@ class CyclicVariableError(VariableError):
self.queue[:-1]))
class VariableProperty:
class VariableType:
'''Базовый класс для объектов свойств.'''
def __init__(self, parent):
self.parent = parent
class StringVariable(VariableProperty):
class StringVariable(VariableType):
'''Класс свойства, соответствующий переменным просто хранящим строки как
значения.'''
pass
class ListVariable(VariableProperty):
class ListVariable(VariableType):
'''Класс свойства, соответствующий переменным хранящим списки значений.'''
def set_value(self, value, force=False):
if isinstance(value, (list, tuple)):
@ -69,7 +69,8 @@ class ListVariable(VariableProperty):
return value
class ReadonlyVariable(VariableProperty):
class ReadonlyVariable(VariableType):
'''Класс свойства, соответствующий переменным только для чтения.'''
def set_value(self, value, force=False):
if not force:
raise VariableError(
@ -78,7 +79,8 @@ class ReadonlyVariable(VariableProperty):
return value
class IntegerVariable(VariableProperty):
class IntegerVariable(VariableType):
'''Класс свойства, соответствующий целочисленным переменным.'''
re_match = re.compile(r"^-?\d+$")
def check(self, value):
@ -91,12 +93,14 @@ class IntegerVariable(VariableProperty):
return int(value)
class BooleanVariable(VariableProperty):
class BooleanVariable(VariableType):
'''Класс свойства, соответствующий логическим переменным.'''
def post_get(self, value):
return value == "true"
class ChoiceVariable(VariableProperty):
class ChoiceVariable(VariableType):
'''Класс свойства, соответствующий переменным выбора.'''
def check(self, value):
choices = self.parent.choice()
if value and value not in choices:
@ -116,18 +120,20 @@ class ChoiceVariable(VariableProperty):
return [(x, x) for x in self.parent.choice()]
class DefaultValue(VariableProperty):
class DefaultValue(VariableType):
'''Класс свойства, соответствующий логическим переменным.'''
def __init__(self, value):
self.value = value
self.parent = None
def get_value(self, invalidate_subs=None):
def get_value(self, subscribers=None):
if self.parent._value is None:
self.parent._unsubscribe_depends()
self.parent._value = self.value
class IniCreated(DefaultValue):
'''Класс свойства, соответствующий переменным, полученным из ini-файла.'''
pass
@ -151,8 +157,10 @@ class Variable(BaseClass):
self.name = name
self._value = None
self.vars = None
self.invalidate_subs = set()
self.depends = set()
self.subscribers = set()
# множество переменных, на которые подписана данная переменная.
self.subscriptions = set()
self.calculating = False
self._properties = [x(self) for x in self.properties]
# версия значения переменной, если переменная со времен получения
@ -163,94 +171,109 @@ class Variable(BaseClass):
'''Метод для получения полного имени переменной.'''
return "{}.{}".format(self.vars.get_fullname(), self.name)
def addProperty(self, prop):
def add_property(self, prop):
'''Метод для подключения свойств к переменной.'''
prop.parent = self
self._properties.insert(0, prop)
def findProperty(self, propclass):
for prop in self._properties:
if isinstance(prop, propclass):
return prop
def find_property(self, property_class):
'''Метод для проверки наличия заданного свойства у переменной.'''
for _property in self._properties:
if isinstance(_property, property_class):
return _property
else:
return None
def _emit_invalidate(self, slots):
for f in slots:
def _emit_invalidate(self, subscribers):
print('subscribers = {}'.format(subscribers))
for f in subscribers:
print('f = {}'.format(f))
f()
def _unsubscribe_depends(self):
for dependvar in self.depends:
dependvar.update_unsubscribe(self.invalidate)
self.depends = set()
for subscription in self.subscriptions:
subscription.update_unsubscribe(self.invalidate)
self.subscriptions = set()
def update_subscribe(self, f_or_var):
if isinstance(f_or_var, Variable):
f_or_var.depends.add(self)
self.invalidate_subs.add(f_or_var.invalidate)
f_or_var.subscriptions.add(self)
self.subscribers.add(f_or_var.invalidate)
else:
self.invalidate_subs.add(f_or_var)
self.subscribers.add(f_or_var)
def update_unsubscribe(self, f):
if f in self.invalidate_subs:
self.invalidate_subs.remove(f)
if f in self.subscribers:
self.subscribers.remove(f)
def invalidate(self):
'''Метод для инвалидации всех переменных-подписчиков.'''
print('invalidation method')
print('subscribers = {}'.format(self.subscribers))
print('subscriptions = {}'.format(self.subscriptions))
self._value = None
if self.invalidate_subs:
slots = self.invalidate_subs
self.invalidate_subs = set()
self._emit_invalidate(slots)
if self.subscribers:
subscribers = self.subscribers
self.subscribers = set()
self._emit_invalidate(subscribers)
def set_parent(self, namespace):
'''Метод для установки родительского пространства имен переменной.'''
self.vars = namespace
@contextmanager
def _start_calculate(self):
'''Менеджер контекста для запуска расчета переменных с учетом всех
взаимозависимостей.'''
try:
self.calculating = True
yield self
finally:
self.calculating = False
def get_value(self, invalidate_sub=None):
for f in self.call_properties("get_value"):
f(invalidate_sub)
def get_value(self, subscription=None):
for get_method in self.call_properties("get_value"):
get_method(subscription)
if self.calculating:
raise CyclicVariableError(self.name)
if self._value is None:
with self._start_calculate():
try:
self._unsubscribe_depends()
self._value = self.get()
if isinstance(self._value, types.GeneratorType):
self._value = list(self._value)
except CyclicVariableError as e:
raise CyclicVariableError(self.name, *e.queue)
except CyclicVariableError as error:
raise CyclicVariableError(self.name, *error.queue)
if invalidate_sub is not None:
self.update_subscribe(invalidate_sub)
if subscription is not None:
self.update_subscribe(subscription)
return self.post_get(self._value)
def post_get(self, value):
for f in self.call_properties("post_get"):
ret = f(value)
if ret is not None:
return ret
for post_get_method in self.call_properties("post_get"):
result = post_get_method(value)
if result is not None:
return result
return value
def call_properties(self, method_name, *args):
'''Метод для вызова указанного метода у всех объектов, которыми владеет
переменная.'''
for _property in self._properties:
method = getattr(_property, method_name, None)
if method:
yield method
property_method = getattr(_property, method_name, None)
if property_method:
yield property_method
def set_value(self, value, force=False):
'''Метод для установки некоторого заданного значения всем объектам,
принадлежащим переменной.'''
for setter in self.call_properties("set_value"):
value = setter(value, force)
value = self.set(value)
self.check(value)
self.invalidate()
@ -268,17 +291,17 @@ class Variable(BaseClass):
'''Метод для заполнения переменной.'''
return self.value
def get_comment_value(self, invalidate_sub=None):
def get_comment_value(self, subscription=None):
'''Этот метод вызывается внутри методов get.'''
val = self.get_comment()
if invalidate_sub is not None:
self.update_subscribe(invalidate_sub)
if subscription is not None:
self.update_subscribe(subscription)
return val
def get_comment(self):
'''Метод для установки .'''
for f in self.call_properties("get_comment"):
return f()
for get_method in self.call_properties("get_comment"):
return get_method()
return self.get_value()
def set(self, value):
@ -409,8 +432,8 @@ class HashVariable(Namespace):
self.parent = None
self.master_variable = master_variable
def get_value(self, invalidate_sub=None):
return self.master_variable.getHashValue(self.name, invalidate_sub)
def get_value(self, subscription=None):
return self.master_variable.getHashValue(self.name, subscription)
def set_value(self, value, force=False):
return self.master_variable.setHashValue(self.name, value, force)
@ -421,8 +444,8 @@ class HashVariable(Namespace):
class Data(Variable):
BASE_CLASS = "Data"
def getHashValue(self, name, invalidate_sub=None):
return self.get_value(invalidate_sub)[name]
def getHashValue(self, name, subscription=None):
return self.get_value(subscription)[name]
def setHashValue(self, name, value, force):
if name in self.readonly_vars and not force:
@ -433,8 +456,8 @@ class HashVariable(Namespace):
data[name] = value
self.set_value(data, force)
def get_value(self, invalidate_sub=None):
return self.master_variable.get_value(invalidate_sub)
def get_value(self, subscription=None):
return self.master_variable.get_value(subscription)
hash_vars = []
readonly_vars = []
@ -475,10 +498,10 @@ class TableVariable(Namespace):
self.master_variable._index = index
class Data(HashVariable.Data):
def getHashValue(self, name, invalidate_sub=None):
def getHashValue(self, name, subscription=None):
return self.vars.master_variable.getTableValue(name,
self._index,
invalidate_sub)
subscription)
def setHashValue(self, name, value, force):
self.vars.master_variable.setTableValue(name, self._index,
@ -487,8 +510,8 @@ class TableVariable(Namespace):
class Data(Variable):
BASE_CLASS = "Data"
def getTableValue(self, name, index, invalidate_sub=None):
data = self.get_value(invalidate_sub)
def getTableValue(self, name, index, subscription=None):
data = self.get_value(subscription)
return data[index][name]
def setTableValue(self, name, index, value, force):
@ -517,8 +540,8 @@ class TableVariable(Namespace):
def childs(self, value):
self._childs = value
def get_value(self, invalidate_sub=None):
return self.master_variable.get_value(invalidate_sub)
def get_value(self, subscription=None):
return self.master_variable.get_value(subscription)
def set_value(self, value, force=False):
self.master_variable.set_value(value, force)

@ -12,131 +12,150 @@ from calculate.utils.fs import readFile
from calculate.utils.files import list_directory
class NamespaceIniFiller(CalculateIniParser):
class NamespaceIniFiller:
'''Класс, предназначенный для наполнения Namespace объекта переменными
из calculate.ini файла.'''
def error(self, lineno, s):
self.errors.append(lineno, s)
def __init__(self):
self.ini_parser = CalculateIniParser()
def fill(self, ns, data):
self.ns = ns
self.curns = self.ns
def error(self, lineno, error_message):
self.errors.append(lineno, error_message)
def fill(self, namespace, ini_file_text):
self.namespace = namespace
self.current_namespace = self.namespace
self.errors = []
self.parse(data)
for parsed_line in self.ini_parser.parse(ini_file_text):
self._line_processor(**parsed_line)
def _line_processor(self, start_section=None,
clear_section=None,
define_key=None,
error=None, **kwargs):
if start_section is not None:
self.start_section(*start_section)
elif clear_section is not None:
self.clear_section(*clear_section)
elif define_key is not None:
self.define_key(*define_key)
elif error is not None:
self.set_error(*error)
def start_section(self, sections):
self.curns = self.ns
self.current_namespace = self.namespace
for section in sections:
if section not in self.curns.childs:
self.curns.add_namespace(Namespace(section))
self.curns = self.curns[section]
if section not in self.current_namespace.childs:
self.current_namespace.add_namespace(Namespace(section))
self.current_namespace = self.current_namespace[section]
def clear_section(self, sections):
curns = self.ns
current_namespace = self.namespace
for section in sections:
if section not in curns.childs:
if section not in current_namespace.childs:
return
curns = curns[section]
curns.clear_childs()
current_namespace = current_namespace[section]
current_namespace.clear_childs()
def change_value(self, key, value):
self.curns[key].set_value(value)
self.current_namespace[key].set_value(value)
def define_variable(self, key, value):
self.curns.add_string_variable(key, value)
self.current_namespace.add_string_variable(key, value)
def append_value(self, key, value):
l = self.curns[key].get_value().split(",")
line = self.current_namespace[key].get_value().split(",")
vlist = value.split(",")
for v in vlist:
if v not in l:
l.append(v)
self.change_value(key, ",".join(l))
if v not in line:
line.append(v)
self.change_value(key, ",".join(line))
def remove_value(self, key, value):
l = self.curns[key].get_value().split(",")
line = self.current_namespace[key].get_value().split(",")
vlist = value.split(",")
for v in vlist:
if v in l:
l.remove(v)
self.change_value(key, ",".join(l))
if v in line:
line.remove(v)
self.change_value(key, ",".join(line))
def define_key(self, section, key, value, optype):
Define = CalculateIniParser.Define
if optype == Define.assign:
if key not in self.curns:
if key not in self.current_namespace:
self.define_variable(key, value)
else:
self.change_value(key, value)
elif optype == Define.append:
if key not in self.curns:
if key not in self.current_namespace:
self.define_variable(key, value)
else:
self.append_value(key, value)
elif optype == Define.remove:
if key not in self.curns:
if key not in self.current_namespace:
self.define_variable(key, value)
else:
self.remove_value(key, value)
def parse_error(self, line, lineno, col):
self.error(lineno, _("Syntax error: %s") % line)
def set_error(self, line, lineno, col):
self.error(lineno, "Syntax error: %s".format(line))
class NamespaceIniFillerStrict(NamespaceIniFiller):
"""
Объект используемый для наполения Namespace объекта переменными
из calculate.ini файла, с возможность определять только
из calculate.ini файла, с возможность определять только
"""
availableSection = ["custom"]
def fill(self, ns, data):
def fill(self, namespace, data):
self.canCreate = False
for newns in self.availableSection:
if newns not in ns:
ns.add_namespace(Namespace(newns))
super().fill(ns, data)
if newns not in namespace:
namespace.add_namespace(Namespace(newns))
super().fill(namespace, data)
def start_section(self, sections):
self.curns = self.ns
self.current_namespace = self.namespace
self.canCreate = sections[0] in self.availableSection
for section in sections:
if section not in self.curns.childs:
if isinstance(self.curns, TableVariable) or self.canCreate:
self.curns.add_namespace(Namespace(section))
if section not in self.current_namespace.childs:
if isinstance(self.current_namespace,
TableVariable) or self.canCreate:
self.current_namespace.add_namespace(Namespace(section))
else:
self.curns = None
self.curns = self.curns[section]
self.current_namespace = None
self.current_namespace = self.current_namespace[section]
# def clear_section(self, sections):
# curns = self.ns
# current_namespace = self.namespace
# self.canCreate = sections[0] in self.availableSection
# for section in sections:
# if section not in curns.childs:
# if section not in current_namespace.childs:
# return
# curns = curns[section]
# curns.clear_childs()
# current_namespace = current_namespace[section]
# current_namespace.clear_childs()
def define_variable(self, key, value):
if not self.canCreate:
pass
var = Variable(key)
var.addProperty(IniCreated(value))
self.curns.add_variable(var)
var.add_property(IniCreated(value))
self.current_namespace.add_variable(var)
def change_value(self, key, value):
var = self.curns[key]
var = self.current_namespace[key]
var.set_value(value)
if isinstance(var, HashVariable.HashValue):
var = var.master_variable
value = var.get_value()
prop = var.findProperty(DefaultValue)
prop = var.find_property(DefaultValue)
if prop:
prop.value = value
else:
var.addProperty(DefaultValue(value))
var.add_property(DefaultValue(value))
var.invalidate()
@ -150,22 +169,23 @@ class VariableLoader:
yield self.re_upper.sub(r"\1_\2", attrname).lower(), \
getattr(obj, attrname)
def fill(self, ns, dirpath, package):
def fill(self, namespace, dirpath, package):
'''Загрузить в namespace переменные из указанных модулей.'''
for fullfn in list_directory(dirpath, fullpath=True):
dn, fn = os.path.split(fullfn)
if os.path.isdir(fullfn):
newns = ns.add_namespace(Namespace(fn))
newns = namespace.add_namespace(Namespace(fn))
self.fill(newns, fullfn, "{}.{}".format(package, fn))
elif fn.endswith(".py"):
module = self._load_module_source(package, fn, fullfn)
for varname, cls in self._get_varlike_attrs(module):
if Variable.is_implementation(cls):
ns.add_variable(cls(varname))
namespace.add_variable(cls(varname))
elif HashVariable.is_implementation(cls) or \
TableVariable.is_implementation(cls) or \
Namespace.is_implementation(cls):
_newns = ns.add_namespace(cls(varname, ns))
_newns = namespace.add_namespace(cls(varname,
namespace))
for _varname, _cls in self._get_varlike_attrs(cls):
if Variable.is_implementation(_cls):
_newns.add_variable(_cls(_varname))
@ -199,14 +219,15 @@ class ProfileFiller:
Заполнитель значений переменных из файлов calculate.ini в профилях
"""
basename = "calculate.ini"
def get_repository_map(self, ns):
def get_repository_map(self, namespace):
return {
x.name.get_value(): x.path.get_value()
for x in ns.os.gentoo.repositories
for x in namespace.os.gentoo.repositories
}
def fill(self, ns, profile_path):
nif = NamespaceIniFillerStrict()
pw = ProfileWalker(self.basename, self.get_repository_map(ns))
def fill(self, namespace, profile_path):
nif = NamespaceIniFillerStrict()
pw = ProfileWalker(self.basename, self.get_repository_map(namespace))
for fn in pw.find(profile_path):
nif.fill(ns, readFile(fn))
nif.fill(namespace, readFile(fn))

@ -437,7 +437,7 @@ class TestNamespace:
assert test1.get_value() == "123"
test1.set_value("987")
test1.addProperty(DefaultValue("567"))
test1.add_property(DefaultValue("567"))
assert test1.get_value() == "987"
test1.invalidate()
@ -476,6 +476,7 @@ class TestNamespace:
assert test3.get_value() == "ZXC [234]"
test2.set_value("567")
assert test3.get_value() == "ZXC [567]"
assert False
def test_wrong_choice_varaible(self):
class TestVar1(Variable):

@ -3,4 +3,4 @@ from calculate.vars.datavars import Variable
class Vargetter(Variable):
def get(self):
return "%s test".format(self.vars.root.main.chroot.get_value(self))
return "{} test".format(self.vars.root.main.chroot.get_value(self))

Loading…
Cancel
Save