|
|
|
@ -78,11 +78,12 @@ class ReadonlyType(VariableType):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Dependence:
|
|
|
|
|
class DependenceSource:
|
|
|
|
|
current_namespace = None
|
|
|
|
|
datavars_root = None
|
|
|
|
|
|
|
|
|
|
def __init__(self, *variables, depend=None):
|
|
|
|
|
def __init__(self, variables, depend=None):
|
|
|
|
|
self.error = None
|
|
|
|
|
self._subscriptions = variables
|
|
|
|
|
self.depend_function = depend
|
|
|
|
|
|
|
|
|
@ -138,43 +139,20 @@ class Dependence:
|
|
|
|
|
# Проверяем совпадение количества аргументов функции и заданных для
|
|
|
|
|
# функции переменных.
|
|
|
|
|
function_signature = signature(function_to_check)
|
|
|
|
|
print('subscribers: {}'.format(self._subscriptions))
|
|
|
|
|
if not len(self._subscriptions) == len(function_signature.parameters):
|
|
|
|
|
raise VariableError("the depend function takes {} arguments,"
|
|
|
|
|
" while {} is given".format(
|
|
|
|
|
len(function_signature.parameters),
|
|
|
|
|
len(self._subscriptions)))
|
|
|
|
|
|
|
|
|
|
def _get_variables(self, variables_names):
|
|
|
|
|
print('current_namespace: {}'.format(self.current_namespace.fullname))
|
|
|
|
|
print('root:')
|
|
|
|
|
for namespace_name, namespace in self.datavars_root:
|
|
|
|
|
print(namespace_name)
|
|
|
|
|
|
|
|
|
|
variables = list()
|
|
|
|
|
for variable_name in variables_names:
|
|
|
|
|
result = None
|
|
|
|
|
name_parts = variable_name.split('.')
|
|
|
|
|
if not name_parts[0]:
|
|
|
|
|
namespace = self.current_namespace
|
|
|
|
|
index = 1
|
|
|
|
|
while not name_parts[index]:
|
|
|
|
|
namespace = namespace.parent
|
|
|
|
|
index += 1
|
|
|
|
|
name_parts = name_parts[index:]
|
|
|
|
|
else:
|
|
|
|
|
namespace = self.datavars_root
|
|
|
|
|
result = namespace
|
|
|
|
|
for part in name_parts:
|
|
|
|
|
result = result[part]
|
|
|
|
|
variables.append(result)
|
|
|
|
|
|
|
|
|
|
return variables
|
|
|
|
|
|
|
|
|
|
def __ne__(self, other):
|
|
|
|
|
if not isinstance(other, DependenceSource):
|
|
|
|
|
return True
|
|
|
|
|
return not self == other
|
|
|
|
|
|
|
|
|
|
def __eq__(self, other):
|
|
|
|
|
if not isinstance(other, Dependence):
|
|
|
|
|
if not isinstance(other, DependenceSource):
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
if not self._compare_depend_functions(self.depend_function,
|
|
|
|
@ -205,6 +183,7 @@ class Dependence:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableNode:
|
|
|
|
|
'''Класс ноды соответствующей переменной в дереве переменных.'''
|
|
|
|
|
def __init__(self, name, namespace, variable_type=StringType, source=None):
|
|
|
|
|
self.name = name
|
|
|
|
|
if issubclass(variable_type, VariableType):
|
|
|
|
@ -228,6 +207,8 @@ class VariableNode:
|
|
|
|
|
self.update_value()
|
|
|
|
|
|
|
|
|
|
def update_value(self):
|
|
|
|
|
'''Метод для обновления значения переменной с помощью указанного
|
|
|
|
|
источника ее значения.'''
|
|
|
|
|
if self.calculating:
|
|
|
|
|
raise CyclicVariableError(self.name)
|
|
|
|
|
|
|
|
|
@ -235,7 +216,7 @@ class VariableNode:
|
|
|
|
|
raise VariableError("No sources to update variable '{}'".
|
|
|
|
|
format(self.fullname))
|
|
|
|
|
|
|
|
|
|
if isinstance(self._source, Dependence):
|
|
|
|
|
if isinstance(self._source, DependenceSource):
|
|
|
|
|
with self._start_calculate():
|
|
|
|
|
try:
|
|
|
|
|
self.value = self._source.calculate_value()
|
|
|
|
@ -253,7 +234,7 @@ class VariableNode:
|
|
|
|
|
if self._source != source:
|
|
|
|
|
self._invalidate()
|
|
|
|
|
self._source = source
|
|
|
|
|
if isinstance(source, Dependence):
|
|
|
|
|
if isinstance(source, DependenceSource):
|
|
|
|
|
for subscription in source._subscriptions:
|
|
|
|
|
subscription.subscribers.add(self)
|
|
|
|
|
|
|
|
|
@ -293,6 +274,7 @@ class VariableNode:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class NamespaceNode:
|
|
|
|
|
'''Класс ноды соответствующей пространству имен в дереве переменных.'''
|
|
|
|
|
def __init__(self, name='', parent=None):
|
|
|
|
|
self.name = name
|
|
|
|
|
self.variables = dict()
|
|
|
|
@ -300,10 +282,12 @@ class NamespaceNode:
|
|
|
|
|
self.parent = parent
|
|
|
|
|
|
|
|
|
|
def add_variable(self, variable: VariableNode):
|
|
|
|
|
'''Метод для добавления переменной в пространство имен.'''
|
|
|
|
|
self.variables.update({variable.name: variable})
|
|
|
|
|
variable.namespace = self
|
|
|
|
|
|
|
|
|
|
def add_namespace(self, namespace):
|
|
|
|
|
'''Метод для добавления пространства имен в пространство имен.'''
|
|
|
|
|
self.namespaces.update({namespace.name: namespace})
|
|
|
|
|
namespace.parent = self
|
|
|
|
|
|
|
|
|
@ -339,12 +323,54 @@ class NamespaceNode:
|
|
|
|
|
namespace_name=self.name))
|
|
|
|
|
|
|
|
|
|
def __contains__(self, name):
|
|
|
|
|
return name in self.childs or name in self.variables
|
|
|
|
|
return name in self.namespaces or name in self.variables
|
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
|
return '<Namespace: {}>'.format(self.fullname)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DependenceAPI:
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self.current_namespace = None
|
|
|
|
|
self.datavars_root = None
|
|
|
|
|
|
|
|
|
|
def __call__(self, *variables, depend=None):
|
|
|
|
|
subscriptions = list()
|
|
|
|
|
for variable in variables:
|
|
|
|
|
if isinstance(variable, str):
|
|
|
|
|
variable = self._find_variable(variable)
|
|
|
|
|
if variable is None:
|
|
|
|
|
raise DependenceError("variable '{}' not found".format(
|
|
|
|
|
variable))
|
|
|
|
|
elif not isinstance(variable, VariableNode):
|
|
|
|
|
raise DependenceError("dependence variables must be 'str' or"
|
|
|
|
|
" 'VariableNode' not '{}'".format(
|
|
|
|
|
type(variable)))
|
|
|
|
|
subscriptions.append(variable)
|
|
|
|
|
return DependenceSource(subscriptions, depend=depend)
|
|
|
|
|
|
|
|
|
|
def _find_variable(self, variable_name):
|
|
|
|
|
'''Метод для поиска переменной в пространстве'''
|
|
|
|
|
variable = None
|
|
|
|
|
name_parts = variable_name.split('.')
|
|
|
|
|
|
|
|
|
|
if not name_parts[0]:
|
|
|
|
|
namespace = self.current_namespace
|
|
|
|
|
index = 1
|
|
|
|
|
while not name_parts[index]:
|
|
|
|
|
namespace = namespace.parent
|
|
|
|
|
index += 1
|
|
|
|
|
name_parts = name_parts[index:]
|
|
|
|
|
else:
|
|
|
|
|
namespace = self.datavars_root
|
|
|
|
|
|
|
|
|
|
variable = namespace
|
|
|
|
|
for part in name_parts:
|
|
|
|
|
variable = variable[part]
|
|
|
|
|
|
|
|
|
|
return variable
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableAPI:
|
|
|
|
|
'''Класс для создания переменных при задании их через
|
|
|
|
|
python-скрипты.'''
|
|
|
|
@ -360,15 +386,16 @@ class VariableAPI:
|
|
|
|
|
else:
|
|
|
|
|
variable = self.current_namespace[name]
|
|
|
|
|
|
|
|
|
|
if isinstance(source, Dependence):
|
|
|
|
|
if isinstance(source, DependenceSource):
|
|
|
|
|
try:
|
|
|
|
|
source.check()
|
|
|
|
|
variable.source = source
|
|
|
|
|
except DependenceError as error:
|
|
|
|
|
self.errors.append('Dependence error: {} in variable: {}'.
|
|
|
|
|
raise VariableError('Dependence error: {} in variable: {}'.
|
|
|
|
|
format(str(error), variable.fullname))
|
|
|
|
|
else:
|
|
|
|
|
variable.source = source
|
|
|
|
|
|
|
|
|
|
return variable
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -376,14 +403,19 @@ class NamespaceAPI:
|
|
|
|
|
'''Класс для создания пространств имен при задании переменных через
|
|
|
|
|
python-скрипты.'''
|
|
|
|
|
def __init__(self, var_fabric: VariableAPI,
|
|
|
|
|
dependence_fabric: DependenceAPI(),
|
|
|
|
|
datavars_root=NamespaceNode('<root>')):
|
|
|
|
|
self._datavars = datavars_root
|
|
|
|
|
self._variables_fabric = var_fabric
|
|
|
|
|
self.namespace_stack = [datavars_root]
|
|
|
|
|
|
|
|
|
|
# Привязываем фабрику переменных.
|
|
|
|
|
self._variables_fabric = var_fabric
|
|
|
|
|
self._variables_fabric.current_namespace = self._datavars
|
|
|
|
|
|
|
|
|
|
Dependence.current_namespace = self._datavars
|
|
|
|
|
Dependence.datavars_root = self._datavars
|
|
|
|
|
# Привязываем фабрику зависимостей.
|
|
|
|
|
self._dependence_fabric = dependence_fabric
|
|
|
|
|
self._dependence_fabric.current_namespace = self._datavars
|
|
|
|
|
self._dependence_fabric.datavars_root = self._datavars
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def datavars(self):
|
|
|
|
@ -402,9 +434,9 @@ class NamespaceAPI:
|
|
|
|
|
'''Метод для сброса корневого пространства имен.'''
|
|
|
|
|
self._datavars = NamespaceNode('<root>')
|
|
|
|
|
self.namespace_stack = [self._datavars]
|
|
|
|
|
self._variables_fabric.current_namespace = self._datavars
|
|
|
|
|
|
|
|
|
|
Dependence.current_namespace = self._datavars
|
|
|
|
|
self._variables_fabric.current_namespace = self._datavars
|
|
|
|
|
self._dependence_fabric.current_namespace = self._datavars
|
|
|
|
|
|
|
|
|
|
@contextmanager
|
|
|
|
|
def __call__(self, namespace_name):
|
|
|
|
@ -417,17 +449,21 @@ class NamespaceAPI:
|
|
|
|
|
|
|
|
|
|
self.namespace_stack[-1].add_namespace(namespace)
|
|
|
|
|
self.namespace_stack.append(namespace)
|
|
|
|
|
# Устанавливаем текущее пространство имен фабрике переменных.
|
|
|
|
|
self._variables_fabric.current_namespace = namespace
|
|
|
|
|
|
|
|
|
|
Dependence.datavars_root = self._datavars
|
|
|
|
|
Dependence.current_namespace = namespace
|
|
|
|
|
# Устанавливаем текущее пространство имен фабрике зависимостей.
|
|
|
|
|
self._dependence_fabric.current_namespace = namespace
|
|
|
|
|
print('current_namespace in DependenceAPI: {}'.format(
|
|
|
|
|
namespace.fullname))
|
|
|
|
|
try:
|
|
|
|
|
yield self
|
|
|
|
|
finally:
|
|
|
|
|
current_namespace = self.namespace_stack.pop()
|
|
|
|
|
self._variables_fabric.current_namespace = current_namespace
|
|
|
|
|
Dependence.current_namespace = current_namespace
|
|
|
|
|
self._dependence_fabric.current_namespace = current_namespace
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Dependence = DependenceAPI()
|
|
|
|
|
Variable = VariableAPI()
|
|
|
|
|
Namespace = NamespaceAPI(Variable)
|
|
|
|
|
Namespace = NamespaceAPI(Variable, Dependence)
|
|
|
|
|