From df9e70a0d84b49fb5b5062fb098a564a6209d88e 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: Fri, 17 Jul 2020 18:11:34 +0300 Subject: [PATCH] The new version of the datavars module is partly implemented. --- calculate/vars/alt_datavars.py | 115 +++++++++++++++++++++++++++++---- pytest.ini | 1 + tests/vars/test_alt_vars.py | 49 +++++++++++++- 3 files changed, 153 insertions(+), 12 deletions(-) diff --git a/calculate/vars/alt_datavars.py b/calculate/vars/alt_datavars.py index 499a6dd..9eed71a 100644 --- a/calculate/vars/alt_datavars.py +++ b/calculate/vars/alt_datavars.py @@ -3,8 +3,17 @@ import re class VariableType: '''Базовый класс для типов.''' + @classmethod def check(self, value: str) -> bool: - pass + return True + + +class VariableError(Exception): + pass + + +class VariableTypeError(Exception): + pass class StringType(VariableType): @@ -14,14 +23,16 @@ class StringType(VariableType): class IntegerType(VariableType): integer_pattern = re.compile(r"^-?\d+$") - def check(self, value) -> bool: + @classmethod + def check(self, value: str) -> bool: return self.integer_pattern.match(value) class FloatType(VariableType): float_pattern = re.compile(r"^-?\d+(\.\d+)?$") - def check(self, value) -> bool: + @classmethod + def check(self, value: str) -> bool: return self.integer_pattern.match(value) @@ -37,12 +48,94 @@ class ReadonlyType(VariableType): pass -class Variable: - def __init__(self, variable_type: VariableType): - self.var_type = variable_type - - -class Namespace: - def __init__(self, name=''): - self._name = name +class VariableNode: + def __init__(self, name, namespace, variable_type=StringType): + self.name = name + if issubclass(variable_type, VariableType): + self.variable_type = variable_type + else: + raise VariableTypeError('variable_type must be VariableType' + ', but not {}'.format(type(variable_type))) + self.namespace = namespace + self.set_function = None + self.value = None + + self.subscribers = set() + self.subscriptions = tuple() + + def update_value(self, value=None): + if self.set_function is not None: + if self.subscriptions: + self.value = self.set_function(*(subscription.value for + subscription in + self.subscriptions)) + elif value is not None: + self.value = self.set_function(value) + else: + # Пока что полагаем ее обнуленной в этой ситуации. + # TODO Обдумать поведение. + self.value = None + elif self.subscriptions: + # Пока что будем полагать, что в такой ситуации берем значение из + # последней подписки. + self.value = self.subscriptions[-1] + else: + self.value = value + + @property + def fullname(self) -> str: + if self.namespace: + return "{}.{}".format(self.namespace.fullname, self.name) + else: + return self.name + + def __repr__(self): + return ''.format(self.fullname, + self.value or + 'INVALIDATED') + + +class NamespaceNode: + def __init__(self, name='', parent=None): + self.name = name self.variables = dict() + self.namespaces = dict() + 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 + + @property + def fullname(self) -> str: + if self.parent is not None: + return '{}.{}'.format(self.parent.fullname, self.name) + else: + return self.name + + def get_variable_node(self, name): + if name in self.variables: + return self.variables[name] + else: + raise VariableError("Variable '{variable_name}' is not found in" + " the namespace '{namespace_name}'".format( + variable_name=name, + namespace_name=self.name)) + + def __getattr__(self, name: str): + if name in self.namespaces: + return self.namespaces[name] + elif name in self.variables: + return self.variables[name].value + else: + raise VariableError("'{variable_name}' is not found in the" + " namespace '{namespace_name}'".format( + variable_name=name, + namespace_name=self.name)) + + def __repr__(self): + return ''.format(self.fullname) diff --git a/pytest.ini b/pytest.ini index aa69364..bec6ea6 100644 --- a/pytest.ini +++ b/pytest.ini @@ -25,6 +25,7 @@ markers = package_utils: marker for running tests for calculate.utils.contents module. vars: marker for running tests for datavars + alt_vars: marker for testing alternative version of the datavars module. gentoo: marker for running tests for utils.gentoo calculateini: marker for running tests for utils.calculateini template_engine: marker for running tests for TemplateEngine. diff --git a/tests/vars/test_alt_vars.py b/tests/vars/test_alt_vars.py index a859470..bb5e2be 100644 --- a/tests/vars/test_alt_vars.py +++ b/tests/vars/test_alt_vars.py @@ -1,6 +1,53 @@ import pytest +from calculate.vars.alt_datavars import NamespaceNode, VariableNode @pytest.mark.alt_vars class TestNamespace: - pass + # Сначала тестируем классы и методы необходимые для построения дерева + # переменных и пространств имен. + def test_if_NamespaceNode_just_initialized_with_its_name__the_NamespaceNode_object_contains_empty_namespaces_and_variables_dictionaries_and_fullname_method_returns_only_the_namespace_s_name(self): + namespace_1 = NamespaceNode('namespace_1') + + assert namespace_1.namespaces == dict() + assert namespace_1.variables == dict() + assert namespace_1.fullname == 'namespace_1' + + def test_if_namespace_node_added_in_an_other_namespace_node__a_parent_namespace_node_contains_child_namespace_in_its_namespaces_dictionary_and_child_namespace_object_has_parent_namespace_in_its_parent_attribute_and_its_fullname_method_returns_names_of_both_namespaces(self): + namespace_1 = NamespaceNode('namespace_1') + namespace_2 = NamespaceNode('namespace_2') + namespace_1.add_namespace(namespace_2) + + assert namespace_1.namespaces == {'namespace_2': namespace_2} + assert namespace_1.variables == dict() + assert namespace_1.fullname == 'namespace_1' + + assert namespace_2.namespaces == dict() + assert namespace_2.variables == dict() + assert namespace_2.parent == namespace_1 + assert namespace_2.fullname == 'namespace_1.namespace_2' + + def test_if_two_VariableNode_objects_are_initialized_and_added_to_a_namespace__the_NamespaceNode_object_contains_this_variables_and_can_be_used_to_get_variables_values_and_variables_have_namespace_name_in_their_fullnames(self): + namespace_1 = NamespaceNode('namespace_1') + variable_1 = VariableNode('var_1', namespace_1) + variable_1.update_value(value='value_1') + variable_2 = VariableNode('var_2', namespace_1) + variable_2.update_value(value='value_2') + + namespace_1.add_variable(variable_1) + namespace_1.add_variable(variable_2) + + assert namespace_1.namespaces == dict() + assert namespace_1.variables == {'var_1': variable_1, + 'var_2': variable_2} + assert namespace_1.fullname == 'namespace_1' + + assert namespace_1.var_1 == 'value_1' + assert namespace_1.var_2 == 'value_2' + assert namespace_1.get_variable_node('var_1').fullname ==\ + 'namespace_1.var_1' + assert namespace_1.get_variable_node('var_2').fullname ==\ + 'namespace_1.var_2' + + def test_updating_variable_using_set_function(self): + pass