From d7048baecd45dc7f15929b09194adea9714daf61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=B2=D0=B0=D0=BD=D0=BE=D0=B2=20=D0=94=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=81?= Date: Thu, 23 Jul 2020 18:22:52 +0300 Subject: [PATCH] Works with errors. Some changes to the dependencies and interface architecture and errors processing. --- calculate/vars/alt_datavars.py | 124 +++++++++++++++++++++------------ tests/vars/test_alt_vars.py | 74 +++++++++----------- 2 files changed, 114 insertions(+), 84 deletions(-) diff --git a/calculate/vars/alt_datavars.py b/calculate/vars/alt_datavars.py index e3db5f4..fe3b898 100644 --- a/calculate/vars/alt_datavars.py +++ b/calculate/vars/alt_datavars.py @@ -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 ''.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: {}'. - format(str(error), variable.fullname)) + 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('')): 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('') 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) diff --git a/tests/vars/test_alt_vars.py b/tests/vars/test_alt_vars.py index a5a1f54..e0e9f23 100644 --- a/tests/vars/test_alt_vars.py +++ b/tests/vars/test_alt_vars.py @@ -51,6 +51,28 @@ class TestNamespace: assert namespace_1['var_1'].fullname == 'namespace_1.var_1' assert namespace_1['var_2'].fullname == 'namespace_1.var_2' + def test_find_vars(self): + Namespace.reset() + datavars = Namespace.datavars + var_1 = Variable('var', source='null') + + with Namespace('namespace_1'): + Variable('var_1', source='val_1') + Variable('var_2', source=2) + Variable('var_3', source=4) + + with Namespace('namespace_1_1'): + Variable('var_1', source='val_1') + + assert Dependence(var_1)._find_variable('.var_1') ==\ + datavars.namespace_1.namespace_1_1['var_1'] + assert Dependence(var_1)._find_variable('..var_3') ==\ + datavars.namespace_1['var_3'] + + with Namespace('namespace_2'): + assert Dependence(var_1)._find_variable('namespace_1.var_2') ==\ + datavars.namespace_1['var_2'] + def test_compare_two_dependencies_equal(self): namespace_1 = NamespaceNode('namespace_1') variable_1 = VariableNode('var_1', namespace_1, source=2) @@ -137,11 +159,11 @@ class TestNamespace: var_1 = Variable('var_2', source=2) - var_2 = Variable('var_3', source=4) + Variable('var_3', source=4) with Namespace('namespace_2'): Variable('var_1', source=Dependence( - var_1, var_2, + var_1, '.var_2', depend=lambda arg_1, arg_2: 'greater' if arg_1 > arg_2 else 'less')) with Namespace('namespace_2_1'): @@ -171,7 +193,7 @@ class TestNamespace: assert datavars.namespace_1.var_2 == 'from var_1: value_1' with Namespace('namespace_1'): - var_1 = Variable('var_1', source='value_2') + Variable('var_1', source='value_2') assert datavars.namespace_1.var_1 == 'value_2' assert datavars.namespace_1.var_2 == 'from var_1: value_2' @@ -181,17 +203,17 @@ class TestNamespace: datavars = Namespace.datavars with Namespace('namespace_1'): - var_1 = Variable('var_1', source='value_1') - Variable('var_2', source=Dependence(var_1, depend=lambda arg_1: + Variable('var_1', source='value_1') + Variable('var_2', source=Dependence('.var_1', depend=lambda arg_1: 'from var_1: {}'. format(arg_1))) - var_3 = Variable('var_3', source='value_3') + Variable('var_3', source='value_3') assert datavars.namespace_1.var_1 == 'value_1' assert datavars.namespace_1.var_2 == 'from var_1: value_1' with Namespace('namespace_1'): - Variable('var_2', source=Dependence(var_3, depend=lambda arg_1: + Variable('var_2', source=Dependence('.var_3', depend=lambda arg_1: 'from var_3: {}'. format(arg_1))) @@ -202,47 +224,19 @@ class TestNamespace: datavars = Namespace.datavars with Namespace('namespace_1'): - var_1 = Variable('var_1', source='value_1') - var_2 = Variable('var_2', - source=Dependence(var_1, + Variable('var_1', source='value_1') + Variable('var_2', source=Dependence( + 'namespace_1.var_1', depend=lambda arg_1: 'from var_1: {}'.format(arg_1))) def comparator(arg_1): return 'from var_2: {}'.format(arg_1) - var_3 = Variable('var_3', - source=Dependence(var_2, depend=comparator)) + Variable('var_3', source=Dependence('.var_2', depend=comparator)) Variable('var_1', - source=Dependence(var_3, + source=Dependence('.var_3', depend=lambda arg_1: 'from var_3: {}'.format(arg_1))) with pytest.raises(CyclicVariableError): datavars.namespace_1.var_3 - - def test_find_vars(self): - Namespace.reset() - datavars = Namespace.datavars - var_1 = Variable('var', source='null') - - with Namespace('namespace_1'): - Variable('var_1', source='val_1') - Variable('var_2', source=2) - Variable('var_3', source=4) - - with Namespace('namespace_1_1'): - Variable('var_1', source='val_1') - - for var in Dependence(var_1)._get_variables(['.var_1', - '..var_3']): - print(var.fullname) - - with Namespace('namespace_2'): - print('current_datavars:') - for namespace_name, namespace in datavars.namespaces.items(): - print(namespace_name) - - for var in Dependence(var_1)._get_variables(['namespace_1.var_2']): - print(var.fullname) - - assert False