|
|
|
@ -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)
|
|
|
|
|