From 6bfd4c046b536b6d0febc8afc101f3bd834435bc 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, 6 Aug 2020 19:22:25 +0300 Subject: [PATCH] Added the new VariablesLoader for new version of the variables API. --- calculate/templates/template_engine.py | 5 +- calculate/vars/alt_datavars.py | 11 +- calculate/vars/alt_vars_loader.py | 95 ++---- tests/vars/test_alt_vars.py | 296 +++++++++++++++++- tests/vars/testfiles/variables_0/__init__.py | 0 .../testfiles/variables_0/main/__init__.py | 6 + .../vars/testfiles/variables_0/os/__init__.py | 6 + 7 files changed, 343 insertions(+), 76 deletions(-) create mode 100644 tests/vars/testfiles/variables_0/__init__.py create mode 100644 tests/vars/testfiles/variables_0/main/__init__.py create mode 100644 tests/vars/testfiles/variables_0/os/__init__.py diff --git a/calculate/templates/template_engine.py b/calculate/templates/template_engine.py index 12faa6f..99039c3 100644 --- a/calculate/templates/template_engine.py +++ b/calculate/templates/template_engine.py @@ -506,7 +506,7 @@ class ParametersProcessor: return {'uid': uid, 'gid': gid} else: raise IncorrectParameter("'chown' value '{0}' is not correct". - format(chown, self.template_path)) + format(chown)) def get_uid_from_passwd(self, user_name: str): """Функция для получения uid из chroot passwd файла.""" @@ -572,7 +572,8 @@ class ParametersProcessor: 'format') for module_name in os.listdir(format_directory_path): - if (os.path.isdir(os.path.join('format', module_name)) or + if (os.path.isdir(os.path.join(format_directory_path, + module_name)) or module_name == '__init__.py'): continue diff --git a/calculate/vars/alt_datavars.py b/calculate/vars/alt_datavars.py index bd8da13..0967d72 100644 --- a/calculate/vars/alt_datavars.py +++ b/calculate/vars/alt_datavars.py @@ -93,7 +93,7 @@ class FloatType(VariableType): @classmethod def process_value(self, value, variable) -> float: if isinstance(value, float): - return True + return value else: try: return float(value) @@ -620,11 +620,15 @@ class NamespaceNode: namespace.parent = self def clear(self): + '''Метод для очистки пространства имен. Очищает и пространства имен + и переменные. Предназначен только для использования в calculate.ini.''' for namespace in self.namespaces: namespace.clear() + self.variables.clear() self.namespaces.clear() def get_fullname(self) -> str: + '''Метод для получения полного имени пространства имен.''' if self.parent is not None: return '{}.{}'.format(self.parent.get_fullname(), self.name) else: @@ -772,6 +776,11 @@ class NamespaceAPI: self._variables_fabric.current_namespace = self._datavars self._dependence_fabric.current_namespace = self._datavars + def set_current_namespace(self, namespace: NamespaceNode): + self.current_namespace = namespace + self._variables_fabric.current_namespace = namespace + self._dependence_fabric.current_namespace = namespace + @contextmanager def __call__(self, namespace_name): '''Метод для создания пространств имен с помощью with.''' diff --git a/calculate/vars/alt_vars_loader.py b/calculate/vars/alt_vars_loader.py index 0e69e03..ca175b0 100644 --- a/calculate/vars/alt_vars_loader.py +++ b/calculate/vars/alt_vars_loader.py @@ -8,7 +8,8 @@ from calculate.vars.datavars import Variable, Namespace, HashVariable,\ TableVariable, IniCreated, DefaultValue from calculate.vars.alt_datavars import NamespaceNode, VariableNode,\ ListType, IntegerType,\ - FloatType, IniType, TableType + FloatType, IniType, TableType,\ + Namespace from calculate.utils.gentoo import ProfileWalker from calculate.utils.fs import readFile from calculate.utils.files import list_directory @@ -97,7 +98,6 @@ class CalculateIniParser: section, defkeys = tokens - print('section type: {}'.format(section.getName())) if section.getName() == 'namespace': section_list = section.asList() if section_list[-1] == []: @@ -198,6 +198,8 @@ class NamespaceIniFiller: def clear_section(self, sections: list) -> None: '''Метод для очистки пространства имен.''' + if self.restricted: + self.modify_only = sections[0] not in self.available_sections current_namespace = self.namespace for section in sections: @@ -210,16 +212,18 @@ class NamespaceIniFiller: table_to_clear = table_variable.get_value() table_to_clear.clear() table_variable.source = table_to_clear + return else: return - current_namespace.clear() + if not self.modify_only: + current_namespace.clear() def start_table(self, sections: str, row) -> None: '''Метод для создания и модификации таблиц.''' if self.restricted: self.modify_only = sections[0] not in self.available_sections self.current_namespace = self.namespace - row_number = int(sections.pop()) + row_index = int(sections.pop()) table_name = sections.pop() for section in sections: if section not in self.current_namespace.namespaces: @@ -239,8 +243,8 @@ class NamespaceIniFiller: else: table_variable = self.current_namespace.variables[table_name] table = table_variable.get_value() - if len(table) < row_number: - table.change_row(row, row_number) + if row_index < len(table): + table.change_row(row, row_index) else: table.add_row(row) table_variable.source = table @@ -341,64 +345,29 @@ class NamespaceIniFiller: self.error(lineno, "Syntax error: {}".format(line)) -class NamespaceIniFillerStrict(NamespaceIniFiller): - """Объект используемый для наполнения Namespace объекта переменными - из calculate.ini файла, с возможность определять только - """ - availableSection = ["custom"] - - def fill(self, namespace, data): - self.canCreate = False - for newns in self.availableSection: - if newns not in namespace: - namespace.add_namespace(Namespace(newns)) - super().fill(namespace, data) - - def start_section(self, sections): - self.current_namespace = self.namespace - self.canCreate = sections[0] in self.availableSection - for section in sections: - 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.current_namespace = None - self.current_namespace = self.current_namespace[section] - - # def clear_section(self, sections): - # current_namespace = self.namespace - # self.canCreate = sections[0] in self.availableSection - # for section in sections: - # if section not in current_namespace.childs: - # return - # current_namespace = current_namespace[section] - # current_namespace.clear_childs() - - def define_variable(self, key, value): - if not self.canCreate: - pass - var = Variable(key) - var.add_property(IniCreated(value)) - self.current_namespace.add_variable(var) - - def change_value(self, key, value): - var = self.current_namespace[key] - var.set_value(value) - - if isinstance(var, HashVariable.HashValue): - var = var.master_variable - value = var.get_value() - - prop = var.find_property(DefaultValue) - if prop: - prop.value = value - else: - var.add_property(DefaultValue(value)) - var.invalidate() - - class VariableLoader: + def load_variables(self, directory_path) -> NamespaceNode: + root = Namespace.datavars + package = '.'.join(directory_path.split('/')) + self._fill(root, directory_path, package) + return root + + def _fill(self, current_namespace: NamespaceNode, + directory_path: str, package: str) -> None: + for file_name in list_directory(directory_path): + file_path = os.path.join(directory_path, file_name) + if os.path.isdir(file_path): + namespace = NamespaceNode(file_name) + current_namespace.add_namespace(namespace) + self._fill(namespace, file_path, "{}.{}".format(package, + file_name)) + elif file_name.endswith('.py'): + file_name = file_name[:-3] + Namespace.set_current_namespace(current_namespace) + importlib.import_module('{}.{}'.format(package, file_name)) + + +class OldVariableLoader: '''Класс, используемый для загрузки переменных из python модуля.''' re_upper = re.compile("(.)([A-Z])") diff --git a/tests/vars/test_alt_vars.py b/tests/vars/test_alt_vars.py index a823858..bacd388 100644 --- a/tests/vars/test_alt_vars.py +++ b/tests/vars/test_alt_vars.py @@ -3,8 +3,9 @@ from calculate.vars.alt_datavars import NamespaceNode, VariableNode,\ Namespace, Variable, Dependence,\ CyclicVariableError, HashType,\ StringType, IntegerType, VariableType,\ - VariableError, TableType -from calculate.vars.alt_vars_loader import NamespaceIniFiller + VariableError, TableType, BooleanType,\ + VariableTypeError, FloatType, ListType +from calculate.vars.alt_vars_loader import NamespaceIniFiller, VariableLoader from calculate.utils.files import stderr_devnull @@ -55,7 +56,7 @@ class TestNamespace: assert namespace_1['var_1'].get_fullname() == 'namespace_1.var_1' assert namespace_1['var_2'].get_fullname() == 'namespace_1.var_2' - def test_compare_two_dependencies_equal(self): + def test_if_two_dependencies_is_created_with_the_same_variables_and_their_depend_functions_are_equivalent_lambdas__the_dependencies_are_equal(self): namespace_1 = NamespaceNode('namespace_1') variable_1 = VariableNode('var_1', namespace_1, source=2, variable_type=IntegerType) @@ -71,7 +72,7 @@ class TestNamespace: 'greater' if var_1.value > var_2.value else 'less') assert dependence_1 == dependence_2 - def test_compare_two_dependencies_equal_but_one_is_function_and_other_is_lambda(self): + def test_if_two_dependencies_is_created_with_the_same_variables_and_their_depend_functions_are_equivalent_lambda_and_function__the_dependencies_are_equal(self): namespace_1 = NamespaceNode('namespace_1') variable_1 = VariableNode('var_1', namespace_1, source=2, variable_type=IntegerType) @@ -90,7 +91,7 @@ class TestNamespace: dependence_2 = Dependence(variable_1, variable_2, depend=comparator) assert dependence_1 == dependence_2 - def test_compare_two_dependencies_not_equal(self): + def test_if_two_dependencies_is_created_with_the_same_variables_and_their_depend_functions_are_not_equivalent_lambdas__the_dependencies_are_equal(self): namespace_1 = NamespaceNode('namespace_1') variable_1 = VariableNode('var_1', namespace_1, source=2, variable_type=IntegerType) @@ -106,7 +107,7 @@ class TestNamespace: 'greater' if var_1.value > var_2.value else 'less') assert dependence_1 != dependence_2 - def test_compare_two_dependencies_not_equal_but_one_is_function_and_other_is_lambda(self): + def test_if_two_dependencies_is_created_with_the_same_variables_and_their_depend_functions_are_not_equivalent_lambda_and_function__the_dependencies_are_equal(self): namespace_1 = NamespaceNode('namespace_1') variable_1 = VariableNode('var_1', namespace_1, source=2, variable_type=IntegerType) @@ -163,6 +164,18 @@ class TestNamespace: assert datavars.namespace_1.var_3 == 4 assert datavars.namespace_1.namespace_1_1.var_1 == 'val_1' + def test_if_variables_creation_api_is_used_for_creating_of_a_namespace_and_of_a_number_of_variables_and_there_is_attempt_to_get_value_of_an_unexistent_variable_from_a_created_namespace__the_namespace_raises_the_VariableError_exception(self): + Namespace.reset() + datavars = Namespace.datavars + + with Namespace('namespace_1'): + Variable('var_1', source='val_1') + Variable('var_2', source=2, type=IntegerType) + Variable('var_3', source=4, type=IntegerType) + + with pytest.raises(VariableError): + datavars.namespace_1.var_4 + def test_if_dependence_interface_object_is_used_for_finding_an_existing_variable_from_an_other_namespace_using_absolute_variable_name__the_find_variable_method_returns_the_desired_variable(self): Namespace.reset() datavars = Namespace.datavars @@ -357,6 +370,107 @@ class TestNamespace: with pytest.raises(VariableError): Variable('var_1', source='value_2', type=StringType) + def test_if_variables_with_string_type_is_created_with_correct_source__the_variable_api_creates_this_variables_and_the_variables_contains_strings(self): + Namespace.reset() + datavars = Namespace.datavars + + with Namespace('namespace_1'): + Variable('var_1', source='value', type=StringType) + Variable('var_2', source=2, type=StringType) + Variable('var_3', source=False, type=StringType) + + assert datavars.namespace_1.var_1 == 'value' + assert datavars.namespace_1.var_2 == '2' + assert datavars.namespace_1.var_3 == 'False' + + def test_if_variables_with_integer_type_is_created_with_correct_source__the_variable_api_creates_this_variables_and_the_variables_contains_integers(self): + Namespace.reset() + datavars = Namespace.datavars + + with Namespace('namespace_1'): + Variable('var_1', source=1, type=IntegerType) + Variable('var_2', source='2', type=IntegerType) + Variable('var_3', source=3.1, type=IntegerType) + Variable('var_4', source='-4', type=IntegerType) + + assert datavars.namespace_1.var_1 == 1 + assert datavars.namespace_1.var_2 == 2 + assert datavars.namespace_1.var_3 == 3 + assert datavars.namespace_1.var_4 == -4 + + def test_if_variable_with_integer_type_is_created_with_source_that_can_not_be_converted_to_an_integer_value__the_variable_api_raises_the_VariableTypeError_exception(self): + Namespace.reset() + datavars = Namespace.datavars + + with Namespace('namespace_1'): + Variable('var_1', source='a', type=IntegerType) + + with pytest.raises(VariableTypeError): + datavars.namespace_1.var_1 + + def test_if_variables_with_float_type_is_created_with_correct_source__the_variable_api_creates_this_variables_and_the_variables_contains_floats(self): + Namespace.reset() + datavars = Namespace.datavars + + with Namespace('namespace_1'): + Variable('var_1', source=1.1, type=FloatType) + Variable('var_2', source='2.2', type=FloatType) + Variable('var_3', source=3, type=FloatType) + Variable('var_4', source='-4.4', type=FloatType) + + assert datavars.namespace_1.var_1 == 1.1 + assert datavars.namespace_1.var_2 == 2.2 + assert datavars.namespace_1.var_3 == 3.0 + assert datavars.namespace_1.var_4 == -4.4 + + def test_if_variable_with_float_type_is_created_with_source_that_can_not_be_converted_to_an_float_value__the_variable_api_raises_the_VariableTypeError_exception(self): + Namespace.reset() + datavars = Namespace.datavars + + with Namespace('namespace_1'): + Variable('var_1', source='a', type=FloatType) + + with pytest.raises(VariableTypeError): + datavars.namespace_1.var_1 + + def test_if_variables_with_bool_type_is_created_with_correct_source__the_variable_api_creates_this_variables_and_the_variables_contains_bool(self): + Namespace.reset() + datavars = Namespace.datavars + + with Namespace('namespace_1'): + Variable('var_1', source=True, type=BooleanType) + Variable('var_2', source='false', type=BooleanType) + Variable('var_3', source='True', type=BooleanType) + Variable('var_4', source='0', type=BooleanType) + + assert datavars.namespace_1.var_1 is True + assert datavars.namespace_1.var_2 is False + assert datavars.namespace_1.var_3 is True + assert datavars.namespace_1.var_4 is True + + def test_if_variables_with_list_type_is_created_with_correct_source__the_variable_api_creates_this_variables_and_the_variables_contains_lists(self): + Namespace.reset() + datavars = Namespace.datavars + + with Namespace('namespace_1'): + Variable('var_1', source=['a', 'b', 'c'], type=ListType) + Variable('var_2', source='a, b, c', type=ListType) + Variable('var_3', source={'a', 'b', 'c'}, type=ListType) + + assert datavars.namespace_1.var_1 == ['a', 'b', 'c'] + assert datavars.namespace_1.var_2 == ['a', 'b', 'c'] + assert datavars.namespace_1.var_3 == list({'a', 'b', 'c'}) + + def test_if_variable_with_list_type_is_created_with_source_that_can_not_be_converted_to_an_list_value__the_variable_api_raises_the_VariableTypeError_exception(self): + Namespace.reset() + datavars = Namespace.datavars + + with Namespace('namespace_1'): + Variable('var_1', source=1, type=ListType) + + with pytest.raises(VariableTypeError): + datavars.namespace_1.var_1 + def test_if_hash_variable_is_created_with_Hash_type_and_with_a_dictionary_in_its_source__the_variable_is_created_with_Hash_value_in_it(self): Namespace.reset() datavars = Namespace.datavars @@ -567,7 +681,7 @@ test -= asdf,zxcv namespace_filler.fill(datavars, third_ini_text) assert datavars.os.test == "123,qwer" - def test_custom_and_not_custom_creation(self): + def test_if_calculate_ini_file_is_used_for_creation_variables_in_the_custom_and_some_other_namespaces_and_for_changing_of_a_variable_from_not_custom_namespaces__the_NamespaceIniFiller_object_just_creates_variables_in_the_custom_namespace_and_modifies_variables_from_other_namespaces(self): Namespace.reset() datavars = Namespace.datavars namespace_filler = NamespaceIniFiller() @@ -594,7 +708,38 @@ var_1 = value_1 assert datavars.custom.test_1.var_1 == "value_1" assert datavars.custom.test_2.var_1 == "value_1" - def test_table_from_ini(self): + def test_if_calculate_ini_file_is_used_for_clearing_namespaces_in_the_custom_and_some_other_namespaces__the_NamespaceIniFiller_object_clears_namespaces_only_from_the_custom_namespace(self): + Namespace.reset() + datavars = Namespace.datavars + namespace_filler = NamespaceIniFiller() + + with Namespace('os'): + Variable('var_1', type=StringType, source='value_1') + Variable('var_2', type=StringType, source='value_2') + + first_ini_text = """ +[custom][test] +var_1 = value_1 +var_2 = value_2 +""" + + namespace_filler.fill(datavars, first_ini_text) + assert datavars.custom.test.var_1 == 'value_1' + assert datavars.custom.test.var_2 == 'value_2' + + second_ini_text = """ +[custom][test][] + +[os][] + """ + + namespace_filler.fill(datavars, second_ini_text) + assert datavars.os.var_1 == 'value_1' + assert datavars.os.var_2 == 'value_2' + assert 'var_1' not in datavars.custom.test.variables + assert 'var_2' not in datavars.custom.test.variables + + def test_if_calculate_ini_file_is_used_for_creation_of_the_table_variable_in_the_custom_namespace__the_NamespaceIniFiller_object_creates_table_in_the_custom_namespace(self): Namespace.reset() datavars = Namespace.datavars namespace_filler = NamespaceIniFiller() @@ -623,14 +768,145 @@ value = value_2 {'name': 'name_1', 'value': 'value_1'}, {'name': 'name_2', 'value': 'value_2'}] - second_ini_text = """ + def test_if_calculate_ini_file_is_used_for_creation_of_the_table_variable_in_not_custom_namespaces__the_NamespaceIniFiller_object_does_not_create_anything(self): + Namespace.reset() + datavars = Namespace.datavars + namespace_filler = NamespaceIniFiller() + + with Namespace('os'): + Variable('var_1', type=StringType, source='value_1') + Variable('var_2', type=StringType, source='value_2') + + first_ini_text = """ +[os][test][0] +name = name_0 +value = value_0 + +[os][test][1] +name = name_1 +value = value_1 + +[os][test][2] +name = name_2 +value = value_2 +""" + + namespace_filler.fill(datavars, first_ini_text) + assert 'test' not in datavars.os + + def test_if_two_tables_are_created_using_calculate_ini_file_and_variables_api_and_then_calculate_ini_file_is_used_for_clearing_and_add_rows_in_the_both_tables__the_NamespaceIniFiller_object_clears_tables_and_adds_rows(self): + Namespace.reset() + datavars = Namespace.datavars + namespace_filler = NamespaceIniFiller() + + with Namespace('os'): + Variable('test', type=TableType, source=[{'name': 'name_1', + 'value': 'value_1'}, + {'name': 'name_2', + 'value': 'value_2'}, + {'name': 'name_3', + 'value': 'value_3'}]) + + first_ini_text = """ +# Создаем таблицу. +[custom][test][0] +name = name_0 +value = value_0 + +[custom][test][1] +name = name_1 +value = value_1 + +# Очищаем таблицу. [custom][test][] +# Добавляем в таблицу строку. [custom][test][0] name = name_0 value = value_0 + +# Очищаем таблицу созданную интерфейсом. +[os][test][] + +# Добавляем в эту таблицу строку. +[os][test][0] +name = strange_name +value = weird_value """ - namespace_filler.fill(datavars, second_ini_text) + namespace_filler.fill(datavars, first_ini_text) assert datavars.custom.test.get_table() ==\ [{'name': 'name_0', 'value': 'value_0'}] + assert datavars.os.test.get_table() ==\ + [{'name': 'strange_name', 'value': 'weird_value'}] + + def test_if_calculate_ini_file_is_used_for_modifying_of_the_table_from_calculate_ini_file__the_NamespaceIniFiller_object_modifies_the_table(self): + Namespace.reset() + datavars = Namespace.datavars + namespace_filler = NamespaceIniFiller() + + first_ini_text = """ +# Создаем таблицу. +[custom][test][0] +name = name_0 +value = value_0 + +[custom][test][1] +name = name_1 +value = value_1 +""" + + namespace_filler.fill(datavars, first_ini_text) + assert datavars.custom.test.get_table() ==\ + [{'name': 'name_0', 'value': 'value_0'}, + {'name': 'name_1', 'value': 'value_1'}] + + second_ini_text = """ +[custom][test][1] +name = other_name +value = another_value +""" + + namespace_filler.fill(datavars, second_ini_text) + assert datavars.custom.test.get_table() ==\ + [{'name': 'name_0', 'value': 'value_0'}, + {'name': 'other_name', 'value': 'another_value'}] + + def test_if_calculate_ini_file_is_used_for_modifying_of_the_table_created_using_variables_api__the_NamespaceIniFiller_object_modifies_the_table(self): + Namespace.reset() + datavars = Namespace.datavars + namespace_filler = NamespaceIniFiller() + + with Namespace('namespace_1'): + Variable('test', type=TableType, source=[{'name': 'name_1', + 'value': 'value_1'}, + {'name': 'name_2', + 'value': 'value_2'}, + {'name': 'name_3', + 'value': 'value_3'}]) + + first_ini_text = """ +[namespace_1][test][0] +name = new_name +value = other_value + +[namespace_1][test][1] +name = common_name +value = another_value +""" + + namespace_filler.fill(datavars, first_ini_text) + assert datavars.namespace_1.test.get_table() ==\ + [{'name': 'new_name', 'value': 'other_value'}, + {'name': 'common_name', 'value': 'another_value'}, + {'name': 'name_3', 'value': 'value_3'}] + + def test_loader(self): + Namespace.reset() + loader = VariableLoader() + datavars = loader.load_variables('tests/vars/testfiles/variables_0') + assert datavars.os.ns.var_1 == 'value_1' + assert datavars.os.ns.var_2 == 2 + + assert datavars.main.strange_variable == 'weird_value' + assert datavars.main.plain_variable is True diff --git a/tests/vars/testfiles/variables_0/__init__.py b/tests/vars/testfiles/variables_0/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/vars/testfiles/variables_0/main/__init__.py b/tests/vars/testfiles/variables_0/main/__init__.py new file mode 100644 index 0000000..6b42a89 --- /dev/null +++ b/tests/vars/testfiles/variables_0/main/__init__.py @@ -0,0 +1,6 @@ +from calculate.vars.alt_datavars import Namespace, Variable, StringType,\ + IntegerType, BooleanType + +Variable('strange_variable', source='weird_value', type=StringType) + +Variable('plain_variable', source=True, type=BooleanType) diff --git a/tests/vars/testfiles/variables_0/os/__init__.py b/tests/vars/testfiles/variables_0/os/__init__.py new file mode 100644 index 0000000..af84cad --- /dev/null +++ b/tests/vars/testfiles/variables_0/os/__init__.py @@ -0,0 +1,6 @@ +from calculate.vars.alt_datavars import Namespace, Variable, StringType,\ + IntegerType + +with Namespace('ns'): + Variable('var_1', source='value_1', type=StringType) + Variable('var_2', source=2, type=IntegerType)