|
|
|
@ -36,6 +36,14 @@ from calculate.variables.datavars import (
|
|
|
|
|
TableType,
|
|
|
|
|
VariableNotFoundError
|
|
|
|
|
)
|
|
|
|
|
from typing import (
|
|
|
|
|
Union,
|
|
|
|
|
Dict,
|
|
|
|
|
List,
|
|
|
|
|
Tuple,
|
|
|
|
|
Iterator,
|
|
|
|
|
Callable
|
|
|
|
|
)
|
|
|
|
|
from calculate.variables.loader import Datavars
|
|
|
|
|
from .format.base_format import Format
|
|
|
|
|
from ..utils.io_module import IOModule
|
|
|
|
@ -70,15 +78,15 @@ class TemplateCollisionError(Exception):
|
|
|
|
|
|
|
|
|
|
class CalculateConfigFile:
|
|
|
|
|
'''Класс для работы с файлом /var/lib/calculate/config.'''
|
|
|
|
|
def __init__(self, cl_config_path='/var/lib/calculate/config',
|
|
|
|
|
cl_chroot_path='/'):
|
|
|
|
|
self.chroot_path = cl_chroot_path
|
|
|
|
|
def __init__(self, cl_config_path: str = '/var/lib/calculate/config',
|
|
|
|
|
cl_chroot_path: str = '/'):
|
|
|
|
|
self.chroot_path: str = cl_chroot_path
|
|
|
|
|
|
|
|
|
|
self.cl_config_path = cl_config_path
|
|
|
|
|
self.cl_config_path: str = cl_config_path
|
|
|
|
|
|
|
|
|
|
self._config_dictionary = self._get_cl_config_dictionary()
|
|
|
|
|
self._config_dictionary: OrderedDict = self._get_cl_config_dictionary()
|
|
|
|
|
|
|
|
|
|
self._unsaved_changes = False
|
|
|
|
|
self._unsaved_changes: bool = False
|
|
|
|
|
|
|
|
|
|
def __contains__(self, file_path: str) -> bool:
|
|
|
|
|
file_path = self._remove_chroot(file_path)
|
|
|
|
@ -158,12 +166,13 @@ class TemplateWrapper:
|
|
|
|
|
'''Класс связывающий шаблон с целевым файлом и определяющий параметры
|
|
|
|
|
наложения шаблона, обусловленные состоянием целевого файла.'''
|
|
|
|
|
|
|
|
|
|
type_checks = {DIR: os.path.isdir,
|
|
|
|
|
FILE: os.path.isfile}
|
|
|
|
|
type_checks: Dict[int,
|
|
|
|
|
Callable[[str], bool]] = {DIR: os.path.isdir,
|
|
|
|
|
FILE: os.path.isfile}
|
|
|
|
|
|
|
|
|
|
_protected_is_set = False
|
|
|
|
|
_protected_set = set()
|
|
|
|
|
_unprotected_set = set()
|
|
|
|
|
_protected_is_set: bool = False
|
|
|
|
|
_protected_set: set = set()
|
|
|
|
|
_unprotected_set: set = set()
|
|
|
|
|
|
|
|
|
|
def __new__(cls, *args, **kwargs):
|
|
|
|
|
if not cls._protected_is_set:
|
|
|
|
@ -175,63 +184,64 @@ class TemplateWrapper:
|
|
|
|
|
cls._set_protected(chroot_path)
|
|
|
|
|
return super().__new__(cls)
|
|
|
|
|
|
|
|
|
|
def __init__(self, target_file_path,
|
|
|
|
|
parameters,
|
|
|
|
|
template_type,
|
|
|
|
|
template_path,
|
|
|
|
|
template_text='',
|
|
|
|
|
target_package=None,
|
|
|
|
|
chroot_path='/',
|
|
|
|
|
config_archive_path='/var/lib/calculate/config-archive',
|
|
|
|
|
dbpkg=True):
|
|
|
|
|
self.target_path = target_file_path
|
|
|
|
|
self.template_path = template_path
|
|
|
|
|
self.chroot_path = chroot_path
|
|
|
|
|
self.config_archive_path = config_archive_path
|
|
|
|
|
|
|
|
|
|
self.target_package_name = None
|
|
|
|
|
self.package_atom_parser = PackageAtomParser(
|
|
|
|
|
def __init__(
|
|
|
|
|
self, target_file_path: str,
|
|
|
|
|
parameters: ParametersContainer,
|
|
|
|
|
template_type: int,
|
|
|
|
|
template_path: str,
|
|
|
|
|
template_text: str = '',
|
|
|
|
|
target_package: Union[Package, None] = None,
|
|
|
|
|
chroot_path: str = '/',
|
|
|
|
|
config_archive_path: str = '/var/lib/calculate/config-archive',
|
|
|
|
|
dbpkg: bool = True):
|
|
|
|
|
self.target_path: str = target_file_path
|
|
|
|
|
self.template_path: str = template_path
|
|
|
|
|
self.chroot_path: str = chroot_path
|
|
|
|
|
self.config_archive_path: str = config_archive_path
|
|
|
|
|
|
|
|
|
|
self.target_package_name: Union[PackageAtomName, None] = None
|
|
|
|
|
self.package_atom_parser: PackageAtomParser = PackageAtomParser(
|
|
|
|
|
chroot_path=self.chroot_path)
|
|
|
|
|
|
|
|
|
|
# Вспомогательный флаг, включается, если по целевому пути лежит файл,
|
|
|
|
|
# для которого не определился никакой пакет.
|
|
|
|
|
self.target_without_package = False
|
|
|
|
|
self.target_without_package: bool = False
|
|
|
|
|
|
|
|
|
|
self.parameters = parameters
|
|
|
|
|
self.parameters: ParametersContainer = parameters
|
|
|
|
|
|
|
|
|
|
self.output_path = self.target_path
|
|
|
|
|
self.input_path = None
|
|
|
|
|
self.output_path: str = self.target_path
|
|
|
|
|
self.input_path: Union[str, None] = None
|
|
|
|
|
|
|
|
|
|
self.template_type = template_type
|
|
|
|
|
self.template_text = template_text
|
|
|
|
|
self.template_type: int = template_type
|
|
|
|
|
self.template_text: str = template_text
|
|
|
|
|
|
|
|
|
|
self.contents_matching = True
|
|
|
|
|
self.ca_is_missed = False
|
|
|
|
|
self.contents_matching: bool = True
|
|
|
|
|
self.ca_is_missed: bool = False
|
|
|
|
|
|
|
|
|
|
# Флаг, указывающий, что нужно удалить файл из target_path перед
|
|
|
|
|
# применением шаблона.
|
|
|
|
|
self.remove_original = False
|
|
|
|
|
self.remove_original: bool = False
|
|
|
|
|
|
|
|
|
|
# Флаг, указывающий, что целевой путь был изменен.
|
|
|
|
|
self.target_path_is_changed = False
|
|
|
|
|
self.target_path_is_changed: bool = False
|
|
|
|
|
|
|
|
|
|
# Флаг, указывающий, что файл по целевому пути является ссылкой.
|
|
|
|
|
self.target_is_link = False
|
|
|
|
|
self.target_is_link: bool = False
|
|
|
|
|
|
|
|
|
|
# Пакет, к которому относится файл.
|
|
|
|
|
self.target_package = target_package
|
|
|
|
|
self.target_package: Package = target_package
|
|
|
|
|
|
|
|
|
|
# Флаг, разрешающий работу с CONTENTS. Если False, то выключает
|
|
|
|
|
# protected для всех файлов блокирует все операции с CONTENTS и ._cfg.
|
|
|
|
|
self.dbpkg = dbpkg
|
|
|
|
|
self.dbpkg: bool = dbpkg
|
|
|
|
|
|
|
|
|
|
# Флаг, указывающий, что файл является PROTECTED.
|
|
|
|
|
self.protected = False
|
|
|
|
|
self.protected: bool = False
|
|
|
|
|
|
|
|
|
|
# Временный флаг для определения того, является ли шаблон userspace.
|
|
|
|
|
self.is_userspace = False
|
|
|
|
|
self.is_userspace: bool = False
|
|
|
|
|
|
|
|
|
|
self.format_class = None
|
|
|
|
|
self.format_class: Union[Format, None] = None
|
|
|
|
|
|
|
|
|
|
if self.parameters.run or self.parameters.exec:
|
|
|
|
|
# Если есть параметр run или exec, то кроме текста шаблона ничего
|
|
|
|
@ -410,8 +420,8 @@ class TemplateWrapper:
|
|
|
|
|
self.target_package_name = parameter_package
|
|
|
|
|
|
|
|
|
|
elif file_package != parameter_package and self.template_type != DIR:
|
|
|
|
|
target_name = self._check_packages_slots(parameter_package,
|
|
|
|
|
file_package)
|
|
|
|
|
target_name = self._compare_packages(parameter_package,
|
|
|
|
|
file_package)
|
|
|
|
|
if (target_name is not None and self.target_package is not None
|
|
|
|
|
and self.target_package.package_name == target_name):
|
|
|
|
|
target_name = self.target_package.package_name
|
|
|
|
@ -453,7 +463,10 @@ class TemplateWrapper:
|
|
|
|
|
self.target_package = Package(self.target_package_name,
|
|
|
|
|
chroot_path=self.chroot_path)
|
|
|
|
|
|
|
|
|
|
def _check_packages_slots(self, lpackage, rpackage):
|
|
|
|
|
def _compare_packages(self, lpackage: PackageAtomName,
|
|
|
|
|
rpackage: PackageAtomName
|
|
|
|
|
) -> Union[None, PackageAtomName]:
|
|
|
|
|
'''Метод, сравнивающий пакеты по их именам, возвращает старший.'''
|
|
|
|
|
if lpackage.category != rpackage.category:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
@ -606,7 +619,7 @@ class TemplateWrapper:
|
|
|
|
|
if self.template_type == DIR and self.target_package is not None:
|
|
|
|
|
self.target_package.clear_dir(self.target_path)
|
|
|
|
|
|
|
|
|
|
def add_to_contents(self, file_md5=None) -> None:
|
|
|
|
|
def add_to_contents(self, file_md5: Union[str, None] = None) -> None:
|
|
|
|
|
'''Метод для добавления целевого файла в CONTENTS.'''
|
|
|
|
|
if self.target_package is None:
|
|
|
|
|
return
|
|
|
|
@ -684,60 +697,65 @@ class TemplateWrapper:
|
|
|
|
|
|
|
|
|
|
class TemplateExecutor:
|
|
|
|
|
'''Класс исполнительного модуля.'''
|
|
|
|
|
def __init__(self, datavars_module=Variables(), chroot_path='/',
|
|
|
|
|
cl_config_archive='/var/lib/calculate/config-archive',
|
|
|
|
|
cl_config_path='/var/lib/calculate/config',
|
|
|
|
|
execute_archive_path='/var/lib/calculate/.execute/',
|
|
|
|
|
dbpkg=True):
|
|
|
|
|
def __init__(self,
|
|
|
|
|
datavars_module: Union[Datavars, Variables] = Variables(),
|
|
|
|
|
chroot_path: str = '/',
|
|
|
|
|
cl_config_archive: str = '/var/lib/calculate/config-archive',
|
|
|
|
|
cl_config_path: str = '/var/lib/calculate/config',
|
|
|
|
|
execute_archive_path: str = '/var/lib/calculate/.execute/',
|
|
|
|
|
dbpkg: bool = True):
|
|
|
|
|
# TODO добавить список измененных файлов.
|
|
|
|
|
self.datavars_module = datavars_module
|
|
|
|
|
self.datavars_module: Union[Datavars, Variables] = datavars_module
|
|
|
|
|
|
|
|
|
|
self.chroot_path = chroot_path
|
|
|
|
|
self.chroot_path: str = chroot_path
|
|
|
|
|
|
|
|
|
|
# Объект для проверки файловых систем. Пока не инициализируем.
|
|
|
|
|
self.mounts = None
|
|
|
|
|
self.mounts: Union[Mounts, None] = None
|
|
|
|
|
|
|
|
|
|
# Директория для хранения полученных при обработке exec скриптов.
|
|
|
|
|
self.execute_archive_path = execute_archive_path
|
|
|
|
|
self.execute_files = OrderedDict()
|
|
|
|
|
self.execute_archive_path: str = execute_archive_path
|
|
|
|
|
self.execute_files: OrderedDict = OrderedDict()
|
|
|
|
|
|
|
|
|
|
self.dbpkg = dbpkg
|
|
|
|
|
self.dbpkg: bool = dbpkg
|
|
|
|
|
|
|
|
|
|
# Словарь с измененными файлами и статусами их изменений.
|
|
|
|
|
self.changed_files = {}
|
|
|
|
|
self.changed_files: dict = {}
|
|
|
|
|
|
|
|
|
|
# Список целевых путей измененных файлов. Нужен для корректного
|
|
|
|
|
# формирования calculate-заголовка.
|
|
|
|
|
self.processed_targets = []
|
|
|
|
|
self.processed_targets: list = []
|
|
|
|
|
|
|
|
|
|
self.directory_default_parameters =\
|
|
|
|
|
# TODO разобраться с этим.
|
|
|
|
|
# Значения параметров по умолчанию, пока не используются.
|
|
|
|
|
self.directory_default_parameters: dict =\
|
|
|
|
|
ParametersProcessor.directory_default_parameters
|
|
|
|
|
self.file_default_parameters =\
|
|
|
|
|
self.file_default_parameters: dict =\
|
|
|
|
|
ParametersProcessor.file_default_parameters
|
|
|
|
|
|
|
|
|
|
# Отображение имен действий для директорий на методы их реализующие.
|
|
|
|
|
self.directory_appends = {'join': self._append_join_directory,
|
|
|
|
|
'remove': self._append_remove_directory,
|
|
|
|
|
'skip': self._append_skip_directory,
|
|
|
|
|
'clear': self._append_clear_directory,
|
|
|
|
|
'link': self._append_link_directory,
|
|
|
|
|
'replace': self._append_replace_directory}
|
|
|
|
|
self.directory_appends: dict = {
|
|
|
|
|
'join': self._append_join_directory,
|
|
|
|
|
'remove': self._append_remove_directory,
|
|
|
|
|
'skip': self._append_skip_directory,
|
|
|
|
|
'clear': self._append_clear_directory,
|
|
|
|
|
'link': self._append_link_directory,
|
|
|
|
|
'replace': self._append_replace_directory}
|
|
|
|
|
|
|
|
|
|
# Отображение имен действий для файлов на методы их реализующие.
|
|
|
|
|
self.file_appends = {'join': self._append_join_file,
|
|
|
|
|
'after': self._append_after_file,
|
|
|
|
|
'before': self._append_before_file,
|
|
|
|
|
'replace': self._append_replace_file,
|
|
|
|
|
'remove': self._append_remove_file,
|
|
|
|
|
'skip': self._append_skip_file,
|
|
|
|
|
'clear': self._append_clear_file,
|
|
|
|
|
'link': self._append_link_file}
|
|
|
|
|
|
|
|
|
|
self.formats_classes = ParametersProcessor.available_formats
|
|
|
|
|
self.calculate_config_file = CalculateConfigFile(
|
|
|
|
|
self.file_appends: dict = {'join': self._append_join_file,
|
|
|
|
|
'after': self._append_after_file,
|
|
|
|
|
'before': self._append_before_file,
|
|
|
|
|
'replace': self._append_replace_file,
|
|
|
|
|
'remove': self._append_remove_file,
|
|
|
|
|
'skip': self._append_skip_file,
|
|
|
|
|
'clear': self._append_clear_file,
|
|
|
|
|
'link': self._append_link_file}
|
|
|
|
|
|
|
|
|
|
self.formats_classes: set = ParametersProcessor.available_formats
|
|
|
|
|
self.calculate_config_file: CalculateConfigFile = CalculateConfigFile(
|
|
|
|
|
cl_config_path=cl_config_path,
|
|
|
|
|
cl_chroot_path=chroot_path)
|
|
|
|
|
self.cl_config_archive_path = cl_config_archive
|
|
|
|
|
self.cl_config_archive_path: str = cl_config_archive
|
|
|
|
|
Format.CALCULATE_VERSION = CALCULATE_VERSION
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
@ -750,8 +768,9 @@ class TemplateExecutor:
|
|
|
|
|
|
|
|
|
|
def execute_template(self, target_path: str,
|
|
|
|
|
parameters: ParametersContainer, template_type: int,
|
|
|
|
|
template_path: str, template_text='',
|
|
|
|
|
save_changes=True, target_package=None) -> dict:
|
|
|
|
|
template_path: str, template_text: str = '',
|
|
|
|
|
save_changes: bool = True,
|
|
|
|
|
target_package: Union[Package, None] = None) -> dict:
|
|
|
|
|
'''Метод для запуска выполнения шаблонов.'''
|
|
|
|
|
# Словарь с данными о результате работы исполнительного метода.
|
|
|
|
|
self.executor_output = {'target_path': None,
|
|
|
|
@ -831,7 +850,7 @@ class TemplateExecutor:
|
|
|
|
|
|
|
|
|
|
return self.executor_output
|
|
|
|
|
|
|
|
|
|
def save_changes(self):
|
|
|
|
|
def save_changes(self) -> None:
|
|
|
|
|
'''Метод для сохранения чего-нибудь после выполнения всех шаблонов.'''
|
|
|
|
|
# Пока сохраняем только получившееся содержимое config-файла.
|
|
|
|
|
self.calculate_config_file.save_changes()
|
|
|
|
@ -851,8 +870,8 @@ class TemplateExecutor:
|
|
|
|
|
if self.dbpkg:
|
|
|
|
|
template_object.add_to_contents()
|
|
|
|
|
|
|
|
|
|
def _append_remove_directory(self,
|
|
|
|
|
template_object: TemplateWrapper) -> None:
|
|
|
|
|
def _append_remove_directory(self, template_object: TemplateWrapper
|
|
|
|
|
) -> None:
|
|
|
|
|
'''Метод описывающий действия для append = "remove", если шаблон --
|
|
|
|
|
директория. Удаляет директорию со всем содержимым, если она есть.'''
|
|
|
|
|
if template_object.target_type is not None:
|
|
|
|
@ -964,7 +983,8 @@ class TemplateExecutor:
|
|
|
|
|
template_object.clear_dir_contents()
|
|
|
|
|
|
|
|
|
|
def _append_join_file(self, template_object: TemplateWrapper,
|
|
|
|
|
join_before=False, replace=False) -> None:
|
|
|
|
|
join_before: bool = False, replace: bool = False
|
|
|
|
|
) -> None:
|
|
|
|
|
'''Метод описывающий действия при append = "join", если шаблон -- файл.
|
|
|
|
|
Объединяет шаблон с целевым файлом.'''
|
|
|
|
|
output_path = template_object.output_path
|
|
|
|
@ -1178,7 +1198,8 @@ class TemplateExecutor:
|
|
|
|
|
self.calculate_config_file.remove_file(template_object.target_path)
|
|
|
|
|
|
|
|
|
|
def _copy_from_source(self, template_object: TemplateWrapper,
|
|
|
|
|
chown=None, chmod=None) -> str:
|
|
|
|
|
chown: Union[dict, None] = None,
|
|
|
|
|
chmod: Union[int, None] = None) -> str:
|
|
|
|
|
'''Метод для копирования файла, указанного в source.'''
|
|
|
|
|
output_path = template_object.output_path
|
|
|
|
|
source_path = template_object.input_path
|
|
|
|
@ -1361,7 +1382,7 @@ class TemplateExecutor:
|
|
|
|
|
return hashlib.md5(source_path.encode()).hexdigest()
|
|
|
|
|
|
|
|
|
|
def _create_directory(self, template_object: TemplateWrapper,
|
|
|
|
|
path_to_create=None) -> None:
|
|
|
|
|
path_to_create: Union[str, None] = None) -> None:
|
|
|
|
|
'''Метод для создания директории и, при необходимости, изменения
|
|
|
|
|
владельца и доступа все директорий на пути к целевой.'''
|
|
|
|
|
if path_to_create is None:
|
|
|
|
@ -1817,7 +1838,7 @@ class TemplateExecutor:
|
|
|
|
|
return hasattr(error, 'errno') and error.errno == errno.EACCES and\
|
|
|
|
|
'var/calculate/remote' in path_to_check
|
|
|
|
|
|
|
|
|
|
def _is_vfat(self, path_to_check):
|
|
|
|
|
def _is_vfat(self, path_to_check: str):
|
|
|
|
|
'''Метод, проверяющий является ли файловая система vfat. Нужно для того,
|
|
|
|
|
чтобы знать о возможности применения chown, chmod и т.д.'''
|
|
|
|
|
# Инициализируем объект для проверки примонтированных файловых систем.
|
|
|
|
@ -1833,7 +1854,7 @@ class TemplateExecutor:
|
|
|
|
|
|
|
|
|
|
class DirectoryTree:
|
|
|
|
|
'''Класс реализующий дерево каталогов для пакета.'''
|
|
|
|
|
def __init__(self, base_directory):
|
|
|
|
|
def __init__(self, base_directory: str):
|
|
|
|
|
self.base_directory = base_directory
|
|
|
|
|
self._tree = {}
|
|
|
|
|
|
|
|
|
@ -1856,7 +1877,7 @@ class DirectoryTree:
|
|
|
|
|
def show_tree(self) -> None:
|
|
|
|
|
pprint(self._tree)
|
|
|
|
|
|
|
|
|
|
def get_directory_tree(self, directory: str):
|
|
|
|
|
def get_directory_tree(self, directory: str) -> "DirectoryTree":
|
|
|
|
|
'''Метод для получения нового дерева из ветви данного дерева,
|
|
|
|
|
соответствующей некоторому каталогу, содержащемуся в корне данного
|
|
|
|
|
дерева.'''
|
|
|
|
@ -1872,10 +1893,10 @@ class DirectoryTree:
|
|
|
|
|
else:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def __setitem__(self, name: str, value):
|
|
|
|
|
def __setitem__(self, name: str, value: Union[None, dict]) -> None:
|
|
|
|
|
self._tree[name] = value
|
|
|
|
|
|
|
|
|
|
def __iter__(self):
|
|
|
|
|
def __iter__(self) -> Iterator[str]:
|
|
|
|
|
if self._tree is not None:
|
|
|
|
|
return iter(self._tree.keys())
|
|
|
|
|
else:
|
|
|
|
@ -1890,35 +1911,40 @@ class DirectoryTree:
|
|
|
|
|
|
|
|
|
|
class DirectoryProcessor:
|
|
|
|
|
'''Класс обработчика директорий шаблонов.'''
|
|
|
|
|
def __init__(self, action: str, datavars_module=Variables(), package='',
|
|
|
|
|
output_module=IOModule(), dbpkg=True,
|
|
|
|
|
namespace: NamespaceNode = None, **groups):
|
|
|
|
|
def __init__(self, action: str,
|
|
|
|
|
datavars_module: Union[Datavars, Variables] = Variables(),
|
|
|
|
|
install: Union[str, PackageAtomName] = '',
|
|
|
|
|
output_module: IOModule = IOModule(), dbpkg: bool = True,
|
|
|
|
|
namespace: NamespaceNode = None, **groups: dict):
|
|
|
|
|
if isinstance(action, list):
|
|
|
|
|
self.action = action
|
|
|
|
|
else:
|
|
|
|
|
self.action = [action]
|
|
|
|
|
|
|
|
|
|
self.output = output_module
|
|
|
|
|
self.datavars_module = datavars_module
|
|
|
|
|
self._namespace = namespace
|
|
|
|
|
self.output: IOModule = output_module
|
|
|
|
|
self.datavars_module: Variables = datavars_module
|
|
|
|
|
self._namespace: NamespaceNode = namespace
|
|
|
|
|
|
|
|
|
|
# Корневая директория.
|
|
|
|
|
self.cl_chroot_path: str
|
|
|
|
|
if 'cl_chroot_path' in datavars_module.main:
|
|
|
|
|
self.cl_chroot_path = datavars_module.main.cl_chroot_path
|
|
|
|
|
else:
|
|
|
|
|
self.cl_chroot_path = '/'
|
|
|
|
|
|
|
|
|
|
# Корневая директория.
|
|
|
|
|
# Корневой путь шаблонов.
|
|
|
|
|
self.templates_root: str
|
|
|
|
|
if 'cl_root_path' in datavars_module.main:
|
|
|
|
|
self.templates_root = join_paths(self.cl_chroot_path,
|
|
|
|
|
datavars_module.main.cl_root_path)
|
|
|
|
|
else:
|
|
|
|
|
self.templates_root = self.cl_chroot_path
|
|
|
|
|
|
|
|
|
|
self.cl_ignore_files = self._get_cl_ignore_files()
|
|
|
|
|
self.cl_ignore_files: List[str] = self._get_cl_ignore_files()
|
|
|
|
|
|
|
|
|
|
# Путь к файлу config с хэш-суммами файлов, для которых уже
|
|
|
|
|
# предлагались изменения.
|
|
|
|
|
self.cl_config_path: str
|
|
|
|
|
if 'cl_config_path' in datavars_module.main:
|
|
|
|
|
self.cl_config_path = self._add_chroot_path(
|
|
|
|
|
self.datavars_module.main.cl_config_path)
|
|
|
|
@ -1928,6 +1954,7 @@ class DirectoryProcessor:
|
|
|
|
|
|
|
|
|
|
# Путь к директории config-archive для хранения оригинальной ветки
|
|
|
|
|
# конфигурационных файлов.
|
|
|
|
|
self.cl_config_archive: str
|
|
|
|
|
if 'cl_config_archive' in datavars_module.main:
|
|
|
|
|
self.cl_config_archive = self._add_chroot_path(
|
|
|
|
|
self.datavars_module.main.cl_config_archive)
|
|
|
|
@ -1937,6 +1964,7 @@ class DirectoryProcessor:
|
|
|
|
|
|
|
|
|
|
# Путь к директории .execute для хранения хранения файлов скриптов,
|
|
|
|
|
# полученных из шаблонов с параметром exec.
|
|
|
|
|
self.cl_exec_dir_path: str
|
|
|
|
|
if 'cl_exec_dir_path' in datavars_module.main:
|
|
|
|
|
self.cl_exec_dir_path = self._add_chroot_path(
|
|
|
|
|
self.datavars_module.main.cl_exec_dir_path)
|
|
|
|
@ -1945,7 +1973,7 @@ class DirectoryProcessor:
|
|
|
|
|
'/var/lib/calculate/.execute/')
|
|
|
|
|
|
|
|
|
|
# Инициализируем исполнительный модуль.
|
|
|
|
|
self.template_executor = TemplateExecutor(
|
|
|
|
|
self.template_executor: TemplateExecutor = TemplateExecutor(
|
|
|
|
|
datavars_module=self.datavars_module,
|
|
|
|
|
chroot_path=self.cl_chroot_path,
|
|
|
|
|
cl_config_archive=self.cl_config_archive,
|
|
|
|
@ -1966,22 +1994,23 @@ class DirectoryProcessor:
|
|
|
|
|
self._make_current_template_var()
|
|
|
|
|
|
|
|
|
|
# Инициализируем шаблонизатор.
|
|
|
|
|
self.template_engine = TemplateEngine(
|
|
|
|
|
self.template_engine: TemplateEngine = TemplateEngine(
|
|
|
|
|
datavars_module=self.datavars_module,
|
|
|
|
|
chroot_path=self.cl_chroot_path,
|
|
|
|
|
appends_set=self.template_executor.available_appends)
|
|
|
|
|
|
|
|
|
|
# Разбираем atom имя пакета, для которого накладываем шаблоны.
|
|
|
|
|
self.for_package = False
|
|
|
|
|
if package:
|
|
|
|
|
if isinstance(package, PackageAtomName):
|
|
|
|
|
self.for_package = package
|
|
|
|
|
elif isinstance(package, str):
|
|
|
|
|
self.for_package: Union[PackageAtomName, None] = None
|
|
|
|
|
if install:
|
|
|
|
|
if isinstance(install, PackageAtomName):
|
|
|
|
|
self.for_package = install
|
|
|
|
|
elif isinstance(install, str):
|
|
|
|
|
try:
|
|
|
|
|
self.for_package = self.template_engine.\
|
|
|
|
|
parameters_processor.check_postparse_package(package)
|
|
|
|
|
parameters_processor.check_postparse_package(
|
|
|
|
|
install)
|
|
|
|
|
except ConditionFailed as error:
|
|
|
|
|
# ConfitionFailed потому что для проверки значения пакета,
|
|
|
|
|
# ConditionFailed потому что для проверки значения пакета,
|
|
|
|
|
# используется тот же метод, что проверяет параметр package
|
|
|
|
|
# в шаблонах, а в них этот параметр играет роль условия.
|
|
|
|
|
self.output.set_error(str(error))
|
|
|
|
@ -1990,6 +2019,7 @@ class DirectoryProcessor:
|
|
|
|
|
|
|
|
|
|
# Получаем список директорий шаблонов.
|
|
|
|
|
# TODO переменная список.
|
|
|
|
|
self.template_paths: List[str]
|
|
|
|
|
if isinstance(self.datavars_module, (Datavars, NamespaceNode)):
|
|
|
|
|
var_type = self.datavars_module.main[
|
|
|
|
|
'cl_template_path'].variable_type
|
|
|
|
@ -2012,11 +2042,11 @@ class DirectoryProcessor:
|
|
|
|
|
self.packages_file_trees = OrderedDict()
|
|
|
|
|
|
|
|
|
|
# Список обработчиков.
|
|
|
|
|
self._handlers = {}
|
|
|
|
|
self._handlers_queue = []
|
|
|
|
|
self._handling = None
|
|
|
|
|
self._handlers: Dict[tuple] = {}
|
|
|
|
|
self._handlers_queue: List[str] = []
|
|
|
|
|
self._handling: Union[str, None] = None
|
|
|
|
|
|
|
|
|
|
def _get_cl_ignore_files(self) -> list:
|
|
|
|
|
def _get_cl_ignore_files(self) -> List[str]:
|
|
|
|
|
'''Метод для получения из соответствующей переменной списка паттернов
|
|
|
|
|
для обнаружения игнорируемых в ходе обработки шаблонов файлов.'''
|
|
|
|
|
if 'cl_ignore_files' in self.datavars_module.main:
|
|
|
|
@ -2050,7 +2080,7 @@ class DirectoryProcessor:
|
|
|
|
|
package_atom: str) -> None:
|
|
|
|
|
try:
|
|
|
|
|
groups_namespace = self.datavars_module.main.cl.groups
|
|
|
|
|
except VariableNotFoundError:
|
|
|
|
|
except (VariableNotFoundError, AttributeError):
|
|
|
|
|
namespaces = ['cl', 'groups']
|
|
|
|
|
groups_namespace = self.datavars_module.main
|
|
|
|
|
for namespace in namespaces:
|
|
|
|
@ -2065,6 +2095,9 @@ class DirectoryProcessor:
|
|
|
|
|
groups_namespace[namespace] = Variables()
|
|
|
|
|
groups_namespace = groups_namespace[namespace]
|
|
|
|
|
|
|
|
|
|
print('GROUPS NAMESPACE:')
|
|
|
|
|
print(groups_namespace)
|
|
|
|
|
|
|
|
|
|
atom_dict = PackageAtomParser.parse_atom_name(package_atom)
|
|
|
|
|
if isinstance(self.datavars_module, (Datavars, NamespaceNode)):
|
|
|
|
|
if group_name not in groups_namespace:
|
|
|
|
@ -2074,24 +2107,59 @@ class DirectoryProcessor:
|
|
|
|
|
else:
|
|
|
|
|
group_var = groups_namespace[group_name]
|
|
|
|
|
group_table = group_var.get_value().get_table()
|
|
|
|
|
if not self.check_existance_in_group(atom_dict, group_table):
|
|
|
|
|
check_result = self.check_existance_in_group(atom_dict,
|
|
|
|
|
group_table)
|
|
|
|
|
if check_result == -2:
|
|
|
|
|
group_table.append(atom_dict)
|
|
|
|
|
elif check_result >= 0:
|
|
|
|
|
group_table[check_result] = atom_dict
|
|
|
|
|
|
|
|
|
|
if check_result != -1:
|
|
|
|
|
group_var.source = group_table
|
|
|
|
|
else:
|
|
|
|
|
if group_name not in groups_namespace:
|
|
|
|
|
groups_namespace[group_name] = [atom_dict]
|
|
|
|
|
else:
|
|
|
|
|
group_table = groups_namespace[group_name]
|
|
|
|
|
if not self.check_existance_in_group(atom_dict, group_table):
|
|
|
|
|
check_result = self.check_existance_in_group(atom_dict,
|
|
|
|
|
group_table)
|
|
|
|
|
if check_result == -2:
|
|
|
|
|
group_table.append(atom_dict)
|
|
|
|
|
elif check_result >= 0:
|
|
|
|
|
group_table[check_result] = atom_dict
|
|
|
|
|
|
|
|
|
|
if check_result != -1:
|
|
|
|
|
groups_namespace[group_name] = group_table
|
|
|
|
|
|
|
|
|
|
def check_existance_in_group(self, atom_dict: dict, group: list):
|
|
|
|
|
def check_existance_in_group(self, atom_dict: dict, group: list) -> bool:
|
|
|
|
|
'''Метод для проверки наличия в таблице групп указанного пакета, а
|
|
|
|
|
также для сравнения данных о пакете из таблицы и из атом словаря.
|
|
|
|
|
Возвращает:
|
|
|
|
|
-2 -- если пакета нет;
|
|
|
|
|
-1 -- если присутствует и полностью совпадает;
|
|
|
|
|
>=0 -- если имеющиеся в таблице данные о пакете должены быть заменены
|
|
|
|
|
на указанные. В этом случае возвращается индекс старых данных в
|
|
|
|
|
таблице. '''
|
|
|
|
|
index = 0
|
|
|
|
|
|
|
|
|
|
for group_atom in group:
|
|
|
|
|
for field in ['category', 'name', 'version', 'slot']:
|
|
|
|
|
if atom_dict[field] != group_atom[field]:
|
|
|
|
|
continue
|
|
|
|
|
return True
|
|
|
|
|
if (atom_dict['category'] != group_atom['category'] and
|
|
|
|
|
atom_dict['name'] != group_atom['name']):
|
|
|
|
|
index += 1
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
if (atom_dict['slot'] is not None and
|
|
|
|
|
group_atom['slot'] is not None):
|
|
|
|
|
if atom_dict['slot'] != group_atom['slot']:
|
|
|
|
|
return -2
|
|
|
|
|
|
|
|
|
|
if atom_dict['version'] is not None:
|
|
|
|
|
if (group_atom['version'] is None or
|
|
|
|
|
atom_dict['version'] != group_atom['version']):
|
|
|
|
|
return index
|
|
|
|
|
|
|
|
|
|
return -1
|
|
|
|
|
return -2
|
|
|
|
|
|
|
|
|
|
def _make_current_template_var(self) -> None:
|
|
|
|
|
var_path = ['main', 'cl']
|
|
|
|
@ -2120,6 +2188,7 @@ class DirectoryProcessor:
|
|
|
|
|
# Режим заполнения очередей директорий пакетов, необходимых для более
|
|
|
|
|
# быстрой обработки параметра merge.
|
|
|
|
|
self.fill_trees = bool(self.for_package)
|
|
|
|
|
|
|
|
|
|
if self.for_package:
|
|
|
|
|
if self.for_package is NonePackage:
|
|
|
|
|
package = self.for_package
|
|
|
|
@ -2129,6 +2198,7 @@ class DirectoryProcessor:
|
|
|
|
|
else:
|
|
|
|
|
package = None
|
|
|
|
|
|
|
|
|
|
self.base_directory: str
|
|
|
|
|
for directory_path in self.template_paths:
|
|
|
|
|
self.base_directory = directory_path.strip()
|
|
|
|
|
entries = os.scandir(self.base_directory)
|
|
|
|
@ -2163,11 +2233,12 @@ class DirectoryProcessor:
|
|
|
|
|
self.template_executor.save_changes()
|
|
|
|
|
return self.template_executor.changed_files
|
|
|
|
|
|
|
|
|
|
def _execute_handlers(self):
|
|
|
|
|
def _execute_handlers(self) -> None:
|
|
|
|
|
'''Метод для запуска обработчиков добавленных в очередь обработчиков
|
|
|
|
|
с помощью параметра notify.'''
|
|
|
|
|
self.output.set_info('Processing handlers...')
|
|
|
|
|
index = 0
|
|
|
|
|
|
|
|
|
|
while index < len(self._handlers_queue):
|
|
|
|
|
handler = self._handlers_queue[index]
|
|
|
|
|
index += 1
|
|
|
|
@ -2219,7 +2290,7 @@ class DirectoryProcessor:
|
|
|
|
|
FILE, handler_path,
|
|
|
|
|
template_text=handler_text)
|
|
|
|
|
|
|
|
|
|
def _merge_packages(self):
|
|
|
|
|
def _merge_packages(self) -> None:
|
|
|
|
|
'''Метод для выполнения шаблонов относящихся к пакетам, указанным во
|
|
|
|
|
всех встреченных значениях параметра merge.'''
|
|
|
|
|
not_merged_packages = []
|
|
|
|
@ -2283,7 +2354,7 @@ class DirectoryProcessor:
|
|
|
|
|
else:
|
|
|
|
|
self.output.set_success('All packages are merged.')
|
|
|
|
|
|
|
|
|
|
def _run_exec_files(self):
|
|
|
|
|
def _run_exec_files(self) -> None:
|
|
|
|
|
'''Метод для выполнения скриптов, полученных в результате обработки
|
|
|
|
|
шаблонов с параметром exec.'''
|
|
|
|
|
for exec_file_path, exec_info in\
|
|
|
|
@ -2306,28 +2377,11 @@ class DirectoryProcessor:
|
|
|
|
|
except TemplateExecutorError as error:
|
|
|
|
|
self.output.set_error(str(error))
|
|
|
|
|
|
|
|
|
|
def _get_directories_queue(self, path: str) -> tuple:
|
|
|
|
|
'''Уже не актуальный метод для построение очередей из путей к
|
|
|
|
|
шаблонам. Хотя возможно еще пригодится.'''
|
|
|
|
|
directories_queue = []
|
|
|
|
|
|
|
|
|
|
for base_dir in self.template_paths:
|
|
|
|
|
base_dir = base_dir.strip()
|
|
|
|
|
if path.startswith(base_dir):
|
|
|
|
|
base_directory = base_dir
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
while path != base_directory:
|
|
|
|
|
path, dir_name = os.path.split(path)
|
|
|
|
|
directories_queue.append(dir_name)
|
|
|
|
|
|
|
|
|
|
return base_directory, directories_queue
|
|
|
|
|
|
|
|
|
|
def _walk_directory_tree(self, current_directory_path: str,
|
|
|
|
|
current_target_path: str,
|
|
|
|
|
directory_parameters: ParametersContainer(),
|
|
|
|
|
directory_tree={},
|
|
|
|
|
package=None) -> None:
|
|
|
|
|
directory_parameters: ParametersContainer,
|
|
|
|
|
directory_tree: Union[dict, DirectoryTree] = {},
|
|
|
|
|
package: Union[Package, None] = None) -> None:
|
|
|
|
|
'''Метод для рекурсивного обхода директорий с шаблонами, а также, при
|
|
|
|
|
необходимости, заполнения деревьев директорий шаблонов, с помощью
|
|
|
|
|
которых далее выполняются шаблоны пакетов из merge.'''
|
|
|
|
@ -2389,6 +2443,7 @@ class DirectoryProcessor:
|
|
|
|
|
directory_tree = {}
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# Хэндлеры, вложенные в другие хэндлеры не разрешены.
|
|
|
|
|
if self._handling is not None and directory_parameters.handler:
|
|
|
|
|
if directory_parameters.handler != self._handling:
|
|
|
|
|
self.output.set_error("'handler' parameter is not"
|
|
|
|
@ -2403,6 +2458,8 @@ class DirectoryProcessor:
|
|
|
|
|
if pkg not in self.processed_packages:
|
|
|
|
|
self.packages_to_merge.add(pkg)
|
|
|
|
|
|
|
|
|
|
# Если присутствует параметр notify, в котором указаны хэндлеры для
|
|
|
|
|
# последующего выполнения -- добавляем их в очередь.
|
|
|
|
|
if directory_parameters.notify:
|
|
|
|
|
for handler_id in directory_parameters.notify:
|
|
|
|
|
if handler_id not in self._handlers_queue:
|
|
|
|
@ -2425,6 +2482,7 @@ class DirectoryProcessor:
|
|
|
|
|
current_target_path = os.path.join(current_target_path,
|
|
|
|
|
directory_name)
|
|
|
|
|
|
|
|
|
|
# Для директорий по умолчанию append = join.
|
|
|
|
|
if not directory_parameters.append:
|
|
|
|
|
directory_parameters.set_parameter({'append': 'join'})
|
|
|
|
|
|
|
|
|
@ -2459,7 +2517,7 @@ class DirectoryProcessor:
|
|
|
|
|
template_path = os.path.join(current_directory_path,
|
|
|
|
|
template_name)
|
|
|
|
|
|
|
|
|
|
# Применяем к файлу шаблона шаблонизатор.
|
|
|
|
|
# Обрабатываем файл шаблона шаблонизатором.
|
|
|
|
|
template_text = self._parse_template(template_parameters,
|
|
|
|
|
template_name,
|
|
|
|
|
FILE, current_directory_path)
|
|
|
|
@ -2507,6 +2565,8 @@ class DirectoryProcessor:
|
|
|
|
|
if pkg not in self.processed_packages:
|
|
|
|
|
self.packages_to_merge.add(pkg)
|
|
|
|
|
|
|
|
|
|
# Если присутствует параметр notify, в котором указаны хэндлеры для
|
|
|
|
|
# последующего выполнения -- добавляем их в очередь.
|
|
|
|
|
if template_parameters.notify:
|
|
|
|
|
for handler_id in template_parameters.notify:
|
|
|
|
|
if handler_id not in self._handlers_queue:
|
|
|
|
@ -2572,7 +2632,8 @@ class DirectoryProcessor:
|
|
|
|
|
directory_tree = {}
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
def _scan_directory(self, directory_path: str) -> tuple:
|
|
|
|
|
def _scan_directory(self, directory_path: str
|
|
|
|
|
) -> Tuple[List[str], List[str]]:
|
|
|
|
|
'''Метод для получения и фильтрования списка файлов и директорий,
|
|
|
|
|
содержащихся в директории шаблонов.'''
|
|
|
|
|
template_files = []
|
|
|
|
@ -2604,9 +2665,11 @@ class DirectoryProcessor:
|
|
|
|
|
return False
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def _get_files_and_dirs_from_tree(self, template_files,
|
|
|
|
|
template_directories,
|
|
|
|
|
directory_tree: DirectoryTree):
|
|
|
|
|
def _get_files_and_dirs_from_tree(self, template_files: List[str],
|
|
|
|
|
template_directories: List[str],
|
|
|
|
|
directory_tree: DirectoryTree
|
|
|
|
|
) -> Tuple[List[str],
|
|
|
|
|
List[DirectoryTree]]:
|
|
|
|
|
'''Метод для получения списков файлов и директорий из дерева
|
|
|
|
|
директорий.'''
|
|
|
|
|
tree_files = []
|
|
|
|
@ -2622,8 +2685,8 @@ class DirectoryProcessor:
|
|
|
|
|
|
|
|
|
|
return tree_directories, tree_files
|
|
|
|
|
|
|
|
|
|
def _make_target_path(self, target_path, template_name,
|
|
|
|
|
parameters):
|
|
|
|
|
def _make_target_path(self, target_path: str, template_name: str,
|
|
|
|
|
parameters: ParametersContainer) -> str:
|
|
|
|
|
'''Метод для получения пути к целевому файлу с учетом наличия
|
|
|
|
|
параметров name, path и append = skip.'''
|
|
|
|
|
# Если есть параметр name -- меняем имя шаблона.
|
|
|
|
@ -2642,10 +2705,10 @@ class DirectoryProcessor:
|
|
|
|
|
template_name)
|
|
|
|
|
return target_path
|
|
|
|
|
|
|
|
|
|
def _parse_template(self, parameters,
|
|
|
|
|
template_name,
|
|
|
|
|
template_type,
|
|
|
|
|
template_directory):
|
|
|
|
|
def _parse_template(self, parameters: ParametersContainer,
|
|
|
|
|
template_name: str,
|
|
|
|
|
template_type: int,
|
|
|
|
|
template_directory: str) -> Union[str, bool]:
|
|
|
|
|
'''Метод для разбора шаблонов, получения значений их параметров и их
|
|
|
|
|
текста после отработки шаблонизитора.'''
|
|
|
|
|
if template_type == DIR:
|
|
|
|
@ -2657,6 +2720,7 @@ class DirectoryProcessor:
|
|
|
|
|
self.datavars_module.main.cl['current_template'].set(template_path)
|
|
|
|
|
else:
|
|
|
|
|
self.datavars_module.main.cl['current_template'] = template_path
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
self.template_engine.process_template(template_name,
|
|
|
|
|
template_type,
|
|
|
|
@ -2673,12 +2737,13 @@ class DirectoryProcessor:
|
|
|
|
|
template_path))
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def _execute_template(self, target_path,
|
|
|
|
|
parameters,
|
|
|
|
|
template_type,
|
|
|
|
|
template_path,
|
|
|
|
|
template_text='',
|
|
|
|
|
package=None):
|
|
|
|
|
def _execute_template(self, target_path: str,
|
|
|
|
|
parameters: ParametersContainer,
|
|
|
|
|
template_type: int,
|
|
|
|
|
template_path: str,
|
|
|
|
|
template_text: str = '',
|
|
|
|
|
package: Union[Package, None] = None
|
|
|
|
|
) -> Union[bool, str]:
|
|
|
|
|
'''Метод для наложения шаблонов и обработки информации полученной после
|
|
|
|
|
наложения.'''
|
|
|
|
|
try:
|
|
|
|
@ -2727,7 +2792,8 @@ class DirectoryProcessor:
|
|
|
|
|
format(template_path))
|
|
|
|
|
return target_path
|
|
|
|
|
|
|
|
|
|
def _update_package_tree(self, package, current_level_tree):
|
|
|
|
|
def _update_package_tree(self, package: Package,
|
|
|
|
|
current_level_tree: Union[None, dict]) -> None:
|
|
|
|
|
'''Метод для обновления деревьев директорий пакетов, необходимых для
|
|
|
|
|
обработки шаблонов пакетов из значения параметра merge.'''
|
|
|
|
|
# Если текущему уровню соответствует заглушка None или он содержит
|
|
|
|
@ -2743,12 +2809,13 @@ class DirectoryProcessor:
|
|
|
|
|
# Если для данного пакета еще нет дерева --
|
|
|
|
|
# копируем для него текущее.
|
|
|
|
|
directory_tree = DirectoryTree(self.base_directory)
|
|
|
|
|
directory_tree.update_tree(
|
|
|
|
|
copy.deepcopy(self.directory_tree))
|
|
|
|
|
directory_tree.update_tree(copy.deepcopy(self.directory_tree))
|
|
|
|
|
self.packages_file_trees[package] = directory_tree
|
|
|
|
|
|
|
|
|
|
def _check_package_and_action(self, parameters, template_path,
|
|
|
|
|
directory_tree=None):
|
|
|
|
|
def _check_package_and_action(self, parameters: ParametersContainer,
|
|
|
|
|
template_path: str,
|
|
|
|
|
directory_tree: Union[dict, None] = None
|
|
|
|
|
) -> bool:
|
|
|
|
|
'''Метод для проверки параметров action и package во время обработки
|
|
|
|
|
каталогов с шаблонами. Если среди аргументов указано также
|
|
|
|
|
дерево каталогов, то в случае несовпадения значений package для файла
|
|
|
|
@ -2790,6 +2857,9 @@ class DirectoryProcessor:
|
|
|
|
|
|
|
|
|
|
if parameters.package != self.for_package:
|
|
|
|
|
if directory_tree is not None:
|
|
|
|
|
# Если есть дерево, которое собирается для текущих шаблонов
|
|
|
|
|
# и параметр package шаблона не совпадает с текущим,
|
|
|
|
|
# ставим заглушку, в качестве которой используется None.
|
|
|
|
|
template_name = os.path.basename(template_path)
|
|
|
|
|
directory_tree[template_name] = None
|
|
|
|
|
self.output.set_warning(
|
|
|
|
@ -2804,17 +2874,17 @@ class DirectoryProcessor:
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
@contextmanager
|
|
|
|
|
def _start_handling(self, handler):
|
|
|
|
|
def _start_handling(self, handler_id: str):
|
|
|
|
|
'''Метод для перевода обработчика каталогов в режим обработки
|
|
|
|
|
хэндлеров.'''
|
|
|
|
|
try:
|
|
|
|
|
self._handling = handler
|
|
|
|
|
self._handling = handler_id
|
|
|
|
|
yield self
|
|
|
|
|
finally:
|
|
|
|
|
self._handling = None
|
|
|
|
|
|
|
|
|
|
@contextmanager
|
|
|
|
|
def _set_current_package(self, package):
|
|
|
|
|
def _set_current_package(self, package: Package):
|
|
|
|
|
'''Метод для указания в шаблонизаторе пакета, для которого в данный
|
|
|
|
|
момент проводим настройку. Пока не используется.'''
|
|
|
|
|
try:
|
|
|
|
|