From 6561bb2c002759a1e3cc0f797082c88ebb74bffc 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, 1 Oct 2020 09:30:30 +0300 Subject: [PATCH] 'handler' and 'notify' parameters are added. --- calculate/templates/template_engine.py | 84 +++++++++++++++---- calculate/templates/template_processor.py | 14 +++- calculate/variables/loader.py | 9 +- tests/templates/test_directory_processor.py | 24 ++++++ .../templates_35/install/.calculate_directory | 1 + .../install/dir_37/.calculate_directory | 1 + .../templates_35/install/dir_37/file_0 | 5 ++ .../install/dir_38/.calculate_directory | 1 + .../templates_35/install/dir_38/file_0 | 5 ++ .../templates_35/install/handler_0 | 3 + 10 files changed, 128 insertions(+), 19 deletions(-) create mode 100644 tests/templates/testfiles/test_dir_processor_root/templates_35/install/.calculate_directory create mode 100644 tests/templates/testfiles/test_dir_processor_root/templates_35/install/dir_37/.calculate_directory create mode 100644 tests/templates/testfiles/test_dir_processor_root/templates_35/install/dir_37/file_0 create mode 100644 tests/templates/testfiles/test_dir_processor_root/templates_35/install/dir_38/.calculate_directory create mode 100644 tests/templates/testfiles/test_dir_processor_root/templates_35/install/dir_38/file_0 create mode 100644 tests/templates/testfiles/test_dir_processor_root/templates_35/install/handler_0 diff --git a/calculate/templates/template_engine.py b/calculate/templates/template_engine.py index 3532120..55d0e27 100644 --- a/calculate/templates/template_engine.py +++ b/calculate/templates/template_engine.py @@ -54,11 +54,19 @@ class Variables(MutableMapping): def __getattribute__(self, name): if name == '_Variables__attrs': return super().__getattribute__(name) + if name == 'available_packages': + return super().__getattribute__(name) try: return self.__attrs[name] except KeyError: raise AttributeError(name) + @property + def available_packages(self): + packages = set(self.__attrs.keys()) + packages.update({'custom'}) + return packages + def __getitem__(self, name): return self.__attrs[name] @@ -84,7 +92,8 @@ class ParametersProcessor: 'autoupdate', 'env', 'force', 'source', 'format', 'unbound', 'mirror', 'run', 'exec', 'env', 'package', 'merge', 'postmerge', 'action', - 'rebuild', 'restart', 'stop', 'start'} + 'rebuild', 'restart', 'stop', 'start', 'handler', + 'notify'} inheritable_parameters = {'chmod', 'chown', 'autoupdate', 'env', 'package', 'action'} @@ -135,7 +144,10 @@ class ParametersProcessor: 'source': self.check_source_parameter, 'force': self.check_force_parameter, 'env': self.check_env_parameter, - 'merge': self.check_merge_parameter + 'merge': self.check_merge_parameter, + 'format': self.check_format_parameter, + 'handler': self.check_handler_parameter, + 'notify': self.check_notify_parameter }) # Если добавляемый параметр должен быть проверен после того, как @@ -297,13 +309,6 @@ class ParametersProcessor: raise IncorrectParameter( "'restart' parameter value is not correct") - def check_format_parameter(self, parameter_value): - if parameter_value in self.available_formats: - return parameter_value - else: - raise IncorrectParameter( - "'format' parameter value is not available") - def check_stop_parameter(self, parameter_value): if not parameter_value and isinstance(parameter_value, bool): raise IncorrectParameter("'stop' parameter value is empty") @@ -426,7 +431,35 @@ class ParametersProcessor: return parameter_value else: raise IncorrectParameter( - "'autoupdate' parameter value is not bool") + "'autoupdate' parameter value is not bool.") + + def check_format_parameter(self, parameter_value): + if self.template_type == DIR: + raise IncorrectParameter("'format' parameter is redundant for" + " directory templates.") + if isinstance(parameter_value, str): + if parameter_value not in self.available_formats: + raise IncorrectParameter(f"'{parameter_value}' value of the" + " 'format' parameter is not" + " available.") + return parameter_value + raise IncorrectParameter("'format' parameter must be string value not" + f" {type(parameter_value)}.") + + def check_handler_parameter(self, parameter_value): + if isinstance(parameter_value, str): + return parameter_value + raise IncorrectParameter("'handler' parameter must be string" + f" value not {type(parameter_value)}.") + + def check_notify_parameter(self, parameter_value): + if isinstance(parameter_value, list): + return parameter_value + elif isinstance(parameter_value, str): + return [parameter.strip() for parameter in + parameter_value.split(',')] + raise IncorrectParameter("'notify' parameter must be string or list" + f" value not {type(parameter_value)}.") # Методы для проверки параметров после разбора всего шаблона. @@ -879,7 +912,7 @@ class CalculateExtension(Extension): check_template_node) check_template.render(__datavars__=self._datavars) - elif (self.stream.current.type == 'name' + elif (self._is_variable_name(self.stream.current) or self.stream.current.type == 'lparen' or self.stream.current.type == 'integer'): # разбираем условие. Если условие False -- кидаем исключение. @@ -887,9 +920,16 @@ class CalculateExtension(Extension): if not condition_result: raise ConditionFailed('Condition is failed', lineno=self.stream.current.lineno) + elif self.stream.current.type == 'name': + raise TemplateSyntaxError( + f"Unknown identifier '{self.stream.current.value}'" + " in calculate tag.", + lineno=self.stream.current.lineno) else: - raise TemplateSyntaxError('Name is expected in calculate tag.', - lineno=self.stream.current.lineno) + raise TemplateSyntaxError( + f"Can not parse token '{self.stream.current.value}'" + " in caluculate tag.", + lineno=self.stream.current.lineno) expect_comma_flag = True # dictionary_node = nodes.Dict(pairs_list) @@ -901,6 +941,17 @@ class CalculateExtension(Extension): return nodes.Output([nodes.Const('')], lineno=lineno) + def _is_variable_name(self, token): + if not token.type == 'name': + return False + if (token.value in self._datavars.available_packages or + token.value in self.environment.globals): + return True + for env in self.environment.context_class._env_set: + if token.value in self._datavars[env]: + return True + return False + def check_parameter(self, parameter_name, parameter_value, context): self.parameters_processor.check_template_parameter( parameter_name, @@ -913,6 +964,7 @@ class CalculateExtension(Extension): '''Метод для разбора условий из тега calculate.''' # лучший способ -- парсим в AST дерево, после чего компилируем и # выполняем его. + print('GET CONDITION') self.condition_result = False try: @@ -929,9 +981,11 @@ class CalculateExtension(Extension): template = self.environment.from_string(condition_template) template.render(__datavars__=self._datavars) - except Exception: + except Exception as error: + print('Error during running condition:', str(error)) return False + print('condition result:', self.condition_result) return self.condition_result # собираем исходный код условия из токенов. @@ -1210,7 +1264,7 @@ class CalculateExtension(Extension): # контекста вo время парсинга. env_names = parameter_value.split(',') for name in env_names: - CalculateContext._env_set.add(name.strip()) + self.environment.context_class._env_set.add(name.strip()) else: parameter_rvalue = nodes.Const(True, lineno=lineno) parameter_value = True diff --git a/calculate/templates/template_processor.py b/calculate/templates/template_processor.py index 3e313f8..4341e9a 100644 --- a/calculate/templates/template_processor.py +++ b/calculate/templates/template_processor.py @@ -213,9 +213,10 @@ class TemplateWrapper: if self.parameters.format: self.format_class = ParametersProcessor.\ available_formats[self.parameters.format] - else: + elif self.template_type is FILE: # TODO Здесь будет детектор форматов. Когда-нибудь. - pass + raise TemplateExecutorError("'format' parameter is not set" + " file template.") # Если по этому пути что-то есть -- проверяем конфликты. if os.path.exists(target_file_path): @@ -1687,6 +1688,9 @@ class DirectoryProcessor: # Словарь для хранения деревьев директорий для различных пакетов. self.packages_file_trees = {} + # Список обработчиков. + self.handlers = OrderedDict() + def _get_cl_ignore_files(self) -> list: '''Метод для получения из соответствующей переменной списка паттернов для обнаружения игнорируемых в ходе обработки шаблонов файлов.''' @@ -1941,7 +1945,7 @@ class DirectoryProcessor: # Если в данный момент обходим дерево -- берем список файлов и # директорий из него. if not self.fill_trees and directory_tree: - template_directories, template_file =\ + template_directories, template_files =\ self._get_files_and_dirs_from_tree(template_files, template_directories, directory_tree) @@ -2229,6 +2233,10 @@ class DirectoryProcessor: template_path)) return False + if parameters.handler: + self.handlers.update({parameters.handler: template_path}) + return False + if self.for_package: if not parameters.package: if self.for_package is not NonePackage: diff --git a/calculate/variables/loader.py b/calculate/variables/loader.py index fda6378..29d9ae0 100644 --- a/calculate/variables/loader.py +++ b/calculate/variables/loader.py @@ -749,13 +749,20 @@ class Datavars: def _load_package(self, package_name): '''Метод для загрузки переменных содержащихся в указанном пакете.''' - self.output.set_info("Loading datavars package '{}'".format(package_name)) + self.output.set_info("Loading datavars package '{}'".format( + package_name)) try: self._loader.load_variables_package(package_name) except Exception as error: raise VariableError("Can not load datavars package: {}". format(error)) + @property + def available_packages(self): + packages = set(self._available_packages) + packages.update({'custom'}) + return packages + def __getattr__(self, package_name: str): '''Метод возвращает ноду пространства имен, соответствующего искомому пакету.''' diff --git a/tests/templates/test_directory_processor.py b/tests/templates/test_directory_processor.py index 51f1d19..7d55cba 100644 --- a/tests/templates/test_directory_processor.py +++ b/tests/templates/test_directory_processor.py @@ -894,6 +894,30 @@ class TestDirectoryProcessor: 'etc/dir_35/file_0')) assert '/etc/dir_35/file_0' in test_package + def test_handlers_basic(self): + datavars.main['cl_template_path'] = os.path.join(CHROOT_PATH, + 'templates_35') + directory_processor = DirectoryProcessor( + 'install', + datavars_module=datavars, + package='test-category/test-package' + ) + directory_processor.process_template_directories() + test_package = Package(test_package_name, chroot_path=CHROOT_PATH) + other_package = Package(other_package_name, chroot_path=CHROOT_PATH) + + assert os.path.exists(join_paths(CHROOT_PATH, + 'etc/dir_37')) + assert os.path.exists(join_paths(CHROOT_PATH, + 'etc/dir_37/file_0')) + assert '/etc/dir_37/file_0' in test_package + + assert os.path.exists(join_paths(CHROOT_PATH, + 'etc/dir_38')) + assert os.path.exists(join_paths(CHROOT_PATH, + 'etc/dir_38/file_0')) + assert '/etc/dir_38/file_0' in other_package + def test_view_tree(self): list_path = join_paths(CHROOT_PATH, '/etc') show_tree(list_path) diff --git a/tests/templates/testfiles/test_dir_processor_root/templates_35/install/.calculate_directory b/tests/templates/testfiles/test_dir_processor_root/templates_35/install/.calculate_directory new file mode 100644 index 0000000..e07d6d9 --- /dev/null +++ b/tests/templates/testfiles/test_dir_processor_root/templates_35/install/.calculate_directory @@ -0,0 +1 @@ +{% calculate append = 'skip', path = '/etc', action = 'install' %} diff --git a/tests/templates/testfiles/test_dir_processor_root/templates_35/install/dir_37/.calculate_directory b/tests/templates/testfiles/test_dir_processor_root/templates_35/install/dir_37/.calculate_directory new file mode 100644 index 0000000..0be1eab --- /dev/null +++ b/tests/templates/testfiles/test_dir_processor_root/templates_35/install/dir_37/.calculate_directory @@ -0,0 +1 @@ +{% calculate append = 'join', package = 'test-category/test-package' %} diff --git a/tests/templates/testfiles/test_dir_processor_root/templates_35/install/dir_37/file_0 b/tests/templates/testfiles/test_dir_processor_root/templates_35/install/dir_37/file_0 new file mode 100644 index 0000000..3559960 --- /dev/null +++ b/tests/templates/testfiles/test_dir_processor_root/templates_35/install/dir_37/file_0 @@ -0,0 +1,5 @@ +{% calculate append = 'join', format = 'bind', notify = 'handler_0' %} +options { + parameter-1 {{ variables.variable_1 }}; + parameter-2 {{ variables.variable_2 }}; +}; diff --git a/tests/templates/testfiles/test_dir_processor_root/templates_35/install/dir_38/.calculate_directory b/tests/templates/testfiles/test_dir_processor_root/templates_35/install/dir_38/.calculate_directory new file mode 100644 index 0000000..d40b763 --- /dev/null +++ b/tests/templates/testfiles/test_dir_processor_root/templates_35/install/dir_38/.calculate_directory @@ -0,0 +1 @@ +{% calculate append = 'join', package = 'test-category/other-package' %} diff --git a/tests/templates/testfiles/test_dir_processor_root/templates_35/install/dir_38/file_0 b/tests/templates/testfiles/test_dir_processor_root/templates_35/install/dir_38/file_0 new file mode 100644 index 0000000..f8d2fe6 --- /dev/null +++ b/tests/templates/testfiles/test_dir_processor_root/templates_35/install/dir_38/file_0 @@ -0,0 +1,5 @@ +{% calculate append = 'join', format = 'bind' %} +options { + parameter-1 {{ variables.variable_1 }}; + parameter-2 {{ variables.variable_2 }}; +}; diff --git a/tests/templates/testfiles/test_dir_processor_root/templates_35/install/handler_0 b/tests/templates/testfiles/test_dir_processor_root/templates_35/install/handler_0 new file mode 100644 index 0000000..f3d9d3a --- /dev/null +++ b/tests/templates/testfiles/test_dir_processor_root/templates_35/install/handler_0 @@ -0,0 +1,3 @@ +{% calculate handler = 'handler_0', merge = 'test-category/other-package', +run = '/usr/bin/python' %} +print('This handler is needed for merging test-category.')