|
|
@@ -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: |
|
|
|