Server development is started. Ability to run templates using scripts is added. fixed #14

master
Иванов Денис 4 years ago
parent b6116fccfd
commit 778f665561

1
.gitignore vendored

@ -1,6 +1,7 @@
*.pyc *.pyc
*.pyo *.pyo
*.swp *.swp
*.sock
build/ build/
dist/ dist/
calculate_lib.egg-info/ calculate_lib.egg-info/

@ -1,14 +1,13 @@
from types import FunctionType
from typing import Union, Callable, List from typing import Union, Callable, List
from calculate.scripts.scripts import Script from ..scripts.scripts import Script
from calculate.variables.parameters import Parameters from ..parameters.parameters import BaseParameter, Parameters
from calculate.variables.datavars import NamespaceNode, DependenceAPI,\ from ..variables.datavars import NamespaceNode, DependenceAPI,\
DependenceError, VariableNotFoundError VariableNotFoundError
from calculate.variables.loader import Datavars from ..variables.loader import Datavars
from calculate.utils.io_module import IOModule from ..utils.io_module import IOModule
class CommandCreationError(Exception): class CommandDescriptionError(Exception):
'''Исключение кидаемое при наличии ошибок во время создания объекта '''Исключение кидаемое при наличии ошибок во время создания объекта
команды.''' команды.'''
def __init__(self, message: str, command_id: str = '', title: str = ''): def __init__(self, message: str, command_id: str = '', title: str = ''):
@ -38,103 +37,182 @@ class CommandInitializationError(Exception):
class Command: class Command:
'''Класс команды, предназначен для хранения информации о команде и привязки ее к модулю ввода/вывода''' '''Класс описания команды, предназначен для хранения информации о команде
def __init__(self, command_id: str = '', category: str = '', и создания по данному описанию объекта лаунчера команды.'''
def __init__(self, command_id: str = '',
category: str = '',
title: str = '', title: str = '',
script: Union[Callable, Script, None] = None, script: Union[Callable, Script, None] = None,
parameters: Union[Parameters, None] = None, args: Union[tuple, list] = tuple(),
parameters: Union[List[BaseParameter], None] = None,
namespace: Union[str, NamespaceNode, None] = None, namespace: Union[str, NamespaceNode, None] = None,
command: str = '', gui: bool = False, command: str = '',
gui: bool = False,
icon: Union[str, List[str]] = '', icon: Union[str, List[str]] = '',
setvars: dict = {}, rights: List[str] = []): setvars: dict = {},
self._datavars: Union[Datavars, NamespaceNode, None] = None rights: List[str] = []):
# Идентификатор команды обязателен.
if not command_id: if not command_id:
raise CommandCreationError('command ID is not set') raise CommandDescriptionError('command ID is not set')
self._id: str = command_id self.__id: str = command_id
# Если собственно команда не была указана, получаем ее из ID. # Если собственно команда не была указана, получаем ее из ID.
if command: if command:
self._command: str = command self.__command: str = command
else: else:
self._command: str = f'cl_{self._id}' self.__command: str = f'cl_{self.__id}'
# Название команды.
if not title: if not title:
raise CommandCreationError("title is not set", raise CommandDescriptionError("title is not set",
command_id=command_id) command_id=command_id)
self._title: str = title self.__title: str = title
# Категория, к которой относится команда.
if not category: if not category:
raise CommandCreationError("category is not set", raise CommandDescriptionError("category is not set",
command_title=title) command_title=title)
self._category: str = category self.__category: str = category
# Пространство имен относительно которого, будет выполняться скрипт. # Пространство имен относительно которого, будет выполняться скрипт.
self._namespace: Union[str, NamespaceNode, None] = namespace self.__namespace: Union[str, NamespaceNode, None] = namespace
# Параметры, указываемые при вызове этой команды. # Параметры, указываемые при вызове этой команды.
if parameters is None: if parameters is None:
raise CommandCreationError("parameters is not set", raise CommandDescriptionError("parameters is not set",
command_title=title) command_title=title)
self._parameters: Parameters = parameters self.__parameters: List[BaseParameter] = parameters
# Скрипт выполняемый при вызове этой команды. # Скрипт выполняемый при вызове этой команды.
if not script: if not script:
raise CommandCreationError("script is not set", raise CommandDescriptionError("script is not set",
command_title=title) command_title=title)
elif isinstance(script, FunctionType): self.__script: Script = script
# Поддержка способа описания скрипта в функции.
self._script: Script = script() # Аргументы, с которыми будет запущен скрипт. Пока только статичные
else: # значения.
self._script: Script = script self.__args = args
# Параметр указывающий, должена ли данная команда отображаться в # Параметр указывающий, должена ли данная команда отображаться в
# графическом интерфейсе. # графическом интерфейсе.
self._gui: bool = gui self.__gui: bool = gui
# Устанавливаем название иконки. # Устанавливаем название иконки.
if not icon and self._gui: if not icon and self.__gui:
raise CommandCreationError("icon is not set", raise CommandDescriptionError("icon is not set",
command_title=title) command_title=title)
self._icon: Union[str, List[str]] = icon self.__icon: Union[str, List[str]] = icon
# Словарь с переменными и значениями, которые нужно им присвоить. # Словарь с переменными и значениями, которые нужно им присвоить.
self._setvars = setvars self.__setvars: dict = setvars
# Права, необходимые данной команде. # Права, необходимые данной команде.
# TODO разобраться с тем, как использовать.
if not rights: if not rights:
raise CommandCreationError("rights is not set", raise CommandDescriptionError("rights is not set",
title=title) title=title)
self._rights = rights self.__rights = rights
def make_runner(self, datavars: Union[Datavars, NamespaceNode],
output: IOModule):
'''Метод создающий на основе данного описания команды экземпляр раннера.
'''
return CommandRunner(self, datavars, output)
# Интерфейс иммутабельного объекта описания команды.
@property
def id(self) -> str:
return self.__id
@property
def command(self) -> str:
return self.__command
@property
def title(self) -> str:
return self.__title
@property
def category(self) -> str:
return self.__category
@property
def namespace(self) -> str:
return self.__namespace
@property
def parameters(self) -> str:
return self.__parameters
@property @property
def script(self) -> Script: def script(self) -> Script:
return self._script return self.__script
def initialize(self, datavars: Union[Datavars, NamespaceNode], @property
output: IOModule) -> 'Command': def args(self) -> tuple:
'''Метод для инициализации всего, что нужно инициализировать в команде. return self.__args
'''
@property
def gui(self) -> bool:
return self.__gui
@property
def icon(self) -> Union[str, List[str]]:
return self.__icon
@property
def setvars(self) -> dict:
return self.__setvars
@property
def rights(self) -> List[str]:
return self.__rights
def __repr__(self):
return f"<Command: {self.__title}>"
class CommandRunner:
def __init__(self, command: Command,
datavars: Union[Datavars, NamespaceNode],
output: IOModule):
'''Класс инкапулирующий все данные о команде, а также необходимые для
нее параметры, модуль ввода-вывода, переменные и прочее. Предназначен
для конфигурации команды и запуска ее в воркере.'''
self._command = command
self._datavars = datavars self._datavars = datavars
self._output = output self._output = output
# Сначала найдем пространство имен. # Ищем пространство имен.
if self._namespace is not None: if self._command.namespace is not None:
if isinstance(self._namespace, str): if isinstance(self._command.namespace, str):
self._namespace = self._find_namespace(self._namespace, self._namespace = self._find_namespace(
datavars) self._command.namespace,
datavars)
# Инициализируем параметры. # Инициализируем параметры.
self._parameters.initialize(datavars) self._parameters = Parameters(self._datavars)
self._parameters.add(*self._command.parameters)
# Получаем запускатель скрипта.
self._script_launcher = self._command.script.make_launcher(
output,
datavars,
self._namespace)
# Устанавливаем переменные, если нужно.
if self._command.setvars:
self._set_variables(self._command.setvars,
datavars, self._namespace)
# Инициализируем скрипт. def run_command(self):
self._script.initialize(output, datavars, self._namespace) args = self._command.args
self._script_launcher(*args)
# Устанавливаем переменные, если нужно. def set_parameters(self, values: dict):
if self._setvars: pass
self._set_variables(self._setvars, datavars, self._namespace)
return self def get_parameters(self, group: Union[str, None] = None):
'''Метод для установки параметров.'''
pass
def _find_namespace(self, namespace_path: str, def _find_namespace(self, namespace_path: str,
datavars: Union[Datavars, NamespaceNode] datavars: Union[Datavars, NamespaceNode]

@ -0,0 +1,22 @@
from logging import DEBUG
dictLogConfig = {
"version": 1,
"formatters": {
"form_1": {
"format":
'%(asctime)s %(levelname)s: %(message)s'
}
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"formatter": "form_1",
"level": DEBUG
}
},
"root": {
"handlers": ['console'],
"level": DEBUG
}
}

@ -1,10 +1,10 @@
# vim: fileencoding=utf-8 # vim: fileencoding=utf-8
# #
from .datavars import DependenceAPI, VariableWrapper, NamespaceNode from ..variables.datavars import DependenceAPI, VariableWrapper, NamespaceNode
from .loader import Datavars from ..variables.loader import Datavars
from collections import OrderedDict from collections import OrderedDict
from contextlib import contextmanager from contextlib import contextmanager
from typing import Tuple, Union from typing import Tuple, Union, Any
class ParameterError(Exception): class ParameterError(Exception):
@ -541,7 +541,7 @@ class GroupWrapper:
class Parameters: class Parameters:
'''Класс контейнера параметров.''' '''Класс контейнера параметров.'''
def __init__(self, datavars: Datavars = None, check_order=[]): def __init__(self, datavars: Datavars, check_order=[]):
self._datavars = datavars self._datavars = datavars
self._parameters = OrderedDict() self._parameters = OrderedDict()
self._validation_dict = OrderedDict() self._validation_dict = OrderedDict()
@ -558,24 +558,8 @@ class Parameters:
# параметров. # параметров.
self._gui_helpers = {} self._gui_helpers = {}
self._initialized = False
def initialize(self, datavars: Union[Datavars, NamespaceNode]):
if not self._initialized:
self._datavars = datavars
if self._parameters:
parameters = self._parameters
self._parameters = OrderedDict()
self._add(parameters)
self._initialized = True
return self
def add(self, *parameters: Tuple[BaseParameter]): def add(self, *parameters: Tuple[BaseParameter]):
'''Метод для добавления некоторой совокупности параметров.''' '''Метод для добавления некоторой совокупности параметров.'''
if not self._initialized:
self._parameters = []
self._parameters.extend(parameters)
for parameter in parameters: for parameter in parameters:
self.add_parameter(parameter) self.add_parameter(parameter)
@ -671,12 +655,12 @@ class Parameters:
self._validation = False self._validation = False
self._validation_dict = OrderedDict() self._validation_dict = OrderedDict()
def get_group_parameters(self, group_name): def get_group_parameters(self, group_name: str):
'''Метод для получения списка параметров, относящихся к указанной '''Метод для получения списка параметров, относящихся к указанной
группе.''' группе.'''
return self._parameters[group_name] return self._parameters[group_name]
def get_descriptions(self): def get_descriptions(self) -> dict:
'''Метод для получения словаря с описанием параметров.''' '''Метод для получения словаря с описанием параметров.'''
output = OrderedDict() output = OrderedDict()
for group, parameters in self._parameters.items(): for group, parameters in self._parameters.items():
@ -700,10 +684,10 @@ class Parameters:
return output return output
@property @property
def datavars(self): def datavars(self) -> Union[Datavars, NamespaceNode]:
return self._datavars return self._datavars
def __getitem__(self, name): def __getitem__(self, name: str) -> Any:
for group, parameters in self._parameters.items(): for group, parameters in self._parameters.items():
for parameter in parameters: for parameter in parameters:
if parameter._name == name or parameter._shortname == name: if parameter._name == name or parameter._shortname == name:

@ -3,9 +3,10 @@
import re import re
import inspect import inspect
from typing import Callable, Any, Union, List, Generator from typing import Callable, Any, Union, List, Generator
from calculate.templates.template_processor import DirectoryProcessor
from calculate.variables.datavars import DependenceAPI, DependenceError,\ from calculate.variables.datavars import DependenceAPI, DependenceError,\
VariableNode, NamespaceNode,\ VariableNode, NamespaceNode,\
HashType, TableType HashType, TableType, StringType
from calculate.variables.loader import Datavars from calculate.variables.loader import Datavars
from calculate.utils.io_module import IOModule from calculate.utils.io_module import IOModule
from collections.abc import Iterable, Mapping from collections.abc import Iterable, Mapping
@ -35,79 +36,112 @@ class ConditionError(Exception):
class Script: class Script:
'''Класс скрипта, собирает задачи, обработчики, блоки и прочее. Принимает '''Класс скрипта, собирает задачи, обработчики, блоки и прочее, хранит их
аргументы.''' в себе. Создает экземпляры лаунчеров.'''
def __init__(self, script_id: str, args: List[Any] = [], def __init__(self, script_id: str,
args: List[Any] = [],
success_message: str = None, success_message: str = None,
failed_message: str = None, failed_message: str = None,
interrupt_message: str = None): interrupt_message: str = None):
self._id: str = script_id self._id: str = script_id
self._items: List[Union['Task', 'Block', 'Handler', 'Run']] = None self.__items: List[Union['Task', 'Block', 'Handler', 'Run']] = None
self.__tasks_is_set: bool = False
self._args_names: list = args self.__args: list = args
self._args_vars: list = []
self._success_message: str = success_message self.__success_message: str = success_message
self._failed_message: str = failed_message self.__failed_message: str = failed_message
self._interrupt_message: str = interrupt_message self.__interrupt_message: str = interrupt_message
self._output: IOModule = None @property
self._datavars: Union[Datavars, NamespaceNode] = None def id(self) -> str:
self._namespace: Union[NamespaceNode, None] = None return self._id
self._handlers: set = set() @property
self._initialized: bool = False def args(self) -> list:
return self.__args
@property
def items(self) -> list:
return self.__items
@property
def success_message(self) -> str:
return self.__success_message
@property
def failed_message(self) -> str:
return self.__failed_message
@property
def interrupt_message(self) -> str:
return self.__interrupt_message
def tasks(self, *items: List[Union['Task', 'Block', 'Handler', 'Run']] def tasks(self, *items: List[Union['Task', 'Block', 'Handler', 'Run']]
) -> 'Script': ) -> 'Script':
'''Метод для указания задач и контейнеров, из которых состоит скрипт. '''Метод для указания задач и контейнеров, из которых состоит скрипт.
''' '''
self._items = items if self.__tasks_is_set:
raise ScriptError("Script object is immutable.")
self.__items = items
self.__tasks_is_set = bool(self.__items)
return self return self
def initialize(self, output: IOModule, def make_launcher(self, output: IOModule,
datavars: Union[Datavars, NamespaceNode], datavars: Union[Datavars, NamespaceNode],
namespace: NamespaceNode = None) -> 'Script': namespace: NamespaceNode = None) -> 'ScriptLauncher':
'''Метод для создания экземпляра объекта, для запуска данного
скрипта.'''
return ScriptLauncher(self, output, datavars, namespace)
class ScriptLauncher:
def __init__(self, script: Script,
output: IOModule,
datavars: Union[Datavars, NamespaceNode],
namespace: NamespaceNode):
'''Метод для инициализации, состоящей в установке модулей вывода, '''Метод для инициализации, состоящей в установке модулей вывода,
модуля переменных, текущего пространства имен, а также создании модуля переменных, текущего пространства имен, а также создании
переменных скрипта.''' переменных скрипта.'''
self._script = script
self._output = output self._output = output
self._datavars = datavars self._datavars = datavars
self._namespace = namespace self._namespace = namespace
if not self._initialized: self._script_namespace, self._args_vars = self.make_script_variables(
self._script_namespace, self._args_vars =\ script.id,
self.make_script_variables(self._id, script.args,
self._args_names, datavars)
self._datavars)
self._initialized = True
return self
def run(self) -> None: def run(self) -> None:
'''Метод для запуска задач, содержащихся в данном скрипте.''' '''Метод для запуска задач, содержащихся в данном скрипте.'''
essential_error = None essential_error = None
handlers_to_run = [] founded_handlers = []
handlers_to_run = set()
for task_item in self._items: for task_item in self._script.items:
if isinstance(task_item, Handler): if isinstance(task_item, Handler):
if task_item.id in self._handlers: founded_handlers.append(task_item)
handlers_to_run.append(task_item)
elif essential_error is None: elif essential_error is None:
try: try:
output = task_item.run(self._output, output = task_item.run(self._output,
self._datavars, self._datavars,
self._script_namespace, self._script_namespace,
self._namespace) self._namespace)
self._handlers.update(output) handlers_to_run.update(output)
except Exception as error: except Exception as error:
if isinstance(error, TaskError): if isinstance(error, TaskError):
self._handlers.update(error.handlers) handlers_to_run.update(error.handlers)
essential_error = error essential_error = error
for handler in handlers_to_run: for handler in founded_handlers:
if handler.id not in handlers_to_run:
continue
try: try:
task_item.run(self._output, self._datavars, handler.run(self._output, self._datavars,
self._script_namespace, self._namespace) self._script_namespace, self._namespace)
except Exception as error: except Exception as error:
# TODO Разобраться что делать с ошибками в обработчике. # TODO Разобраться что делать с ошибками в обработчике.
self._output.set_error(f"error in handler '{task_item.id}':" self._output.set_error(f"error in handler '{task_item.id}':"
@ -123,7 +157,7 @@ class Script:
'''Метод для создания переменных скрипта. Создает пространство имен '''Метод для создания переменных скрипта. Создает пространство имен
скрипта и переменных аргументов.''' скрипта и переменных аргументов.'''
if 'scripts' not in datavars: if 'scripts' not in datavars:
scripts = Script.make_scripts_namespace(datavars) scripts = ScriptLauncher.make_scripts_namespace(datavars)
else: else:
scripts = datavars.scripts scripts = datavars.scripts
@ -132,11 +166,14 @@ class Script:
scripts.add_namespace(current_script) scripts.add_namespace(current_script)
else: else:
current_script = scripts[script_id] current_script = scripts[script_id]
current_script.clear() # current_script.clear()
args_vars = [] args_vars = []
for arg in args: for arg in args:
args_vars.append(VariableNode(arg, current_script)) if arg not in current_script.variables:
args_vars.append(VariableNode(arg, current_script))
else:
args_vars.append(current_script.variables[arg])
return current_script, args_vars return current_script, args_vars
@staticmethod @staticmethod
@ -157,14 +194,15 @@ class Script:
def __call__(self, *args): def __call__(self, *args):
'''Метод для запуска скрипта с аргументами.''' '''Метод для запуска скрипта с аргументами.'''
if len(args) < len(self._args_names): if len(args) < len(self._script.args):
raise ScriptError((f"script '{self._id}' missing" raise ScriptError(
f" {len(self._args_names) - len(args)} required" (f"script '{self._script.id}' missing"
" arguments: '") + f" {len(self._script.args) - len(args)} required"
"', '".join(self._args_names[len(args):]) + "'") " arguments: '") +
elif len(args) > len(self._args_names): "', '".join(self._script.args[len(args):]) + "'")
raise ScriptError(f"script '{self._id}' takes" elif len(args) > len(self._script.args):
f" {len(self._args_names)} arguments, but" raise ScriptError(f"script '{self._script.id}' takes"
f" {len(self._script.args)} arguments, but"
f" {len(args)} were given") f" {len(args)} were given")
args_values = self._get_args_values(args, self._datavars, args_values = self._get_args_values(args, self._datavars,
@ -191,10 +229,106 @@ class Script:
return args_values return args_values
class RunTemplate:
'''Класс запускателя наложения шаблонов.'''
def __init__(self, id: str = '',
action: str = '',
package: str = '',
chroot_path: str = '',
root_path: str = '',
dbpkg: bool = True,
essential: bool = True,
**group_packages):
self._id: str = id
self._action: str = action
self._package: str = package or None
self._chroot_path: str = chroot_path or None
self._root_path: str = root_path or None
self._dbpkg = dbpkg
self._essential = essential
self._groups: dict = group_packages
def _initialize(self, output: IOModule,
datavars: Union[Datavars, NamespaceNode]):
# Проверяем наличие идентификатора.
if not self._id:
error_msg = "id is not set for templates runner"
if self._essential:
raise TaskError(error_msg)
else:
output.set_error(error_msg)
return set()
# Проверяем наличие параметра action, который обязателен.
if not self._action:
error_msg = ("action parameter is not set for templates runner"
f" '{self._id}'")
if self._essential:
raise TaskError(error_msg)
else:
output.set_error(error_msg)
return set()
# Если установлен chroot_path -- устанавливаем значение соответствующей
# переменной.
if self._chroot_path is not None:
if 'cl_chroot_path' in datavars.main:
datavars.main['cl_chroot_path'].source = self._chroot_path
else:
VariableNode("cl_chroot_path", datavars.main,
variable_type=StringType,
source=self._chroot_path)
# Если установлен root_path -- устанавливаем значение соответствующей
# переменной.
if self._root_path is not None:
if 'cl_root_path' in datavars.main:
datavars.main['cl_root_path'].source = self._root_path
else:
VariableNode("cl_root_path", datavars.main,
variable_type=StringType,
source=self._chroot_path)
if self._groups:
groups = list(self._groups.keys())
for group, atoms in groups:
if isinstance(atoms, str):
self._groups[group] = [name.strip() for name
in atoms.split(',')]
def run(self, output: IOModule,
datavars: Union[Datavars, NamespaceNode],
script_namespace: NamespaceNode,
namespace: NamespaceNode) -> set:
'''Метод для запуска наложения шаблонов.'''
self._initialize(output, datavars)
template_processor = DirectoryProcessor(self._action,
datavars_module=datavars,
package=self._package,
output_module=output,
dbpkg=self._dbpkg,
**self._groups)
changed_files = template_processor.process_template_directories()
self._create_result_var(script_namespace,
changed_files=changed_files)
return set()
def _create_result_var(self, script_namespace: NamespaceNode,
changed_files: dict = {},
skipped: list = []) -> None:
'''Метод для создания переменной задачи, в которую отправляются
результаты вычислений, имеющихся в функции action.'''
VariableNode(self._id, script_namespace, variable_type=HashType,
source={"changed": changed_files,
"skipped": skipped})
class Run: class Run:
'''Класс запускателя скриптов.''' '''Класс запускателя скриптов.'''
def __init__(self, script: Script, namespace: str = None, def __init__(self, script: Script, namespace: str = None,
args: List[Any] = [], when: 'Var' = None, args: List[Any] = [],
when: 'Var' = None,
essential: bool = True): essential: bool = True):
self._script: Script = script self._script: Script = script
self._namespace: str = namespace self._namespace: str = namespace
@ -204,9 +338,11 @@ class Run:
self._essential = essential self._essential = essential
def run(self, output: IOModule, datavars: Union[Datavars, NamespaceNode], def run(self, output: IOModule,
parent_namespace: NamespaceNode, namespace: NamespaceNode) -> set: datavars: Union[Datavars, NamespaceNode],
'''Метод для запуска скрипта, указанного имеющегося в запускателе.''' parent_namespace: NamespaceNode,
namespace: NamespaceNode) -> set:
'''Метод для запуска скрипта, указанного в запускателе.'''
if self._condition is None or self._condition(datavars, if self._condition is None or self._condition(datavars,
namespace, namespace,
parent_namespace): parent_namespace):
@ -222,8 +358,10 @@ class Run:
try: try:
# if not self._script._initialized: # if not self._script._initialized:
self._script.initialize(output, datavars, self._namespace) launcher = self._script.make_launcher(
self._script(*self._args) output, datavars,
namespace=self._namespace)
launcher(*self._args)
except (ScriptError, TaskError) as error: except (ScriptError, TaskError) as error:
if self._essential: if self._essential:
raise ScriptError(f"essential script '{self._script._id}'" raise ScriptError(f"essential script '{self._script._id}'"
@ -330,8 +468,6 @@ class Task:
else: else:
self._args = self._update_arguments(self._args_sources) self._args = self._update_arguments(self._args_sources)
return self
def run(self, output: IOModule, def run(self, output: IOModule,
datavars: Union[Datavars, NamespaceNode], datavars: Union[Datavars, NamespaceNode],
script_namespace: NamespaceNode, script_namespace: NamespaceNode,
@ -664,7 +800,8 @@ class Until(Loop):
class Block: class Block:
'''Класс блока задач.''' '''Класс блока задач.'''
def __init__(self, *tasks: List[Task], when: Union['Var', None] = None, def __init__(self, *tasks: List[Task],
when: Union['Var', None] = None,
loop: Loop = Loop()): loop: Loop = Loop()):
self._tasks: List[Task] = tasks self._tasks: List[Task] = tasks
self._rescue_tasks: List[Task] = None self._rescue_tasks: List[Task] = None
@ -736,7 +873,8 @@ class Handler:
def id(self) -> str: def id(self) -> str:
return self._id return self._id
def run(self, output: IOModule, datavars: Union[Datavars, NamespaceNode], def run(self, output: IOModule,
datavars: Union[Datavars, NamespaceNode],
script_namespace: NamespaceNode, script_namespace: NamespaceNode,
namespace: NamespaceNode) -> None: namespace: NamespaceNode) -> None:
'''Метод для запуска выполнения задач, содержащихся в обработчике.''' '''Метод для запуска выполнения задач, содержащихся в обработчике.'''

@ -0,0 +1,155 @@
from ..variables.loader import Datavars
from ..commands.commands import Command
from ..logging import dictLogConfig
from logging.config import dictConfig
from logging import getLogger
from typing import Callable
from fastapi import FastAPI
from .worker import Worker
import importlib
import uvicorn
import asyncio
import os
# TODO
# 1. Разобраться с описанием команд как ресурсов и со всем, что от них зависит.
# 2. Разобраться с объектами воркеров. И способом их функционирования.
class Server:
def __init__(self, socket_path='./input.sock',
datavars_path: str = 'calculate/vars/',
commands_path: str = 'calculate/commands'):
self._app = FastAPI()
self._socket_path = socket_path
self._event_loop = asyncio.get_event_loop()
# Конфигурируем логгирование.
dictConfig(dictLogConfig)
self._logger = getLogger("main")
self.log_msg = {'DEBUG': self._logger.debug,
'INFO': self._logger.info,
'WARNING': self._logger.warning,
'ERROR': self._logger.error,
'CRITICAL': self._logger.critical}
self._datavars = Datavars(variables_path=datavars_path,
logger=self._logger)
# Словарь описаний команд.
self._commands = self._get_commands_list(commands_path)
# Словарь CID и экземпляров команд, передаваемых воркерам.
self._commands_instances = {}
# Словарь WID и экземпляров процессов-воркеров, передаваемых воркерам.
self._workers = {}
# Соответствие путей обработчикам запросов для HTTP-метода GET.
self._add_routes(self._app.get,
{"/": self._get_root,
"/commands": self._get_commands,
"/commands/{cid}": self._get_command,
"/workers/{wid}": self._get_worker})
self._add_routes(self._app.post,
{"/commands/{command_id}": self._post_command})
def _get_commands_list(self, commands_path: str) -> list:
'''Метод для получения совокупности описаний команд.'''
output = {}
package = ".".join(commands_path.split("/"))
for entry in os.scandir(commands_path):
if (not entry.name.endswith('.py')
or entry.name in {"commands.py", "__init__.py"}):
continue
module_name = entry.name[:-3]
try:
module = importlib.import_module("{}.{}".format(package,
module_name))
for obj in dir(module):
if type(module.__getattribute__(obj)) == Command:
command_object = module.__getattribute__(obj)
output[command_object.id] = command_object
except Exception:
continue
return output
async def _get_root(self) -> dict:
'''Обработчик корневых запросов.'''
return {'msg': 'root msg'}
async def _get_commands(self) -> dict:
'''Обработчик, отвечающий на запросы списка команд.'''
response = {}
for command_id, command_object in self._commands.items():
response.update({command_id: {"title": command_object.title,
"category": command_object.category,
"icon": command_object.icon,
"command": command_object.command}})
return response
async def _get_command(self, cid: int) -> dict:
'''Обработчик запросов списка команд.'''
if cid not in self._commands_instances:
# TODO добавить какую-то обработку ошибки.
pass
return {'id': cid,
'name': f'command_{cid}'}
async def _get_worker(self, wid: int):
'''Тестовый '''
self._make_worker(wid=wid)
worker = self._workers[wid]
worker.run(None)
await worker.send({"text": "INFO"})
data = await worker.get()
if data['type'] == 'log':
self.log_msg[data['level']](data['msg'])
return data
async def _post_command(self, command_id: str) -> int:
if command_id not in self._commands:
# TODO добавить какую-то обработку ошибки.
pass
return
def _add_routes(self, method: Callable, routes: dict) -> None:
'''Метод для добавления методов.'''
for path, handler in routes.items():
router = method(path)
router(handler)
def _make_worker(self, wid: int = None):
'''Метод для создания воркера для команды.'''
worker = Worker(self._event_loop)
if wid is None:
self._workers[wid] = worker
return wid
elif not self._workers:
self._workers[0] = worker
return 0
else:
wid = max(self._workers.keys()) + 1
self._workers[wid] = worker
return wid
def _make_command(self, command_id: str) -> int:
'''Метод для создания команды по ее описанию.'''
command_description = self._commands[command]
@property
def app(self):
return self._app
def run(self):
'''Метод для запуска сервера.'''
# Выгружаем список команд.
uvicorn.run(self._app,
uds=self._socket_path)
if __name__ == '__main__':
server = Server()
server.run()

@ -0,0 +1,38 @@
from multiprocessing import Queue, Process
# from time import sleep
class Worker:
def __init__(self, loop):
self._output_queue = Queue()
self._in_queue = Queue()
self._event_loop = loop
async def send(self, data: dict):
self._in_queue.put(data)
async def get(self):
data = await self._event_loop.run_in_executor(None, self._get_output,
self._output_queue)
return data
@staticmethod
def _get_output(output_queue: Queue) -> dict:
return output_queue.get()
@staticmethod
def _main_loop(command, in_queue: Queue, out_queue: Queue):
data = in_queue.get()
print('\nworker for command:', command)
output = {"type": "log",
"level": "INFO",
"msg": f"recieved message {data['text']}"}
out_queue.put(output)
def run(self, command):
'''Метод для запуска процесса воркера с заданным '''
worker_process = Process(target=self._main_loop,
args=(command,
self._in_queue,
self._output_queue))
worker_process.start()

@ -429,8 +429,6 @@ class ParametersProcessor:
real_path = parameter_value real_path = parameter_value
# Ставим True, чтобы потом проверить этот параметр в postparse # Ставим True, чтобы потом проверить этот параметр в postparse
print(f'source value {parameter_value}')
print(f'source <- {real_path}')
if not os.path.exists(real_path): if not os.path.exists(real_path):
return True return True
@ -592,10 +590,6 @@ class ParametersProcessor:
else: else:
groups = self._parameters_container.group groups = self._parameters_container.group
print(f'PACKAGE: {parameter_value}')
print(f'PARSED: {package_atom}')
print(f'GROUPS: {groups}')
print(f'UNINSTALL: {self._groups.get("uninstall", None)}')
for group in groups: for group in groups:
if group == 'install': if group == 'install':
try: try:
@ -610,7 +604,6 @@ class ParametersProcessor:
self._parameters_container.remove_parameter('package') self._parameters_container.remove_parameter('package')
return return
print('check failed')
raise ConditionFailed(f"package '{parameter_value}'" raise ConditionFailed(f"package '{parameter_value}'"
" does not match the template condition", " does not match the template condition",
self.lineno) self.lineno)
@ -623,8 +616,6 @@ class ParametersProcessor:
if package[parameter] is not None: if package[parameter] is not None:
if (group_package[parameter] is None if (group_package[parameter] is None
or group_package[parameter] != package[parameter]): or group_package[parameter] != package[parameter]):
print(f'{group_package[parameter]} !='
f' {package[parameter]}')
break break
else: else:
if package['use_flags'] is not None: if package['use_flags'] is not None:

@ -719,8 +719,6 @@ class TemplateExecutor:
self.executor_output = {'target_path': None, self.executor_output = {'target_path': None,
'stdout': None, 'stdout': None,
'stderr': None} 'stderr': None}
print('TARGET PATH:', target_path)
if parameters.append == 'skip': if parameters.append == 'skip':
return self.executor_output return self.executor_output
@ -1762,7 +1760,8 @@ class DirectoryTree:
class DirectoryProcessor: class DirectoryProcessor:
'''Класс обработчика директорий шаблонов.''' '''Класс обработчика директорий шаблонов.'''
def __init__(self, action: str, datavars_module=Variables(), package='', def __init__(self, action: str, datavars_module=Variables(), package='',
output_module=IOModule(), dbpkg=True, **groups): output_module=IOModule(), dbpkg=True,
namespace: NamespaceNode = None, **groups):
if isinstance(action, list): if isinstance(action, list):
self.action = action self.action = action
else: else:
@ -1781,7 +1780,6 @@ class DirectoryProcessor:
if 'cl_root_path' in datavars_module.main: if 'cl_root_path' in datavars_module.main:
self.templates_root = join_paths(self.cl_chroot_path, self.templates_root = join_paths(self.cl_chroot_path,
datavars_module.main.cl_root_path) datavars_module.main.cl_root_path)
print('ROOT PATH:', self.templates_root)
else: else:
self.templates_root = self.cl_chroot_path self.templates_root = self.cl_chroot_path
@ -1826,7 +1824,11 @@ class DirectoryProcessor:
# Разбираем atom имена пакетов, указанных для групп пакетов. # Разбираем atom имена пакетов, указанных для групп пакетов.
if groups: if groups:
for group_name, package_name in groups.items(): for group_name, package_name in groups.items():
self._add_package_to_group(group_name, package_name) if isinstance(package_name, list):
for atom in package_name:
self._add_package_to_group(group_name, atom)
else:
self._add_package_to_group(group_name, package_name)
# Инициализируем шаблонизатор. # Инициализируем шаблонизатор.
self.template_engine = TemplateEngine( self.template_engine = TemplateEngine(
@ -1998,6 +2000,7 @@ class DirectoryProcessor:
self._run_exec_files() self._run_exec_files()
self.template_executor.save_changes() self.template_executor.save_changes()
return self.template_executor.changed_files
def _execute_handlers(self): def _execute_handlers(self):
'''Метод для запуска обработчиков добавленных в очередь обработчиков '''Метод для запуска обработчиков добавленных в очередь обработчиков
@ -2159,7 +2162,6 @@ class DirectoryProcessor:
необходимости, заполнения деревьев директорий шаблонов, с помощью необходимости, заполнения деревьев директорий шаблонов, с помощью
которых далее выполняются шаблоны пакетов из merge.''' которых далее выполняются шаблоны пакетов из merge.'''
directory_name = os.path.basename(current_directory_path) directory_name = os.path.basename(current_directory_path)
print('CURRENT TARGET PATH =', current_target_path)
# Если включено заполнение дерева создаем пустой словарь для сбора # Если включено заполнение дерева создаем пустой словарь для сбора
# содержимого текущей директории. # содержимого текущей директории.

@ -1,6 +1,7 @@
# vim: fileencoding=utf-8 # vim: fileencoding=utf-8
# #
import os import os
import logging
import importlib import importlib
import importlib.util import importlib.util
from jinja2 import Environment, FileSystemLoader from jinja2 import Environment, FileSystemLoader
@ -12,7 +13,6 @@ from calculate.variables.datavars import NamespaceNode, VariableNode,\
from calculate.utils.gentoo import ProfileWalker from calculate.utils.gentoo import ProfileWalker
from calculate.utils.files import read_file, FilesError from calculate.utils.files import read_file, FilesError
from calculate.utils.tools import Singleton from calculate.utils.tools import Singleton
from calculate.utils.io_module import IOModule
from pyparsing import Literal, Word, ZeroOrMore, Group, Optional, restOfLine,\ from pyparsing import Literal, Word, ZeroOrMore, Group, Optional, restOfLine,\
empty, printables, OneOrMore, lineno, line, SkipTo,\ empty, printables, OneOrMore, lineno, line, SkipTo,\
LineEnd, Combine, nums LineEnd, Combine, nums
@ -489,7 +489,7 @@ class VariableLoader:
def __init__(self, datavars, variables_path, repository_map=None): def __init__(self, datavars, variables_path, repository_map=None):
self.datavars = datavars self.datavars = datavars
self.output = datavars.output self.logger = datavars.logger
self.ini_filler = NamespaceIniFiller() self.ini_filler = NamespaceIniFiller()
self.variables_path = variables_path self.variables_path = variables_path
@ -519,9 +519,9 @@ class VariableLoader:
if section in current_namespace: if section in current_namespace:
current_namespace = current_namespace[section] current_namespace = current_namespace[section]
else: else:
self.output.set_error("Variable 'os.gentoo.repositories'" self.logger.error("Variable 'os.gentoo.repositories'"
" is not found. Can not load profile" " is not found. Can not load profile"
" variables.") " variables.")
return return
self.repository_map = self._get_repository_map(self.datavars) self.repository_map = self._get_repository_map(self.datavars)
@ -530,12 +530,12 @@ class VariableLoader:
'path' in self.datavars.os.gentoo.profile): 'path' in self.datavars.os.gentoo.profile):
profile_path = self.datavars.os.gentoo.profile.path profile_path = self.datavars.os.gentoo.profile.path
else: else:
self.output.set_error("Variable 'os.gentoo.profile.path'" self.logger.error("Variable 'os.gentoo.profile.path'"
" is not found. Can not load profile" " is not found. Can not load profile"
" variables.") " variables.")
return return
self.output.set_info("Load variables from profile: '{}'.".format( self.logger.info("Load variables from profile: '{}'.".format(
profile_path)) profile_path))
self._fill_from_profile_ini(profile_path) self._fill_from_profile_ini(profile_path)
@ -546,20 +546,20 @@ class VariableLoader:
env_order = self.datavars.system.env_order env_order = self.datavars.system.env_order
env_path = self.datavars.system.env_path env_path = self.datavars.system.env_path
except VariableNotFoundError as error: except VariableNotFoundError as error:
self.output.set_warning("Can not load additional variables: {}". self.logger.warning("Can not load additional variables: {}".
format(str(error))) format(str(error)))
return return
for ini_file in env_order: for ini_file in env_order:
self.output.set_info("Loading variables from file: '{}'".format( self.logger.info("Loading variables from file: '{}'".format(
ini_file)) ini_file))
if ini_file in env_path: if ini_file in env_path:
self.fill_from_custom_ini(env_path[ini_file].value) self.fill_from_custom_ini(env_path[ini_file].value)
self.output.set_success("Variables from '{}' are loaded". self.logger.info("Variables from '{}' are loaded".format(
format(ini_file)) ini_file))
else: else:
self.output.set_warning("File '{}' is not found. Variables are" self.logger.warning("File '{}' is not found. Variables are"
" not loaded".format(ini_file)) " not loaded".format(ini_file))
def _fill_from_package(self, current_namespace: NamespaceNode, def _fill_from_package(self, current_namespace: NamespaceNode,
directory_path: str, package: str) -> None: directory_path: str, package: str) -> None:
@ -575,6 +575,8 @@ class VariableLoader:
# Сначала загружаем переменные из файлов. # Сначала загружаем переменные из файлов.
for file_node in file_nodes: for file_node in file_nodes:
if not file_node.name.endswith('.py'):
continue
file_name = file_node.name[:-3] file_name = file_node.name[:-3]
Namespace.set_current_namespace(current_namespace) Namespace.set_current_namespace(current_namespace)
# with self.test(file_name, current_namespace): # with self.test(file_name, current_namespace):
@ -603,8 +605,8 @@ class VariableLoader:
ini_file_text = read_file(file_path) ini_file_text = read_file(file_path)
self.ini_filler.fill(self.datavars, ini_file_text) self.ini_filler.fill(self.datavars, ini_file_text)
except FilesError: except FilesError:
self.output.set_error("Can not load profile variables from" self.logger.error("Can not load profile variables from"
" unexisting file: {}".format(file_path)) " unexisting file: {}".format(file_path))
def _get_repository_map(self, datavars): def _get_repository_map(self, datavars):
'''Метод для получения из переменной словаря с репозиториями и путями '''Метод для получения из переменной словаря с репозиториями и путями
@ -621,13 +623,13 @@ class VariableLoader:
parsing_errors = self.ini_filler.errors parsing_errors = self.ini_filler.errors
if parsing_errors: if parsing_errors:
for error in parsing_errors: for error in parsing_errors:
self.output.set_error(error) self.logger.error(error)
self.output.set_warning('Some variables was not loaded.') self.logger.warning('Some variables was not loaded.')
else: else:
self.output.set_success('All variables are loaded.') self.logger.info('All variables are loaded.')
else: else:
self.output.set_error("Variables are not loaded. File '{}' does" self.logger.error("Variables are not loaded. File '{}' does"
" not exist.".format(file_path)) " not exist.".format(file_path))
@contextmanager @contextmanager
def test(self, file_name, namespace): def test(self, file_name, namespace):
@ -704,10 +706,16 @@ class CalculateIniSaver:
class Datavars: class Datavars:
'''Класс для хранения переменных и управления ими.''' '''Класс для хранения переменных и управления ими.'''
def __init__(self, variables_path='calculate/vars', repository_map=None, def __init__(self, variables_path='calculate/vars', repository_map=None,
io_module=IOModule()): logger=None):
self._variables_path = variables_path self._variables_path = variables_path
self._available_packages = self._get_available_packages() self._available_packages = self._get_available_packages()
self.output = io_module if logger is not None:
self.logger = logger
else:
logger = logging.getLogger("main")
# stream_handler = logging.StreamHandler()
# logger.addHandler(stream_handler)
self.logger = logger
self.root = NamespaceNode('<root>') self.root = NamespaceNode('<root>')
self._loader = VariableLoader(self, self._variables_path, self._loader = VariableLoader(self, self._variables_path,
@ -749,8 +757,7 @@ class Datavars:
def _load_package(self, package_name): def _load_package(self, package_name):
'''Метод для загрузки переменных содержащихся в указанном пакете.''' '''Метод для загрузки переменных содержащихся в указанном пакете.'''
self.output.set_info("Loading datavars package '{}'".format( self.logger.info("Loading datavars package '{}'".format(package_name))
package_name))
try: try:
self._loader.load_variables_package(package_name) self._loader.load_variables_package(package_name)
except Exception as error: except Exception as error:
@ -843,5 +850,5 @@ class Datavars:
dict_to_save = self.variables_to_save[target] dict_to_save = self.variables_to_save[target]
target_path = target_paths[target].value target_path = target_paths[target].value
saver.save_to_ini(target_path, dict_to_save) saver.save_to_ini(target_path, dict_to_save)
self.output.set_info("Variables for '{}' is saved in the" self.logger.info("Variables for '{}' is saved in the"
" file: {}".format(target, target_path)) " file: {}".format(target, target_path))

@ -0,0 +1,33 @@
import asyncio
import aiohttp
async def get_root(client):
async with client.get('http://localhost/') as resp:
assert resp.status == 200
return await resp.json()
async def main():
unix_conn = aiohttp.UnixConnector(path='./input.sock')
try:
async with aiohttp.ClientSession(connector=unix_conn) as client:
print('---------------------------')
print('Request GET "/" HTTP/1.1:')
response = await get_root(client)
print(f'Response: {response}')
print('---------------------------')
except KeyboardInterrupt:
raise
finally:
print('\nClosing connection...')
await unix_conn.close()
if __name__ == '__main__':
loop = asyncio.new_event_loop()
try:
loop.run_until_complete(main())
except KeyboardInterrupt:
loop.close()

@ -36,5 +36,6 @@ markers =
vars: marker for testing of the datavars module. vars: marker for testing of the datavars module.
parameters: marker for testing of the parameters module. parameters: marker for testing of the parameters module.
tasks: marker for testing of the tasks. scripts: marker for testing of the scripts.
commands: marker for testing of the commands. commands: marker for testing of the commands.
server: marker for testing of the server.

@ -0,0 +1,6 @@
from calculate.server.server import Server
if __name__ == "__main__":
server = Server()
server.run()

@ -40,7 +40,8 @@ def main():
datavars_module=datavars, datavars_module=datavars,
package=package, package=package,
output_module=io_module, output_module=io_module,
dbpkg=args.dbpkg) dbpkg=args.dbpkg,
**group_packages)
template_processor.process_template_directories() template_processor.process_template_directories()

@ -0,0 +1,39 @@
from calculate.parameters.parameters import BaseParameter, Integer,\
String, ValidationError
class MyShinyParameter(BaseParameter):
type = Integer()
def validate(self, var):
if var.value < 10:
raise ValidationError("The value must be greater than 10")
def bind_method(self, var):
return var.value, None
class AnotherParameter(BaseParameter):
type = String()
def bind_method(self):
return 'default string', None
def validate(self, value):
if not value.startswith('/var/lib'):
raise ValidationError("The value must starts with a"
" '/var/lib'")
class OneMoreParameter(BaseParameter):
type = String()
def bind_method(self):
return 'default string', None
def validate(self, value):
available_set = {'mystery', 'horror', 'weird'}
if value not in available_set:
raise ValidationError(f"The value '{value}' is not in"
f" available. Available values:"
f" {', '.join(available_set)}")

@ -0,0 +1,14 @@
from calculate.scripts.scripts import Script, Task
def action(output, arg1: str) -> str:
'''Задача выполняемая скриптом ниже.'''
return f'os.calculate = {arg1}'
# Тестовый скрипт.
test_script = Script('test_script'
).tasks(Task(id='task',
name="Task",
action=action,
args=["os.calculate"]))

@ -1,14 +1,15 @@
import os import os
import shutil import shutil
import pytest import pytest
from calculate.commands.commands import Command from calculate.commands.commands import Command
from calculate.scripts.scripts import Script, Task from calculate.parameters.parameters import Description
from calculate.variables.loader import Datavars from calculate.variables.loader import Datavars
from calculate.variables.parameters import Parameters, BaseParameter, Integer,\
String, ValidationError,\
Description
from calculate.utils.io_module import IOModule from calculate.utils.io_module import IOModule
from .parameters import MyShinyParameter, OneMoreParameter, AnotherParameter
from .scripts import test_script
TESTFILES_PATH = os.path.join(os.getcwd(), 'tests/commands/testfiles') TESTFILES_PATH = os.path.join(os.getcwd(), 'tests/commands/testfiles')
@ -24,78 +25,37 @@ class TestCommands:
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH, datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables')) 'variables'))
# Скрипт выполняемый командой.
def action(output: IOModule, arg1: str) -> str:
return f'os.calculate = {arg1}'
# Для обеспечения многопоточности вероятно придется делать так.
def test_script():
return Script('test_script'
).tasks(Task(id='task',
name="Task",
action=action,
args=["os.calculate"])
)
# Параметры данной команды.
test_parameters = Parameters().initialize(datavars)
class MyShinyParameter(BaseParameter):
type = Integer()
def validate(self, var):
if var.value < 10:
raise ValidationError("The value must be greater than 10")
def bind_method(self, var):
return var.value, None
class AnotherParameter(BaseParameter):
type = String()
def bind_method(self):
return 'default string', None
def validate(self, value):
if not value.startswith('/var/lib'):
raise ValidationError("The value must starts with a"
" '/var/lib'")
class OneMoreParameter(BaseParameter):
type = String()
def bind_method(self):
return 'default string', None
def validate(self, value):
available_set = {'mystery', 'horror', 'weird'}
if value not in available_set:
raise ValidationError(f"The value '{value}' is not in"
f" available. Available values:"
f" {', '.join(available_set)}")
test_parameters.add(
MyShinyParameter('my-shiny', 'The Shiniest ones',
Description(
short='The shiny parameter',
full='The shiniest thing known to science.',
usage='-S COUNT, --my-shiny COUNT'),
shortname='S').bind('os.linux.test_5'),
AnotherParameter('my-other', 'The Others',
Description(short='Not from this world.'),
shortname='o'),
OneMoreParameter('one-more', 'The Others',
Description(full='Plan 9 from outer space.')))
command = Command(command_id='test', command = Command(command_id='test',
category='Test Category', category='Test Category',
title='Test', title='Test',
script=test_script(), script=test_script,
parameters=test_parameters,
namespace='os', namespace='os',
rights=['group']) rights=['group'],
command.initialize(datavars, IOModule()) parameters=[
command.script.run() MyShinyParameter
(
'my-shiny', 'The Shiniest ones',
Description(
short='The shiny parameter',
full='The shiniest thing known to science.',
usage='-S COUNT, --my-shiny COUNT'),
shortname='S'
).bind('os.linux.test_5'),
AnotherParameter
(
'my-other', 'The Others',
Description(
short='Not from this world.'),
shortname='o'
),
OneMoreParameter
(
'one-more', 'The Others',
Description(full='Plan 9 from outer space.')
)])
command_runner = command.make_runner(datavars, IOModule())
command_runner.run_command()
assert datavars.scripts.test_script.task.result ==\ assert datavars.scripts.test_script.task.result ==\
'os.calculate = test1 test2' 'os.calculate = test1 test2'
@ -104,57 +64,7 @@ class TestCommands:
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH, datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables')) 'variables'))
# Скрипт выполняемый командой. test_parameters = [
def action(output: IOModule, arg1: str) -> str:
return f'os.calculate = {arg1}'
# Для обеспечения многопоточности вероятно придется делать так.
def test_script():
return Script('test_script'
).tasks(Task(id='task',
name="Task",
action=action,
args=["os.calculate"])
)
# Параметры данной команды.
test_parameters = Parameters().initialize(datavars)
class MyShinyParameter(BaseParameter):
type = Integer()
def validate(self, var):
if var.value < 10:
raise ValidationError("The value must be greater than 10")
def bind_method(self, var):
return var.value, None
class AnotherParameter(BaseParameter):
type = String()
def bind_method(self):
return 'default string', None
def validate(self, value):
if not value.startswith('/var/lib'):
raise ValidationError("The value must starts with a"
" '/var/lib'")
class OneMoreParameter(BaseParameter):
type = String()
def bind_method(self):
return 'default string', None
def validate(self, value):
available_set = {'mystery', 'horror', 'weird'}
if value not in available_set:
raise ValidationError(f"The value '{value}' is not in"
f" available. Available values:"
f" {', '.join(available_set)}")
test_parameters.add(
MyShinyParameter('my-shiny', 'The Shiniest ones', MyShinyParameter('my-shiny', 'The Shiniest ones',
Description( Description(
short='The shiny parameter', short='The shiny parameter',
@ -165,23 +75,24 @@ class TestCommands:
Description(short='Not from this world.'), Description(short='Not from this world.'),
shortname='o'), shortname='o'),
OneMoreParameter('one-more', 'The Others', OneMoreParameter('one-more', 'The Others',
Description(full='Plan 9 from outer space.'))) Description(full='Plan 9 from outer space.'))]
command = Command(command_id='test', command = Command(command_id='test',
category='Test Category', category='Test Category',
title='Test', title='Test',
script=test_script(), script=test_script,
parameters=test_parameters, parameters=test_parameters,
namespace='os', namespace='os',
gui=True, gui=True,
icon=['icon_1', 'icon_2'], icon=['icon_1', 'icon_2'],
setvars={'os.hashvar_0': {'value1': 'new_1', setvars={'os.hashvar_0':
'value2': 'new_2'}, {'value1': 'new_1',
'value2': 'new_2'},
'.linux.test_5': 1349}, '.linux.test_5': 1349},
rights=['install']) rights=['install'])
command.initialize(datavars, IOModule()) command_runner = command.make_runner(datavars, IOModule())
command.script.run() command_runner.run_command()
assert datavars.os.hashvar_0.get_hash() == {'value1': 'new_1', assert datavars.os.hashvar_0.get_hash() == {'value1': 'new_1',
'value2': 'new_2'} 'value2': 'new_2'}

@ -2,29 +2,29 @@ import os
import shutil import shutil
import pytest import pytest
from collections import OrderedDict from collections import OrderedDict
from calculate.variables.parameters import BaseParameter, Integer, String,\ from calculate.parameters.parameters import BaseParameter, Integer, String,\
Description, Parameters,\ Description, Parameters,\
ValidationError, Choice, Bool,\ ValidationError, Choice, Bool,\
List, CyclicValidationError,\ List, CyclicValidationError,\
ParameterError, Table, TableValue ParameterError, Table, TableValue
from calculate.variables.loader import Datavars from calculate.variables.loader import Datavars
TESTFILES_PATH = os.path.join(os.getcwd(), 'tests/variables/testfiles') TESTFILES_PATH = os.path.join(os.getcwd(), 'tests/parameters/testfiles')
GENTOO_PATH = os.path.join(TESTFILES_PATH, 'gentoo')
@pytest.mark.parameters @pytest.mark.parameters
class TestParameters: class TestParameters:
def test_to_make_testfiles(self): def test_to_make_testfiles(self):
shutil.copytree(os.path.join(TESTFILES_PATH, 'gentoo.backup'), shutil.copytree(os.path.join(TESTFILES_PATH, 'gentoo.backup'),
os.path.join(TESTFILES_PATH, 'gentoo'), os.path.join(TESTFILES_PATH, 'gentoo'), symlinks=True)
symlinks=True)
def test_if_some_parameters_are_created_using_classes_inheriting_the_BaseParameter_class_and_its_types_is_set_in_the_parameter_s_classes__the_parameters_can_be_created_using_this_classes_and_their_instances_types_are_types_from_their_classes(self): def test_if_some_parameters_are_created_using_classes_inheriting_the_BaseParameter_class_and_its_types_is_set_in_the_parameter_s_classes__the_parameters_can_be_created_using_this_classes_and_their_instances_types_are_types_from_their_classes(self):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH, datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17')) 'variables'))
PARAMS = Parameters().initialize(datavars) PARAMS = Parameters(datavars)
# Описание классов параметров. # Описание классов параметров.
class MyShinyParameter(BaseParameter): class MyShinyParameter(BaseParameter):
@ -75,6 +75,17 @@ class TestParameters:
OneMoreParameter('one-more', 'The Others', OneMoreParameter('one-more', 'The Others',
Description(full='Plan 9 from outer space.'))) Description(full='Plan 9 from outer space.')))
class AnotherParameter(BaseParameter):
type = String()
def bind_method(self):
return 'default string', None
def validate(self, value):
if not value.startswith('/var/lib'):
raise ValidationError("The value must starts with a"
" '/var/lib'")
shiny_one, another_one, weird_one = PARAMS shiny_one, another_one, weird_one = PARAMS
shiny_one.set(9) shiny_one.set(9)
@ -88,9 +99,9 @@ class TestParameters:
def test_if_parameter_is_created_with_bind_method_with_a_variable_in_its_arguments__the_default_parameter_value_is_calculated_using_this_method_and_a_variable_from_arguments_can_invalidate_the_parameter(self): def test_if_parameter_is_created_with_bind_method_with_a_variable_in_its_arguments__the_default_parameter_value_is_calculated_using_this_method_and_a_variable_from_arguments_can_invalidate_the_parameter(self):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH, datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17')) 'variables'))
PARAMS = Parameters().initialize(datavars) PARAMS = Parameters(datavars)
# Описание классов параметров. # Описание классов параметров.
class TestParameter(BaseParameter): class TestParameter(BaseParameter):
@ -120,9 +131,9 @@ class TestParameters:
def test_if_bind_method_is_set_for_parameter_and_then_the_set_method_is_used_to_change_value__the_parameters_value_is_changed_and_a_variable_from_a_bounded_variable_is_not_able_to_invalidate_parameter_s_value(self): def test_if_bind_method_is_set_for_parameter_and_then_the_set_method_is_used_to_change_value__the_parameters_value_is_changed_and_a_variable_from_a_bounded_variable_is_not_able_to_invalidate_parameter_s_value(self):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH, datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17')) 'variables'))
PARAMS = Parameters().initialize(datavars) PARAMS = Parameters(datavars)
# Описание классов параметров. # Описание классов параметров.
class TestParameter(BaseParameter): class TestParameter(BaseParameter):
@ -151,9 +162,9 @@ class TestParameters:
def test_if_the_bind_method_is_set_for_two_variables__the_bind_method_calculates_parameter_s_default_value__variables_can_invalidate_parameter_s_value_before_the_set_method_is_used__the_set_parameter_can_change_value_of_the_parameter(self): def test_if_the_bind_method_is_set_for_two_variables__the_bind_method_calculates_parameter_s_default_value__variables_can_invalidate_parameter_s_value_before_the_set_method_is_used__the_set_parameter_can_change_value_of_the_parameter(self):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH, datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17')) 'variables'))
PARAMS = Parameters().initialize(datavars) PARAMS = Parameters(datavars)
# Описание классов параметров. # Описание классов параметров.
class TestParameter(BaseParameter): class TestParameter(BaseParameter):
@ -193,9 +204,9 @@ class TestParameters:
def test_if_hash_value_is_in_the_set_variables_list__the_parameter_is_able_to_change_that_hash_s_value(self): def test_if_hash_value_is_in_the_set_variables_list__the_parameter_is_able_to_change_that_hash_s_value(self):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH, datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17')) 'variables'))
PARAMS = Parameters().initialize(datavars) PARAMS = Parameters(datavars)
# Описание классов параметров. # Описание классов параметров.
class TestParameter(BaseParameter): class TestParameter(BaseParameter):
@ -227,9 +238,9 @@ class TestParameters:
def test_if_bind_method_is_set_for_parameter__the_method_can_return_as_second_value_of_the_return_tuple_disactivity_comment_witch_disactivates_the_parameter_even_if_parameters_values_is_set_by_user(self): def test_if_bind_method_is_set_for_parameter__the_method_can_return_as_second_value_of_the_return_tuple_disactivity_comment_witch_disactivates_the_parameter_even_if_parameters_values_is_set_by_user(self):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH, datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17')) 'variables'))
PARAMS = Parameters().initialize(datavars) PARAMS = Parameters(datavars)
# Описание классов параметров. # Описание классов параметров.
class BoolTestParameter(BaseParameter): class BoolTestParameter(BaseParameter):
@ -282,8 +293,8 @@ class TestParameters:
def test_Choice_type_is_set_for_parameter_and_bind_method_is_set_too__the_bind_method_can_define_available_values_for_the_choice_parameter__choice_availables_is_invalidatable_even_if_the_value_is_set_by_user__choice_type_checks_if_the_new_value_is_the_availables_list(self): def test_Choice_type_is_set_for_parameter_and_bind_method_is_set_too__the_bind_method_can_define_available_values_for_the_choice_parameter__choice_availables_is_invalidatable_even_if_the_value_is_set_by_user__choice_type_checks_if_the_new_value_is_the_availables_list(self):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH, datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17')) 'variables'))
PARAMS = Parameters().initialize(datavars) PARAMS = Parameters(datavars)
# Описание классов параметров. # Описание классов параметров.
class TestParameter(BaseParameter): class TestParameter(BaseParameter):
@ -312,8 +323,8 @@ class TestParameters:
def test_if_some_parameters_are_created_with_some_types_and_there_are_attempts_to_assign_some_values_which_type_is_not_correct_to_this_parameters__the_parameters_types_checks_this_values_and_raises_ValidationError_exception(self): def test_if_some_parameters_are_created_with_some_types_and_there_are_attempts_to_assign_some_values_which_type_is_not_correct_to_this_parameters__the_parameters_types_checks_this_values_and_raises_ValidationError_exception(self):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH, datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17')) 'variables'))
PARAMS = Parameters().initialize(datavars) PARAMS = Parameters(datavars)
# Описание классов параметров. # Описание классов параметров.
class FirstTestParameter(BaseParameter): class FirstTestParameter(BaseParameter):
@ -365,8 +376,8 @@ class TestParameters:
def test_if_some_parameters_is_created_with_the_validate_methods__the_validate_methods_are_used_for_validating_all_values_that_is_set_to_the_parameter(self): def test_if_some_parameters_is_created_with_the_validate_methods__the_validate_methods_are_used_for_validating_all_values_that_is_set_to_the_parameter(self):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH, datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17')) 'variables'))
PARAMS = Parameters().initialize(datavars) PARAMS = Parameters(datavars)
# Описание классов параметров. # Описание классов параметров.
class FirstTestParameter(BaseParameter): class FirstTestParameter(BaseParameter):
@ -456,8 +467,8 @@ class TestParameters:
def test_if_some_parameters_are_created_with_validation_methods_that_use_parameters_forming_cyclic_validation_process__the_parameters_throw_the_CyclicValidationError_exception(self): def test_if_some_parameters_are_created_with_validation_methods_that_use_parameters_forming_cyclic_validation_process__the_parameters_throw_the_CyclicValidationError_exception(self):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH, datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17')) 'variables'))
PARAMS = Parameters().initialize(datavars) PARAMS = Parameters(datavars)
# Описание классов параметров. # Описание классов параметров.
class FirstTestParameter(BaseParameter): class FirstTestParameter(BaseParameter):
@ -540,8 +551,8 @@ class TestParameters:
def test_if_two_parameters_is_created_with_the_bind_methods_and_one_of_them_can_be_disactivated_depending_on_the_variable_and_other_one_can_change_this_variable_s_value__the_second_parameter_can_disactivate_the_first_one_through_variable(self): def test_if_two_parameters_is_created_with_the_bind_methods_and_one_of_them_can_be_disactivated_depending_on_the_variable_and_other_one_can_change_this_variable_s_value__the_second_parameter_can_disactivate_the_first_one_through_variable(self):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH, datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17')) 'variables'))
PARAMS = Parameters().initialize(datavars) PARAMS = Parameters(datavars)
# Описание классов параметров. # Описание классов параметров.
class FirstTestParameter(BaseParameter): class FirstTestParameter(BaseParameter):
@ -586,8 +597,8 @@ class TestParameters:
def test_if_there_is_an_attempt_to_add_in_the_parameters_container_new_parameter_but_the_container_already_has_parameter_with_the_same_fullname__the_parameters_container_throws_ParameterError_exception(self): def test_if_there_is_an_attempt_to_add_in_the_parameters_container_new_parameter_but_the_container_already_has_parameter_with_the_same_fullname__the_parameters_container_throws_ParameterError_exception(self):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH, datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17')) 'variables'))
PARAMS = Parameters().initialize(datavars) PARAMS = Parameters(datavars)
# Описание классов параметров. # Описание классов параметров.
class FirstTestParameter(BaseParameter): class FirstTestParameter(BaseParameter):
@ -617,8 +628,8 @@ class TestParameters:
def test_if_there_is_an_attempt_to_add_in_the_parameters_container_new_parameter_but_the_container_already_has_parameter_with_the_same_shortname__the_parameters_container_throws_ParameterError_exception(self): def test_if_there_is_an_attempt_to_add_in_the_parameters_container_new_parameter_but_the_container_already_has_parameter_with_the_same_shortname__the_parameters_container_throws_ParameterError_exception(self):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH, datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17')) 'variables'))
PARAMS = Parameters().initialize(datavars) PARAMS = Parameters(datavars)
# Описание классов параметров. # Описание классов параметров.
class FirstTestParameter(BaseParameter): class FirstTestParameter(BaseParameter):
@ -650,8 +661,8 @@ class TestParameters:
def test_if_some_parameters_are_created_with_different_argv_values__their_position_number_will_be_saved_in_the_parameters_container(self): def test_if_some_parameters_are_created_with_different_argv_values__their_position_number_will_be_saved_in_the_parameters_container(self):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH, datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17')) 'variables'))
PARAMS = Parameters().initialize(datavars) PARAMS = Parameters(datavars)
# Описание классов параметров. # Описание классов параметров.
class FirstTestParameter(BaseParameter): class FirstTestParameter(BaseParameter):
@ -695,8 +706,8 @@ class TestParameters:
def test_if_there_is_an_attempt_to_add_in_the_parameters_container_new_parameter_but_the_container_already_has_parameter_with_the_same_argv_value__the_parameters_container_throws_ParameterError_exception(self): def test_if_there_is_an_attempt_to_add_in_the_parameters_container_new_parameter_but_the_container_already_has_parameter_with_the_same_argv_value__the_parameters_container_throws_ParameterError_exception(self):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH, datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17')) 'variables'))
PARAMS = Parameters().initialize(datavars) PARAMS = Parameters(datavars)
# Описание классов параметров. # Описание классов параметров.
class FirstTestParameter(BaseParameter): class FirstTestParameter(BaseParameter):
@ -728,8 +739,8 @@ class TestParameters:
def test_if_parameter_is_created_with_the_bind_method__the_bind_method_can_get_access_to_the_current_parameter_value_during_the_value_calculation(self): def test_if_parameter_is_created_with_the_bind_method__the_bind_method_can_get_access_to_the_current_parameter_value_during_the_value_calculation(self):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH, datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17')) 'variables'))
PARAMS = Parameters().initialize(datavars) PARAMS = Parameters(datavars)
# Описание классов параметров. # Описание классов параметров.
class FirstTestParameter(BaseParameter): class FirstTestParameter(BaseParameter):
@ -773,8 +784,8 @@ class TestParameters:
def test_if_parameter_is_created_with_table_type_and_bind_method_is_set_for_this_parameter__the_bind_method_can_be_used_for_setting_of_the_table_fields_and_its_types(self): def test_if_parameter_is_created_with_table_type_and_bind_method_is_set_for_this_parameter__the_bind_method_can_be_used_for_setting_of_the_table_fields_and_its_types(self):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH, datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17')) 'variables'))
PARAMS = Parameters().initialize(datavars) PARAMS = Parameters(datavars)
# Описание классов параметров. # Описание классов параметров.
class FirstTestParameter(BaseParameter): class FirstTestParameter(BaseParameter):
@ -810,8 +821,8 @@ class TestParameters:
def test_if_parameter_is_created_with_table_type_and_types_of_its_fields_is_set__this_parameter_uses_this_types_for_invalidating_of_all_fields_values(self): def test_if_parameter_is_created_with_table_type_and_types_of_its_fields_is_set__this_parameter_uses_this_types_for_invalidating_of_all_fields_values(self):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH, datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17')) 'variables'))
PARAMS = Parameters().initialize(datavars) PARAMS = Parameters(datavars)
# Описание классов параметров. # Описание классов параметров.
class FirstTestParameter(BaseParameter): class FirstTestParameter(BaseParameter):
@ -870,8 +881,8 @@ class TestParameters:
def test_if_parameter_is_created_with_table_type_and_expandable_flag_is_True__new_rows_can_be_added_to_the_created_table_and_existing_rows_can_be_modified(self): def test_if_parameter_is_created_with_table_type_and_expandable_flag_is_True__new_rows_can_be_added_to_the_created_table_and_existing_rows_can_be_modified(self):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH, datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17')) 'variables'))
PARAMS = Parameters().initialize(datavars) PARAMS = Parameters(datavars)
# Описание классов параметров. # Описание классов параметров.
class FirstTestParameter(BaseParameter): class FirstTestParameter(BaseParameter):
@ -914,8 +925,8 @@ class TestParameters:
def test_if_parameter_is_created_with_table_type_and_expandable_flag_is_False__existing_rows_can_be_modified_but_attempt_to_add_new_will_call_set_error_method_that_can_be_set_for_table(self): def test_if_parameter_is_created_with_table_type_and_expandable_flag_is_False__existing_rows_can_be_modified_but_attempt_to_add_new_will_call_set_error_method_that_can_be_set_for_table(self):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH, datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17')) 'variables'))
PARAMS = Parameters().initialize(datavars) PARAMS = Parameters(datavars)
# Описание классов параметров. # Описание классов параметров.
class FirstTestParameter(BaseParameter): class FirstTestParameter(BaseParameter):
@ -967,8 +978,8 @@ class TestParameters:
def test_if_parameter_is_created_with_table_type_and_to_set_method_is_used_to_add_variables_to_set_value__the_table_parameter_will_set_its_value_to_the_variable_from_set_list(self): def test_if_parameter_is_created_with_table_type_and_to_set_method_is_used_to_add_variables_to_set_value__the_table_parameter_will_set_its_value_to_the_variable_from_set_list(self):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH, datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17')) 'variables'))
PARAMS = Parameters().initialize(datavars) PARAMS = Parameters(datavars)
# Описание классов параметров. # Описание классов параметров.
class FirstTestParameter(BaseParameter): class FirstTestParameter(BaseParameter):
@ -1010,8 +1021,8 @@ class TestParameters:
def test_if_parameter_is_created_with_table_type_and_fill_method_is_set__the_parameter_will_use_fill_method_to_fill_empty_fields_of_the_table(self): def test_if_parameter_is_created_with_table_type_and_fill_method_is_set__the_parameter_will_use_fill_method_to_fill_empty_fields_of_the_table(self):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH, datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17')) 'variables'))
PARAMS = Parameters().initialize(datavars) PARAMS = Parameters(datavars)
# Описание классов параметров. # Описание классов параметров.
class FirstTestParameter(BaseParameter): class FirstTestParameter(BaseParameter):

@ -0,0 +1,21 @@
[os][linux]
ver = 20
shortname = CLD
fullname = Calculate Linux Desktop
subname = KDE
[os][hashvar]
value1 = new1
value2 = new2
[os][tablevar][0]
dev = /dev/sda1
mount = swap
[os][tablevar][1]
dev = /dev/sda2
mount = /
[os][tablevar][2]
dev = /dev/sda5
mount = /var/calculate

@ -0,0 +1,4 @@
from calculate.variables.datavars import Variable, StringType
Variable('chroot', type=StringType.readonly, source='/')

@ -0,0 +1,66 @@
from calculate.variables.datavars import Namespace, Variable, Dependence,\
StringType, HashType, TableType,\
BooleanType, ListType, IntegerType
with Namespace('linux'):
Variable('shortname', source='', type=StringType)
Variable('ver', source='', type=StringType)
Variable('fullname', source='', type=StringType)
Variable('subname', source='', type=StringType)
Variable('arch', source='', type=StringType)
Variable('test_1', source=['choice_1',
'choice_2',
'choice_3',
'choice_4'], type=ListType)
Variable('test_2', source=False, type=BooleanType)
Variable('test_3', source='test string', type=StringType)
Variable('test_4', source='/dev/sda', type=StringType)
Variable('test_5', source=8, type=IntegerType)
Variable('test_6', source="Comment", type=StringType)
def get_title(subname, fullname, ver):
if subname.value:
return '{} {} {}'.format(fullname.value, subname.value, ver.value)
else:
return '{} {}'.format(fullname.value, ver.value)
Variable('title', type=StringType,
source=Dependence('.subname', '.fullname', '.ver',
depend=get_title))
Variable('hashvar', source={'value1': 'test1',
'value2': 'test2'}, type=HashType)
Variable('hashvar_0', source={'value1': 'test1',
'value2': 'test2'}, type=HashType)
Variable('hashvar_1', source={'key1': 'value1',
'key2': 'value2'}, type=HashType)
Variable('hashvar_2', source={'id_1': 1349,
'id_2': 1575}, type=HashType)
Variable('calculate', type=StringType,
source=Dependence('.hashvar_0',
depend=lambda hashvar: "{} {}".format(
hashvar.value['value1'],
hashvar.value['value2'])))
Variable('tablevar', type=TableType, source=[{"dev": "/dev/sdb1",
"mount": "/"},
{"dev": "/dev/sdb2",
"mount": "/var/calculate"}])
Variable('dev_table', type=TableType, source=[{"dev": "/dev/sdb1",
"mount": "/"},
{"dev": "/dev/sdb2",
"mount": "/var/calculate"}])

@ -0,0 +1,57 @@
import os
from calculate.variables.datavars import Variable, Namespace, Dependence,\
StringType, TableType
'''
gentoo:
make_profile -> string
profile:
path -> string
name -> string
repositories[*]{name, path} -> table
config -> undefined
'''
TESTFILES_PATH = os.path.join(os.getcwd(), 'tests/parameters/testfiles')
# Путь до файла, указывающего на активный профиль
Variable('make_profile', type=StringType, source='/etc/portage/make.profile')
# Параметры текущего профиля.
with Namespace('profile'):
# Абсолютный путь до профиля
Variable('path', type=StringType,
source=os.path.join(TESTFILES_PATH,
"gentoo/repos/distros/profiles/CLD/amd64"))
def get_profile_name(path, repositories):
profile_path = path.value
if not profile_path:
return ""
for repository in repositories.value:
repository_path = repository['path']
repository_name = repository['name']
remove_part = os.path.normpath(os.path.join(repository_path,
"profiles"))
if profile_path.startswith(remove_part):
return "{}:{}".format(repository_name,
profile_path[len(remove_part) + 1:])
return profile_path
# Название профиля
Variable('name', type=StringType,
source=Dependence('.path', '..repositories',
depend=get_profile_name))
# Информация о репозиториях
# name: имя репозитория
# path: полный путь до репозитория
Variable('repositories', type=TableType,
source=[{'name': 'distros',
'path': os.path.join(TESTFILES_PATH,
"gentoo/repos/distros")},
{'name': 'calculate',
'path': os.path.join(TESTFILES_PATH,
"gentoo/repos/calculate")},
{'name': 'gentoo',
'path': os.path.join(TESTFILES_PATH,
"gentoo/portage")}])

@ -0,0 +1,19 @@
import os
from calculate.variables.datavars import Variable, ListType, HashType
'''
system:
env_order -> list
env_path -> hash
'''
TESTFILES_PATH = os.path.join(os.getcwd(), 'tests/parameters/testfiles')
# Список мест, где есть calculate.ini файлы.
Variable('env_order', type=ListType, source=['system'])
# Отображение множества мест, где есть calculate.ini файлы, на пути к ним.
Variable('env_path', type=HashType,
source={'system': os.path.join(TESTFILES_PATH,
'parameters_ini/calculate.ini')})

@ -1,22 +1,36 @@
import os
import shutil
import pytest import pytest
from calculate.scripts.scripts import Var, Task, Static, TaskError, Done,\ from calculate.scripts.scripts import Var, Task, Static, TaskError, Done,\
DoneAny, Success, SuccessAny, Failed,\ DoneAny, Success, SuccessAny, Failed,\
FailedAny, Block, For, While, Until,\ FailedAny, Block, For, While, Until,\
Handler, Script, Run, ScriptError,\ Handler, Script, Run, ScriptError,\
ActionError ActionError, ScriptLauncher, RunTemplate
from calculate.variables.datavars import Namespace, Variable, IntegerType,\ from calculate.variables.datavars import Namespace, Variable, IntegerType,\
StringType, BooleanType, ListType,\ StringType, BooleanType, ListType,\
HashType HashType
from calculate.variables.loader import Datavars
from calculate.utils.io_module import IOModule from calculate.utils.io_module import IOModule
@pytest.mark.tasks TESTFILES_PATH = os.path.join(os.getcwd(), 'tests/scripts/testfiles')
@pytest.mark.scripts
class TestTasks(): class TestTasks():
def test_to_make_testfiles(self):
shutil.copytree(os.path.join(TESTFILES_PATH, 'var.backup'),
os.path.join(TESTFILES_PATH, 'var'),
symlinks=True)
shutil.copytree(os.path.join(TESTFILES_PATH, 'etc.backup'),
os.path.join(TESTFILES_PATH, 'etc'),
symlinks=True)
def test_if_arguments_of_the_Var_objects_are_correct_and_they_are_called_with_datavars_namespace_and_script_namespace_arguments__this_objects_can_be_used_for_different_checks(self): def test_if_arguments_of_the_Var_objects_are_correct_and_they_are_called_with_datavars_namespace_and_script_namespace_arguments__this_objects_can_be_used_for_different_checks(self):
Namespace.reset() Namespace.reset()
datavars = Namespace.datavars datavars = Namespace.datavars
script_namespace = Script.make_script_variables('test_script', [], script_namespace = ScriptLauncher.make_script_variables('test_script',
datavars) [], datavars)
with Namespace('os'): with Namespace('os'):
Variable('var_1', source='value', type=StringType) Variable('var_1', source='value', type=StringType)
@ -103,13 +117,15 @@ class TestTasks():
def action(output, arg1): def action(output, arg1):
return f'os.var = {arg1}' return f'os.var = {arg1}'
Script('test_script' script = Script('test_script'
).tasks( ).tasks(
Task(id='task', Task(id='task',
name="Task", name="Task",
action=action, action=action,
args=["os.var"]) args=["os.var"])
).initialize(IOModule(), datavars, None)() )
launcher = script.make_launcher(IOModule(), datavars, None)
launcher()
assert 'test_script' in datavars.scripts assert 'test_script' in datavars.scripts
assert 'task' in datavars.scripts.test_script assert 'task' in datavars.scripts.test_script
@ -139,7 +155,7 @@ class TestTasks():
action=action, action=action,
args=[Static('static_value'), "os.var_1"], args=[Static('static_value'), "os.var_1"],
when=(Var('os.var_2') & Var('os.var_3'))) when=(Var('os.var_2') & Var('os.var_3')))
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts assert 'test_script' in datavars.scripts
assert 'task' in datavars.scripts.test_script assert 'task' in datavars.scripts.test_script
@ -166,7 +182,7 @@ class TestTasks():
action=action, action=action,
args=[Static('value')], args=[Static('value')],
when=~Var('os.linux.var_1')) when=~Var('os.linux.var_1'))
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts assert 'test_script' in datavars.scripts
assert 'task' not in datavars.scripts.test_script assert 'task' not in datavars.scripts.test_script
@ -191,7 +207,7 @@ class TestTasks():
args=[Static('value')], args=[Static('value')],
essential=False, essential=False,
when=Var('os.linux.var_1')) when=Var('os.linux.var_1'))
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts assert 'test_script' in datavars.scripts
assert 'task' in datavars.scripts.test_script assert 'task' in datavars.scripts.test_script
@ -222,7 +238,7 @@ class TestTasks():
args=[Static('value')], args=[Static('value')],
essential=True, essential=True,
when=Var('os.linux.var_1')) when=Var('os.linux.var_1'))
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
def test_if_script_object_is_created_with_some_essential_tasks_with_conditions_and_some_conditions_uses_done_to_check_if_tasks_from_Done_or_DoneAny_arguments_were_completed__the_script_executes_tasks_which_done_condition_is_fulfilled(self): def test_if_script_object_is_created_with_some_essential_tasks_with_conditions_and_some_conditions_uses_done_to_check_if_tasks_from_Done_or_DoneAny_arguments_were_completed__the_script_executes_tasks_which_done_condition_is_fulfilled(self):
Namespace.reset() Namespace.reset()
@ -267,7 +283,7 @@ class TestTasks():
action=action, action=action,
args=[Static('value_4')], args=[Static('value_4')],
when=DoneAny('task_1', 'task_3')) when=DoneAny('task_1', 'task_3'))
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts assert 'test_script' in datavars.scripts
assert 'task_3' not in datavars.scripts.test_script assert 'task_3' not in datavars.scripts.test_script
@ -345,7 +361,7 @@ class TestTasks():
args=[Static('value_10')], args=[Static('value_10')],
when=(Var('os.linux.var_1') & when=(Var('os.linux.var_1') &
SuccessAny('task_3', 'task_4'))) SuccessAny('task_3', 'task_4')))
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts assert 'test_script' in datavars.scripts
assert 'task_4' not in datavars.scripts.test_script assert 'task_4' not in datavars.scripts.test_script
@ -425,7 +441,7 @@ class TestTasks():
args=[Static('value_9')], args=[Static('value_9')],
when=(Var('os.linux.var_1') & when=(Var('os.linux.var_1') &
FailedAny('task_2', 'task_3'))) FailedAny('task_2', 'task_3')))
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts assert 'test_script' in datavars.scripts
assert not datavars.scripts.test_script.task_2.success assert not datavars.scripts.test_script.task_2.success
@ -457,7 +473,7 @@ class TestTasks():
name="Task 1", name="Task 1",
action=action, action=action,
args=['os.linux.var']) args=['os.linux.var'])
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
def test_if_script_object_is_created_with_essential_task_having_set_parameter_containing_a_string_with_path_to_an_existing_variable__the_task_s_execution_result_will_be_set_to_the_variable_from_set_parameter(self): def test_if_script_object_is_created_with_essential_task_having_set_parameter_containing_a_string_with_path_to_an_existing_variable__the_task_s_execution_result_will_be_set_to_the_variable_from_set_parameter(self):
Namespace.reset() Namespace.reset()
@ -480,7 +496,7 @@ class TestTasks():
action=action, action=action,
args=['os.linux.var_1'], args=['os.linux.var_1'],
set='os.linux.var_2') set='os.linux.var_2')
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts assert 'test_script' in datavars.scripts
assert 'task' in datavars.scripts.test_script assert 'task' in datavars.scripts.test_script
@ -510,7 +526,7 @@ class TestTasks():
action=action, action=action,
args=['os.linux.var_1'], args=['os.linux.var_1'],
set=['os.var_1', 'os.linux.var_2']) set=['os.var_1', 'os.linux.var_2'])
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts assert 'test_script' in datavars.scripts
assert 'task' in datavars.scripts.test_script assert 'task' in datavars.scripts.test_script
@ -541,7 +557,7 @@ class TestTasks():
action=action, action=action,
args=['os.linux.var_1'], args=['os.linux.var_1'],
set=('os.var_1', 'os.linux.var_2')) set=('os.var_1', 'os.linux.var_2'))
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts assert 'test_script' in datavars.scripts
assert 'task' in datavars.scripts.test_script assert 'task' in datavars.scripts.test_script
@ -571,7 +587,7 @@ class TestTasks():
action=action, action=action,
args=['os.linux.var_1', 'os.linux.var_2'], args=['os.linux.var_1', 'os.linux.var_2'],
set={'field_1': 'os.var_1', 'field_2': 'os.var_2'}) set={'field_1': 'os.var_1', 'field_2': 'os.var_2'})
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts assert 'test_script' in datavars.scripts
assert 'task' in datavars.scripts.test_script assert 'task' in datavars.scripts.test_script
@ -596,7 +612,7 @@ class TestTasks():
name="Task", name="Task",
action=action, action=action,
args=['os.linux.var_1']) args=['os.linux.var_1'])
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
def test_if_script_object_is_created_with_essential_task_having_action_function_with_a_return_type_annotation_and_set_parameter_containing_variable_of_the_different_type__the_task_fails_to_set_returned_value_to_this_variable(self): def test_if_script_object_is_created_with_essential_task_having_action_function_with_a_return_type_annotation_and_set_parameter_containing_variable_of_the_different_type__the_task_fails_to_set_returned_value_to_this_variable(self):
Namespace.reset() Namespace.reset()
@ -618,7 +634,7 @@ class TestTasks():
action=action, action=action,
args=['os.linux.var_1'], args=['os.linux.var_1'],
set='os.linux.var_2') set='os.linux.var_2')
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
def test_if_script_object_is_created_with_plain_block_of_correct_tasks__the_script_will_be_executed_successfully(self): def test_if_script_object_is_created_with_plain_block_of_correct_tasks__the_script_will_be_executed_successfully(self):
Namespace.reset() Namespace.reset()
@ -641,7 +657,7 @@ class TestTasks():
name="Task 2", name="Task 2",
action=action, action=action,
args=[Static('value_2')])) args=[Static('value_2')]))
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts assert 'test_script' in datavars.scripts
assert 'task_1' in datavars.scripts.test_script assert 'task_1' in datavars.scripts.test_script
@ -671,7 +687,7 @@ class TestTasks():
action=action, action=action,
args=[Static('value_2')]), args=[Static('value_2')]),
when=Var('os.linux.var_1')) when=Var('os.linux.var_1'))
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts assert 'test_script' in datavars.scripts
assert 'task_1' in datavars.scripts.test_script assert 'task_1' in datavars.scripts.test_script
@ -699,7 +715,7 @@ class TestTasks():
action=action, action=action,
args=[Static('value_2')]), args=[Static('value_2')]),
when=~Var('os.linux.var_1')) when=~Var('os.linux.var_1'))
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'task_3' not in datavars.scripts.test_script assert 'task_3' not in datavars.scripts.test_script
assert 'task_4' not in datavars.scripts.test_script assert 'task_4' not in datavars.scripts.test_script
@ -735,7 +751,7 @@ class TestTasks():
name="Task 3", name="Task 3",
action=action, action=action,
args=[Static('rescue_value')])) args=[Static('rescue_value')]))
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts assert 'test_script' in datavars.scripts
assert 'task_1' in datavars.scripts.test_script assert 'task_1' in datavars.scripts.test_script
@ -777,7 +793,7 @@ class TestTasks():
action=action_2, action=action_2,
args=['scripts.test_script.value']), args=['scripts.test_script.value']),
loop=For('value', ['value_1', 'value_2', 'value_3'])) loop=For('value', ['value_1', 'value_2', 'value_3']))
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
for l_value, r_value in zip(output, expected): for l_value, r_value in zip(output, expected):
assert l_value == r_value assert l_value == r_value
@ -818,7 +834,7 @@ class TestTasks():
action=action_2, action=action_2,
args=['scripts.test_script.value']), args=['scripts.test_script.value']),
loop=For('value', 'os.linux.var_1')) loop=For('value', 'os.linux.var_1'))
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
for l_value, r_value in zip(output, expected): for l_value, r_value in zip(output, expected):
assert l_value == r_value assert l_value == r_value
@ -851,7 +867,7 @@ class TestTasks():
set={'counter': 'os.counter', set={'counter': 'os.counter',
'output': 'os.output'}), 'output': 'os.output'}),
loop=While(Var('os.counter') < Var('os.ceiling'))) loop=While(Var('os.counter') < Var('os.ceiling')))
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert datavars.os.output == expected assert datavars.os.output == expected
@ -893,7 +909,7 @@ class TestTasks():
args=['os.counter', 'os.ceiling'], args=['os.counter', 'os.ceiling'],
set='os.flag'), set='os.flag'),
loop=While(~Var('os.flag'))) loop=While(~Var('os.flag')))
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert datavars.os.output == expected assert datavars.os.output == expected
@ -920,7 +936,7 @@ class TestTasks():
set={'counter': 'os.counter', set={'counter': 'os.counter',
'output': 'os.output'}), 'output': 'os.output'}),
loop=Until(Var('os.flag'))) loop=Until(Var('os.flag')))
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert datavars.os.output == ['counter = 0'] assert datavars.os.output == ['counter = 0']
@ -962,7 +978,7 @@ class TestTasks():
args=['os.counter', 'os.ceiling'], args=['os.counter', 'os.ceiling'],
set='os.flag'), set='os.flag'),
loop=Until(~Var('os.flag'))) loop=Until(~Var('os.flag')))
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert datavars.os.output == expected assert datavars.os.output == expected
@ -994,7 +1010,7 @@ class TestTasks():
action=action, action=action,
args=[Static('value_2')]) args=[Static('value_2')])
) )
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'task_1' in datavars.scripts.test_script assert 'task_1' in datavars.scripts.test_script
assert 'task_2' in datavars.scripts.test_script assert 'task_2' in datavars.scripts.test_script
@ -1047,7 +1063,7 @@ class TestTasks():
action=action, action=action,
args=[Static('value_8')]) args=[Static('value_8')])
) )
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts assert 'test_script' in datavars.scripts
assert 'task_1' in datavars.scripts.test_script assert 'task_1' in datavars.scripts.test_script
@ -1112,7 +1128,7 @@ class TestTasks():
action=action, action=action,
args=[Static('value_8')]) args=[Static('value_8')])
) )
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts assert 'test_script' in datavars.scripts
assert 'task_1' in datavars.scripts.test_script assert 'task_1' in datavars.scripts.test_script
@ -1177,7 +1193,7 @@ class TestTasks():
action=action, action=action,
args=[Static('value_8')]) args=[Static('value_8')])
) )
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts assert 'test_script' in datavars.scripts
assert 'task_1' in datavars.scripts.test_script assert 'task_1' in datavars.scripts.test_script
@ -1242,7 +1258,7 @@ class TestTasks():
action=action, action=action,
args=[Static('value_8')]) args=[Static('value_8')])
) )
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts assert 'test_script' in datavars.scripts
assert 'task_1' in datavars.scripts.test_script assert 'task_1' in datavars.scripts.test_script
@ -1311,7 +1327,7 @@ class TestTasks():
action=action, action=action,
args=[Static('value_8')]) args=[Static('value_8')])
) )
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts assert 'test_script' in datavars.scripts
assert 'task_1' in datavars.scripts.test_script assert 'task_1' in datavars.scripts.test_script
@ -1340,6 +1356,16 @@ class TestTasks():
Script('test_script', Script('test_script',
args=['arg1', 'arg_2'], args=['arg1', 'arg_2'],
).tasks( ).tasks(
Handler('handler_1',
Task(id='task_7',
name="Task 7",
action=action,
args=[Static('value_7')]),
Task(id='task_8',
name="Task 8",
action=action,
args=[Static('value_8')])
),
Task(id='task_1', Task(id='task_1',
name="Task 1", name="Task 1",
action=action, action=action,
@ -1366,18 +1392,8 @@ class TestTasks():
Task(id='task_6', Task(id='task_6',
name="Task 6", name="Task 6",
action=action, action=action,
args=[Static('value_6')]), args=[Static('value_6')])
Handler('handler_1', ).make_launcher(IOModule(), datavars, None)(True, False)
Task(id='task_7',
name="Task 7",
action=action,
args=[Static('value_7')]),
Task(id='task_8',
name="Task 8",
action=action,
args=[Static('value_8')])
)
).initialize(IOModule(), datavars, None)(True, False)
assert 'test_script' in datavars.scripts assert 'test_script' in datavars.scripts
assert 'task_1' in datavars.scripts.test_script assert 'task_1' in datavars.scripts.test_script
@ -1411,7 +1427,7 @@ class TestTasks():
name="Task", name="Task",
action=action, action=action,
args=[Static('value_2')]), args=[Static('value_2')]),
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'test_script_1' in datavars.scripts assert 'test_script_1' in datavars.scripts
assert 'test_script_2' in datavars.scripts assert 'test_script_2' in datavars.scripts
@ -1448,7 +1464,7 @@ class TestTasks():
name="Task", name="Task",
action=action, action=action,
args=[Static('value_2')]), args=[Static('value_2')]),
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'test_script_1' in datavars.scripts assert 'test_script_1' in datavars.scripts
assert 'test_script_2' in datavars.scripts assert 'test_script_2' in datavars.scripts
@ -1485,7 +1501,7 @@ class TestTasks():
name="Task", name="Task",
action=action, action=action,
args=[Static('value_2')]), args=[Static('value_2')]),
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'test_script_1' in datavars.scripts assert 'test_script_1' in datavars.scripts
assert 'test_script_2' in datavars.scripts assert 'test_script_2' in datavars.scripts
@ -1527,7 +1543,7 @@ class TestTasks():
name="Task 3", name="Task 3",
action=action, action=action,
args=[Static('value_3')]), args=[Static('value_3')]),
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'test_script_1' in datavars.scripts assert 'test_script_1' in datavars.scripts
assert 'test_script_2' in datavars.scripts assert 'test_script_2' in datavars.scripts
@ -1573,7 +1589,7 @@ class TestTasks():
name="Task 3", name="Task 3",
action=action, action=action,
args=[Static('value_3')]), args=[Static('value_3')]),
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
def test_if_script_object_is_created_with_any_number_of_tasks_and_Run_object_is_used_to_run_other_script_with_excess_number_of_variables_as_its_arguments__the_outer_script_interrupts_its_execution_with_error(self): def test_if_script_object_is_created_with_any_number_of_tasks_and_Run_object_is_used_to_run_other_script_with_excess_number_of_variables_as_its_arguments__the_outer_script_interrupts_its_execution_with_error(self):
Namespace.reset() Namespace.reset()
@ -1607,7 +1623,7 @@ class TestTasks():
name="Task 3", name="Task 3",
action=action, action=action,
args=[Static('value_3')]), args=[Static('value_3')]),
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
def test_if_script_object_is_created_with_any_number_of_tasks_and_two_Run_objects_that_are_used_to_run_the_same_scripts_with_different_arguments__the_outer_script_runs_script_from_Run_objects_two_times_with_different_arguments(self): def test_if_script_object_is_created_with_any_number_of_tasks_and_two_Run_objects_that_are_used_to_run_the_same_scripts_with_different_arguments__the_outer_script_runs_script_from_Run_objects_two_times_with_different_arguments(self):
Namespace.reset() Namespace.reset()
@ -1637,7 +1653,7 @@ class TestTasks():
args=[Static('value_2')]), args=[Static('value_2')]),
Run(script_1, namespace='os', Run(script_1, namespace='os',
args=[Static(13)]) args=[Static(13)])
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'test_script_1' in datavars.scripts assert 'test_script_1' in datavars.scripts
assert 'test_script_2' in datavars.scripts assert 'test_script_2' in datavars.scripts
@ -1681,7 +1697,7 @@ class TestTasks():
name="Task 2", name="Task 2",
action=action, action=action,
args=[Static('value_2')]) args=[Static('value_2')])
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
def test_if_script_object_is_created_with_any_number_of_tasks_and_not_essential_Run_object_is_used_to_run_other_failing_script__the_outer_script_skips_failing_script_and_continues_to_execute_its_own_tasks(self): def test_if_script_object_is_created_with_any_number_of_tasks_and_not_essential_Run_object_is_used_to_run_other_failing_script__the_outer_script_skips_failing_script_and_continues_to_execute_its_own_tasks(self):
Namespace.reset() Namespace.reset()
@ -1719,7 +1735,7 @@ class TestTasks():
name="Task 3", name="Task 3",
action=action, action=action,
args=[Static('value_3')]) args=[Static('value_3')])
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'task_1' in datavars.scripts.test_script_1 assert 'task_1' in datavars.scripts.test_script_1
assert not datavars.scripts.test_script_1.task_1.success assert not datavars.scripts.test_script_1.task_1.success
@ -1748,11 +1764,10 @@ class TestTasks():
name="Task 1", name="Task 1",
action=action, action=action,
args=['os.var_1']) args=['os.var_1'])
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts assert 'test_script' in datavars.scripts
assert 'task_1' in datavars.scripts.test_script assert 'task_1' in datavars.scripts.test_script
assert 'task_1' in datavars.scripts.test_script
def test_if_script_object_is_created_with_not_essential_task_which_action_raises_ActionError_exception_or_an_exception_inheriting_it__the_outer_script_skips_failing_task(self): def test_if_script_object_is_created_with_not_essential_task_which_action_raises_ActionError_exception_or_an_exception_inheriting_it__the_outer_script_skips_failing_task(self):
Namespace.reset() Namespace.reset()
@ -1776,7 +1791,7 @@ class TestTasks():
essential=False, essential=False,
action=action, action=action,
args=['os.var_1']) args=['os.var_1'])
).initialize(IOModule(), datavars, None)() ).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts assert 'test_script' in datavars.scripts
assert 'task_1' in datavars.scripts.test_script assert 'task_1' in datavars.scripts.test_script
@ -1797,18 +1812,19 @@ class TestTasks():
def action(arg1): def action(arg1):
return f'value = {arg1}' return f'value = {arg1}'
tasks_1 = [Task(id='task_1',
name="Task 1",
action=action,
args=['.var_1'],
when=Var('scripts.test_script_1.arg')),
Task(id='task_2',
name="Task 2",
action=action,
args=['.var_1'],
when=~Var('scripts.test_script_1.arg'))]
script_1 = Script('test_script_1', script_1 = Script('test_script_1',
args=['arg'] args=['arg']
).tasks(Task(id='task_1', ).tasks(*tasks_1)
name="Task 1",
action=action,
args=['.var_1'],
when=Var('scripts.test_script_1.arg')),
Task(id='task_2',
name="Task 2",
action=action,
args=['.var_1'],
when=~Var('scripts.test_script_1.arg')))
Script('test_script_2', Script('test_script_2',
).tasks(Task(id='task_1', ).tasks(Task(id='task_1',
@ -1831,7 +1847,7 @@ class TestTasks():
when=Var('.linux.var_4')), when=Var('.linux.var_4')),
Run(script_1, namespace='os', args=[False], Run(script_1, namespace='os', args=[False],
when=Var('.linux.var_4')) when=Var('.linux.var_4'))
).initialize(IOModule(), datavars, datavars.os)() ).make_launcher(IOModule(), datavars, datavars.os)()
assert 'test_script_1' in datavars.scripts assert 'test_script_1' in datavars.scripts
assert 'test_script_2' in datavars.scripts assert 'test_script_2' in datavars.scripts
@ -1852,3 +1868,53 @@ class TestTasks():
assert 'task_2' in datavars.scripts.test_script_1 assert 'task_2' in datavars.scripts.test_script_1
assert datavars.scripts.test_script_1.task_2.result == 'value = 1349' assert datavars.scripts.test_script_1.task_2.result == 'value = 1349'
def test_run_templates_using_script(self):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables'))
Script('test_script',
).tasks(RunTemplate(id="templates_1",
action='action_1',
package="test-category/test-package",
chroot_path=TESTFILES_PATH,
root_path="/etc"),
).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts
assert 'templates_1' in datavars.scripts.test_script
assert datavars.scripts.test_script.templates_1.get_hash() ==\
{"changed": {os.path.join(TESTFILES_PATH,
'etc/dir_0'): 'N',
os.path.join(TESTFILES_PATH,
'etc/dir_0/file_0'): 'N'},
"skipped": []}
assert os.path.exists(os.path.join(TESTFILES_PATH, 'etc/dir_0/file_0'))
def test_run_templates_using_script_and_namespace_is_set(self):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables'))
Script('test_script',
).tasks(RunTemplate(id="templates_1",
action='action_2',
package="test-category/test-package",
chroot_path=TESTFILES_PATH,
root_path="/etc"),
).make_launcher(IOModule(), datavars, datavars.os)()
assert 'test_script' in datavars.scripts
assert 'templates_1' in datavars.scripts.test_script
assert datavars.scripts.test_script.templates_1.get_hash() ==\
{"changed": {os.path.join(TESTFILES_PATH,
'etc/dir_1'): 'N',
os.path.join(TESTFILES_PATH,
'etc/dir_1/file_0'): 'N'},
"skipped": []}
assert os.path.exists(os.path.join(TESTFILES_PATH, 'etc/dir_1/file_0'))
def test_for_removing_testfiles(self):
shutil.rmtree(os.path.join(TESTFILES_PATH, 'var'))
shutil.rmtree(os.path.join(TESTFILES_PATH, 'etc'))

@ -0,0 +1,2 @@
{% calculate action = "action_1", append = "skip",
package = "test-category/test-package" %}

@ -0,0 +1,4 @@
{% calculate append = "join", format = "kde" %}
[section][first]
parameter_1 = value_1
parameter_2 = no

@ -0,0 +1,2 @@
{% calculate action = "action_2", append = "skip",
package = "test-category/test-package" %}

@ -0,0 +1,4 @@
{% calculate append = "join", format = "kde" %}
[section][first]
parameter_1 = value
parameter_2 = no

@ -0,0 +1,21 @@
[os][linux]
ver = 20
shortname = CLD
fullname = Calculate Linux Desktop
subname = KDE
[os][hashvar]
value1 = new1
value2 = new2
[os][tablevar][0]
dev = /dev/sda1
mount = swap
[os][tablevar][1]
dev = /dev/sda2
mount = /
[os][tablevar][2]
dev = /dev/sda5
mount = /var/calculate

@ -0,0 +1,12 @@
import os
from calculate.variables.datavars import Variable, StringType
TESTFILES_PATH = os.path.join(os.getcwd(), 'tests/scripts/testfiles')
Variable('cl_chroot_path', type=StringType.readonly, source='/')
Variable('cl_root_path', type=StringType.readonly, source='/')
Variable('cl_template_path', type=StringType.readonly,
source=os.path.join(TESTFILES_PATH, 'templates'))

@ -0,0 +1,66 @@
from calculate.variables.datavars import Namespace, Variable, Dependence,\
StringType, HashType, TableType,\
BooleanType, ListType, IntegerType
with Namespace('linux'):
Variable('shortname', source='', type=StringType)
Variable('ver', source='', type=StringType)
Variable('fullname', source='', type=StringType)
Variable('subname', source='', type=StringType)
Variable('arch', source='', type=StringType)
Variable('test_1', source=['choice_1',
'choice_2',
'choice_3',
'choice_4'], type=ListType)
Variable('test_2', source=False, type=BooleanType)
Variable('test_3', source='test string', type=StringType)
Variable('test_4', source='/dev/sda', type=StringType)
Variable('test_5', source=8, type=IntegerType)
Variable('test_6', source="Comment", type=StringType)
def get_title(subname, fullname, ver):
if subname.value:
return '{} {} {}'.format(fullname.value, subname.value, ver.value)
else:
return '{} {}'.format(fullname.value, ver.value)
Variable('title', type=StringType,
source=Dependence('.subname', '.fullname', '.ver',
depend=get_title))
Variable('hashvar', source={'value1': 'test1',
'value2': 'test2'}, type=HashType)
Variable('hashvar_0', source={'value1': 'test1',
'value2': 'test2'}, type=HashType)
Variable('hashvar_1', source={'key1': 'value1',
'key2': 'value2'}, type=HashType)
Variable('hashvar_2', source={'id_1': 1349,
'id_2': 1575}, type=HashType)
Variable('calculate', type=StringType,
source=Dependence('.hashvar_0',
depend=lambda hashvar: "{} {}".format(
hashvar.value['value1'],
hashvar.value['value2'])))
Variable('tablevar', type=TableType, source=[{"dev": "/dev/sdb1",
"mount": "/"},
{"dev": "/dev/sdb2",
"mount": "/var/calculate"}])
Variable('dev_table', type=TableType, source=[{"dev": "/dev/sdb1",
"mount": "/"},
{"dev": "/dev/sdb2",
"mount": "/var/calculate"}])

@ -0,0 +1,59 @@
import os
from calculate.variables.datavars import Variable, Namespace, Dependence,\
StringType, TableType
'''
gentoo:
make_profile -> string
profile:
path -> string
name -> string
repositories[*]{name, path} -> table
config -> undefined
'''
TESTFILES_PATH = os.path.join(os.getcwd(), 'tests/scripts/testfiles')
# Путь до файла, указывающего на активный профиль
Variable('make_profile', type=StringType, source='/etc/portage/make.profile')
# Параметры текущего профиля.
with Namespace('profile'):
# Абсолютный путь до профиля
Variable('path', type=StringType,
source=os.path.join(
TESTFILES_PATH,
"var/lib/gentoo/repos/distros/profiles/CLD/amd64")
)
def get_profile_name(path, repositories):
profile_path = path.value
if not profile_path:
return ""
for repository in repositories.value:
repository_path = repository['path']
repository_name = repository['name']
remove_part = os.path.normpath(os.path.join(repository_path,
"profiles"))
if profile_path.startswith(remove_part):
return "{}:{}".format(repository_name,
profile_path[len(remove_part) + 1:])
return profile_path
# Название профиля
Variable('name', type=StringType,
source=Dependence('.path', '..repositories',
depend=get_profile_name))
# Информация о репозиториях
# name: имя репозитория
# path: полный путь до репозитория
Variable('repositories', type=TableType,
source=[{'name': 'distros',
'path': os.path.join(TESTFILES_PATH,
"var/lib/gentoo/repos/distros")},
{'name': 'calculate',
'path': os.path.join(TESTFILES_PATH,
"var/lib/gentoo/repos/calculate")},
{'name': 'gentoo',
'path': os.path.join(TESTFILES_PATH,
"var/lib/gentoo/portage")}])

@ -0,0 +1,19 @@
import os
from calculate.variables.datavars import Variable, ListType, HashType
'''
system:
env_order -> list
env_path -> hash
'''
TESTFILES_PATH = os.path.join(os.getcwd(), 'tests/parameters/testfiles')
# Список мест, где есть calculate.ini файлы.
Variable('env_order', type=ListType, source=['system'])
# Отображение множества мест, где есть calculate.ini файлы, на пути к ним.
Variable('env_path', type=HashType,
source={'system': os.path.join(TESTFILES_PATH,
'parameters_ini/calculate.ini')})

@ -0,0 +1,58 @@
import os
import pytest
import shutil
from fastapi.testclient import TestClient
from calculate.server.server import Server
TESTFILES_PATH = os.path.join(os.getcwd(), 'tests/server/testfiles')
VARS_PATH = os.path.join(TESTFILES_PATH, 'variables')
COMMANDS_PATH = 'tests/server/testfiles/commands'
server = Server(datavars_path=VARS_PATH, commands_path=COMMANDS_PATH)
test_client = TestClient(server.app)
@pytest.mark.server
class TestServer:
def test_to_make_testfiles(self):
shutil.copytree(os.path.join(TESTFILES_PATH, 'gentoo.backup'),
os.path.join(TESTFILES_PATH, 'gentoo'),
symlinks=True)
def test_get_root_message(self):
response = test_client.get("/")
assert response.status_code == 200
assert response.json() == {"msg": "root msg"}
def test_get_commands_list(self):
response = test_client.get("/commands")
assert response.status_code == 200
assert response.json() == {"test_1":
{"title": "Test 1",
"category": "Test Category",
"icon": "/path/to/icon_1.png",
"command": "test_1"},
"test_2":
{"title": "Test 2",
"category": "Test Category",
"icon": "/path/to/icon_2.png",
"command": "cl_test_2"}}
def test_post_command(self):
response = test_client.get("/commands/")
assert response.status_code == 200
def test_get_command_by_cid(self):
response = test_client.get("/commands/0")
assert response.status_code == 200
assert response.json() == {"id": 0, "name": "command_0"}
def test_get_worker_message_by_wid(self):
response = test_client.get("/workers/0")
assert response.status_code == 200
data = response.json()
assert data == {'type': 'log', 'level': 'INFO',
'msg': 'recieved message INFO'}
def test_for_removing_testfiles(self):
shutil.rmtree(os.path.join(TESTFILES_PATH, 'gentoo'))

@ -0,0 +1,54 @@
from calculate.parameters.parameters import Description
from calculate.commands.commands import Command
from ..parameters.parameters import Parameter1, Parameter2
from ..scripts.scripts import script_1, script_2
command_1 = Command(command_id='test_1',
category='Test Category',
title='Test 1',
command="test_1",
script=script_1,
namespace='os',
icon="/path/to/icon_1.png",
rights=['group'],
parameters=[
Parameter1
(
'first-param', 'Test',
Description(
short='First parameter',
full='First parameter, but who cares'),
shortname='f'
).bind('os.linux.test_5'),
Parameter2
(
'second-param', 'Test',
Description(
short='Second parameter'),
shortname='s'
)])
command_2 = Command(command_id='test_2',
category='Test Category',
title='Test 2',
script=script_2,
namespace='os',
icon="/path/to/icon_2.png",
rights=['group'],
parameters=[
Parameter1
(
'first-param', 'Test',
Description(
short='First parameter',
full='First parameter, but who cares'),
shortname='f'
).bind('os.linux.test_5'),
Parameter2
(
'second-param', 'Test',
Description(
short='Second parameter'),
shortname='s'
)])

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save