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
*.pyo
*.swp
*.sock
build/
dist/
calculate_lib.egg-info/

@ -1,14 +1,13 @@
from types import FunctionType
from typing import Union, Callable, List
from calculate.scripts.scripts import Script
from calculate.variables.parameters import Parameters
from calculate.variables.datavars import NamespaceNode, DependenceAPI,\
DependenceError, VariableNotFoundError
from calculate.variables.loader import Datavars
from calculate.utils.io_module import IOModule
from ..scripts.scripts import Script
from ..parameters.parameters import BaseParameter, Parameters
from ..variables.datavars import NamespaceNode, DependenceAPI,\
VariableNotFoundError
from ..variables.loader import Datavars
from ..utils.io_module import IOModule
class CommandCreationError(Exception):
class CommandDescriptionError(Exception):
'''Исключение кидаемое при наличии ошибок во время создания объекта
команды.'''
def __init__(self, message: str, command_id: str = '', title: str = ''):
@ -38,103 +37,182 @@ class CommandInitializationError(Exception):
class Command:
'''Класс команды, предназначен для хранения информации о команде и привязки ее к модулю ввода/вывода'''
def __init__(self, command_id: str = '', category: str = '',
'''Класс описания команды, предназначен для хранения информации о команде
и создания по данному описанию объекта лаунчера команды.'''
def __init__(self, command_id: str = '',
category: str = '',
title: str = '',
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,
command: str = '', gui: bool = False,
command: str = '',
gui: bool = False,
icon: Union[str, List[str]] = '',
setvars: dict = {}, rights: List[str] = []):
self._datavars: Union[Datavars, NamespaceNode, None] = None
setvars: dict = {},
rights: List[str] = []):
# Идентификатор команды обязателен.
if not command_id:
raise CommandCreationError('command ID is not set')
self._id: str = command_id
raise CommandDescriptionError('command ID is not set')
self.__id: str = command_id
# Если собственно команда не была указана, получаем ее из ID.
if command:
self._command: str = command
self.__command: str = command
else:
self._command: str = f'cl_{self._id}'
self.__command: str = f'cl_{self.__id}'
# Название команды.
if not title:
raise CommandCreationError("title is not set",
command_id=command_id)
self._title: str = title
raise CommandDescriptionError("title is not set",
command_id=command_id)
self.__title: str = title
# Категория, к которой относится команда.
if not category:
raise CommandCreationError("category is not set",
command_title=title)
self._category: str = category
raise CommandDescriptionError("category is not set",
command_title=title)
self.__category: str = category
# Пространство имен относительно которого, будет выполняться скрипт.
self._namespace: Union[str, NamespaceNode, None] = namespace
self.__namespace: Union[str, NamespaceNode, None] = namespace
# Параметры, указываемые при вызове этой команды.
if parameters is None:
raise CommandCreationError("parameters is not set",
command_title=title)
self._parameters: Parameters = parameters
raise CommandDescriptionError("parameters is not set",
command_title=title)
self.__parameters: List[BaseParameter] = parameters
# Скрипт выполняемый при вызове этой команды.
if not script:
raise CommandCreationError("script is not set",
command_title=title)
elif isinstance(script, FunctionType):
# Поддержка способа описания скрипта в функции.
self._script: Script = script()
else:
self._script: Script = script
raise CommandDescriptionError("script is not set",
command_title=title)
self.__script: Script = script
# Аргументы, с которыми будет запущен скрипт. Пока только статичные
# значения.
self.__args = args
# Параметр указывающий, должена ли данная команда отображаться в
# графическом интерфейсе.
self._gui: bool = gui
self.__gui: bool = gui
# Устанавливаем название иконки.
if not icon and self._gui:
raise CommandCreationError("icon is not set",
command_title=title)
self._icon: Union[str, List[str]] = icon
if not icon and self.__gui:
raise CommandDescriptionError("icon is not set",
command_title=title)
self.__icon: Union[str, List[str]] = icon
# Словарь с переменными и значениями, которые нужно им присвоить.
self._setvars = setvars
self.__setvars: dict = setvars
# Права, необходимые данной команде.
# TODO разобраться с тем, как использовать.
if not rights:
raise CommandCreationError("rights is not set",
title=title)
self._rights = rights
raise CommandDescriptionError("rights is not set",
title=title)
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
def script(self) -> Script:
return self._script
return self.__script
def initialize(self, datavars: Union[Datavars, NamespaceNode],
output: IOModule) -> 'Command':
'''Метод для инициализации всего, что нужно инициализировать в команде.
'''
@property
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._output = output
# Сначала найдем пространство имен.
if self._namespace is not None:
if isinstance(self._namespace, str):
self._namespace = self._find_namespace(self._namespace,
datavars)
# Ищем пространство имен.
if self._command.namespace is not None:
if isinstance(self._command.namespace, str):
self._namespace = self._find_namespace(
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)
# Инициализируем скрипт.
self._script.initialize(output, datavars, self._namespace)
def run_command(self):
args = self._command.args
self._script_launcher(*args)
# Устанавливаем переменные, если нужно.
if self._setvars:
self._set_variables(self._setvars, datavars, self._namespace)
def set_parameters(self, values: dict):
pass
return self
def get_parameters(self, group: Union[str, None] = None):
'''Метод для установки параметров.'''
pass
def _find_namespace(self, namespace_path: str,
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
#
from .datavars import DependenceAPI, VariableWrapper, NamespaceNode
from .loader import Datavars
from ..variables.datavars import DependenceAPI, VariableWrapper, NamespaceNode
from ..variables.loader import Datavars
from collections import OrderedDict
from contextlib import contextmanager
from typing import Tuple, Union
from typing import Tuple, Union, Any
class ParameterError(Exception):
@ -541,7 +541,7 @@ class GroupWrapper:
class Parameters:
'''Класс контейнера параметров.'''
def __init__(self, datavars: Datavars = None, check_order=[]):
def __init__(self, datavars: Datavars, check_order=[]):
self._datavars = datavars
self._parameters = OrderedDict()
self._validation_dict = OrderedDict()
@ -558,24 +558,8 @@ class Parameters:
# параметров.
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]):
'''Метод для добавления некоторой совокупности параметров.'''
if not self._initialized:
self._parameters = []
self._parameters.extend(parameters)
for parameter in parameters:
self.add_parameter(parameter)
@ -671,12 +655,12 @@ class Parameters:
self._validation = False
self._validation_dict = OrderedDict()
def get_group_parameters(self, group_name):
def get_group_parameters(self, group_name: str):
'''Метод для получения списка параметров, относящихся к указанной
группе.'''
return self._parameters[group_name]
def get_descriptions(self):
def get_descriptions(self) -> dict:
'''Метод для получения словаря с описанием параметров.'''
output = OrderedDict()
for group, parameters in self._parameters.items():
@ -700,10 +684,10 @@ class Parameters:
return output
@property
def datavars(self):
def datavars(self) -> Union[Datavars, NamespaceNode]:
return self._datavars
def __getitem__(self, name):
def __getitem__(self, name: str) -> Any:
for group, parameters in self._parameters.items():
for parameter in parameters:
if parameter._name == name or parameter._shortname == name:

@ -3,9 +3,10 @@
import re
import inspect
from typing import Callable, Any, Union, List, Generator
from calculate.templates.template_processor import DirectoryProcessor
from calculate.variables.datavars import DependenceAPI, DependenceError,\
VariableNode, NamespaceNode,\
HashType, TableType
HashType, TableType, StringType
from calculate.variables.loader import Datavars
from calculate.utils.io_module import IOModule
from collections.abc import Iterable, Mapping
@ -35,79 +36,112 @@ class ConditionError(Exception):
class Script:
'''Класс скрипта, собирает задачи, обработчики, блоки и прочее. Принимает
аргументы.'''
def __init__(self, script_id: str, args: List[Any] = [],
'''Класс скрипта, собирает задачи, обработчики, блоки и прочее, хранит их
в себе. Создает экземпляры лаунчеров.'''
def __init__(self, script_id: str,
args: List[Any] = [],
success_message: str = None,
failed_message: str = None,
interrupt_message: str = None):
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_vars: list = []
self.__args: list = args
self._success_message: str = success_message
self._failed_message: str = failed_message
self._interrupt_message: str = interrupt_message
self.__success_message: str = success_message
self.__failed_message: str = failed_message
self.__interrupt_message: str = interrupt_message
self._output: IOModule = None
self._datavars: Union[Datavars, NamespaceNode] = None
self._namespace: Union[NamespaceNode, None] = None
@property
def id(self) -> str:
return self._id
self._handlers: set = set()
self._initialized: bool = False
@property
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']]
) -> '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
def initialize(self, output: IOModule,
datavars: Union[Datavars, NamespaceNode],
namespace: NamespaceNode = None) -> 'Script':
def make_launcher(self, output: IOModule,
datavars: Union[Datavars, NamespaceNode],
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._datavars = datavars
self._namespace = namespace
if not self._initialized:
self._script_namespace, self._args_vars =\
self.make_script_variables(self._id,
self._args_names,
self._datavars)
self._initialized = True
return self
self._script_namespace, self._args_vars = self.make_script_variables(
script.id,
script.args,
datavars)
def run(self) -> 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 task_item.id in self._handlers:
handlers_to_run.append(task_item)
founded_handlers.append(task_item)
elif essential_error is None:
try:
output = task_item.run(self._output,
self._datavars,
self._script_namespace,
self._namespace)
self._handlers.update(output)
handlers_to_run.update(output)
except Exception as error:
if isinstance(error, TaskError):
self._handlers.update(error.handlers)
handlers_to_run.update(error.handlers)
essential_error = error
for handler in handlers_to_run:
for handler in founded_handlers:
if handler.id not in handlers_to_run:
continue
try:
task_item.run(self._output, self._datavars,
self._script_namespace, self._namespace)
handler.run(self._output, self._datavars,
self._script_namespace, self._namespace)
except Exception as error:
# TODO Разобраться что делать с ошибками в обработчике.
self._output.set_error(f"error in handler '{task_item.id}':"
@ -123,7 +157,7 @@ class Script:
'''Метод для создания переменных скрипта. Создает пространство имен
скрипта и переменных аргументов.'''
if 'scripts' not in datavars:
scripts = Script.make_scripts_namespace(datavars)
scripts = ScriptLauncher.make_scripts_namespace(datavars)
else:
scripts = datavars.scripts
@ -132,11 +166,14 @@ class Script:
scripts.add_namespace(current_script)
else:
current_script = scripts[script_id]
current_script.clear()
# current_script.clear()
args_vars = []
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
@staticmethod
@ -157,14 +194,15 @@ class Script:
def __call__(self, *args):
'''Метод для запуска скрипта с аргументами.'''
if len(args) < len(self._args_names):
raise ScriptError((f"script '{self._id}' missing"
f" {len(self._args_names) - len(args)} required"
" arguments: '") +
"', '".join(self._args_names[len(args):]) + "'")
elif len(args) > len(self._args_names):
raise ScriptError(f"script '{self._id}' takes"
f" {len(self._args_names)} arguments, but"
if len(args) < len(self._script.args):
raise ScriptError(
(f"script '{self._script.id}' missing"
f" {len(self._script.args) - len(args)} required"
" arguments: '") +
"', '".join(self._script.args[len(args):]) + "'")
elif len(args) > len(self._script.args):
raise ScriptError(f"script '{self._script.id}' takes"
f" {len(self._script.args)} arguments, but"
f" {len(args)} were given")
args_values = self._get_args_values(args, self._datavars,
@ -191,10 +229,106 @@ class Script:
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:
'''Класс запускателя скриптов.'''
def __init__(self, script: Script, namespace: str = None,
args: List[Any] = [], when: 'Var' = None,
args: List[Any] = [],
when: 'Var' = None,
essential: bool = True):
self._script: Script = script
self._namespace: str = namespace
@ -204,9 +338,11 @@ class Run:
self._essential = essential
def run(self, output: IOModule, datavars: Union[Datavars, NamespaceNode],
parent_namespace: NamespaceNode, namespace: NamespaceNode) -> set:
'''Метод для запуска скрипта, указанного имеющегося в запускателе.'''
def run(self, output: IOModule,
datavars: Union[Datavars, NamespaceNode],
parent_namespace: NamespaceNode,
namespace: NamespaceNode) -> set:
'''Метод для запуска скрипта, указанного в запускателе.'''
if self._condition is None or self._condition(datavars,
namespace,
parent_namespace):
@ -222,8 +358,10 @@ class Run:
try:
# if not self._script._initialized:
self._script.initialize(output, datavars, self._namespace)
self._script(*self._args)
launcher = self._script.make_launcher(
output, datavars,
namespace=self._namespace)
launcher(*self._args)
except (ScriptError, TaskError) as error:
if self._essential:
raise ScriptError(f"essential script '{self._script._id}'"
@ -330,8 +468,6 @@ class Task:
else:
self._args = self._update_arguments(self._args_sources)
return self
def run(self, output: IOModule,
datavars: Union[Datavars, NamespaceNode],
script_namespace: NamespaceNode,
@ -664,7 +800,8 @@ class Until(Loop):
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()):
self._tasks: List[Task] = tasks
self._rescue_tasks: List[Task] = None
@ -736,7 +873,8 @@ class Handler:
def id(self) -> str:
return self._id
def run(self, output: IOModule, datavars: Union[Datavars, NamespaceNode],
def run(self, output: IOModule,
datavars: Union[Datavars, NamespaceNode],
script_namespace: NamespaceNode,
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
# Ставим True, чтобы потом проверить этот параметр в postparse
print(f'source value {parameter_value}')
print(f'source <- {real_path}')
if not os.path.exists(real_path):
return True
@ -592,10 +590,6 @@ class ParametersProcessor:
else:
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:
if group == 'install':
try:
@ -610,7 +604,6 @@ class ParametersProcessor:
self._parameters_container.remove_parameter('package')
return
print('check failed')
raise ConditionFailed(f"package '{parameter_value}'"
" does not match the template condition",
self.lineno)
@ -623,8 +616,6 @@ class ParametersProcessor:
if package[parameter] is not None:
if (group_package[parameter] is None
or group_package[parameter] != package[parameter]):
print(f'{group_package[parameter]} !='
f' {package[parameter]}')
break
else:
if package['use_flags'] is not None:

@ -719,8 +719,6 @@ class TemplateExecutor:
self.executor_output = {'target_path': None,
'stdout': None,
'stderr': None}
print('TARGET PATH:', target_path)
if parameters.append == 'skip':
return self.executor_output
@ -1762,7 +1760,8 @@ class DirectoryTree:
class DirectoryProcessor:
'''Класс обработчика директорий шаблонов.'''
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):
self.action = action
else:
@ -1781,7 +1780,6 @@ class DirectoryProcessor:
if 'cl_root_path' in datavars_module.main:
self.templates_root = join_paths(self.cl_chroot_path,
datavars_module.main.cl_root_path)
print('ROOT PATH:', self.templates_root)
else:
self.templates_root = self.cl_chroot_path
@ -1826,7 +1824,11 @@ class DirectoryProcessor:
# Разбираем atom имена пакетов, указанных для групп пакетов.
if groups:
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(
@ -1998,6 +2000,7 @@ class DirectoryProcessor:
self._run_exec_files()
self.template_executor.save_changes()
return self.template_executor.changed_files
def _execute_handlers(self):
'''Метод для запуска обработчиков добавленных в очередь обработчиков
@ -2159,7 +2162,6 @@ class DirectoryProcessor:
необходимости, заполнения деревьев директорий шаблонов, с помощью
которых далее выполняются шаблоны пакетов из merge.'''
directory_name = os.path.basename(current_directory_path)
print('CURRENT TARGET PATH =', current_target_path)
# Если включено заполнение дерева создаем пустой словарь для сбора
# содержимого текущей директории.

@ -1,6 +1,7 @@
# vim: fileencoding=utf-8
#
import os
import logging
import importlib
import importlib.util
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.files import read_file, FilesError
from calculate.utils.tools import Singleton
from calculate.utils.io_module import IOModule
from pyparsing import Literal, Word, ZeroOrMore, Group, Optional, restOfLine,\
empty, printables, OneOrMore, lineno, line, SkipTo,\
LineEnd, Combine, nums
@ -489,7 +489,7 @@ class VariableLoader:
def __init__(self, datavars, variables_path, repository_map=None):
self.datavars = datavars
self.output = datavars.output
self.logger = datavars.logger
self.ini_filler = NamespaceIniFiller()
self.variables_path = variables_path
@ -519,9 +519,9 @@ class VariableLoader:
if section in current_namespace:
current_namespace = current_namespace[section]
else:
self.output.set_error("Variable 'os.gentoo.repositories'"
" is not found. Can not load profile"
" variables.")
self.logger.error("Variable 'os.gentoo.repositories'"
" is not found. Can not load profile"
" variables.")
return
self.repository_map = self._get_repository_map(self.datavars)
@ -530,12 +530,12 @@ class VariableLoader:
'path' in self.datavars.os.gentoo.profile):
profile_path = self.datavars.os.gentoo.profile.path
else:
self.output.set_error("Variable 'os.gentoo.profile.path'"
" is not found. Can not load profile"
" variables.")
self.logger.error("Variable 'os.gentoo.profile.path'"
" is not found. Can not load profile"
" variables.")
return
self.output.set_info("Load variables from profile: '{}'.".format(
self.logger.info("Load variables from profile: '{}'.".format(
profile_path))
self._fill_from_profile_ini(profile_path)
@ -546,20 +546,20 @@ class VariableLoader:
env_order = self.datavars.system.env_order
env_path = self.datavars.system.env_path
except VariableNotFoundError as error:
self.output.set_warning("Can not load additional variables: {}".
format(str(error)))
self.logger.warning("Can not load additional variables: {}".
format(str(error)))
return
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))
if ini_file in env_path:
self.fill_from_custom_ini(env_path[ini_file].value)
self.output.set_success("Variables from '{}' are loaded".
format(ini_file))
self.logger.info("Variables from '{}' are loaded".format(
ini_file))
else:
self.output.set_warning("File '{}' is not found. Variables are"
" not loaded".format(ini_file))
self.logger.warning("File '{}' is not found. Variables are"
" not loaded".format(ini_file))
def _fill_from_package(self, current_namespace: NamespaceNode,
directory_path: str, package: str) -> None:
@ -575,6 +575,8 @@ class VariableLoader:
# Сначала загружаем переменные из файлов.
for file_node in file_nodes:
if not file_node.name.endswith('.py'):
continue
file_name = file_node.name[:-3]
Namespace.set_current_namespace(current_namespace)
# with self.test(file_name, current_namespace):
@ -603,8 +605,8 @@ class VariableLoader:
ini_file_text = read_file(file_path)
self.ini_filler.fill(self.datavars, ini_file_text)
except FilesError:
self.output.set_error("Can not load profile variables from"
" unexisting file: {}".format(file_path))
self.logger.error("Can not load profile variables from"
" unexisting file: {}".format(file_path))
def _get_repository_map(self, datavars):
'''Метод для получения из переменной словаря с репозиториями и путями
@ -621,13 +623,13 @@ class VariableLoader:
parsing_errors = self.ini_filler.errors
if parsing_errors:
for error in parsing_errors:
self.output.set_error(error)
self.output.set_warning('Some variables was not loaded.')
self.logger.error(error)
self.logger.warning('Some variables was not loaded.')
else:
self.output.set_success('All variables are loaded.')
self.logger.info('All variables are loaded.')
else:
self.output.set_error("Variables are not loaded. File '{}' does"
" not exist.".format(file_path))
self.logger.error("Variables are not loaded. File '{}' does"
" not exist.".format(file_path))
@contextmanager
def test(self, file_name, namespace):
@ -704,10 +706,16 @@ class CalculateIniSaver:
class Datavars:
'''Класс для хранения переменных и управления ими.'''
def __init__(self, variables_path='calculate/vars', repository_map=None,
io_module=IOModule()):
logger=None):
self._variables_path = variables_path
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._loader = VariableLoader(self, self._variables_path,
@ -749,8 +757,7 @@ class Datavars:
def _load_package(self, package_name):
'''Метод для загрузки переменных содержащихся в указанном пакете.'''
self.output.set_info("Loading datavars package '{}'".format(
package_name))
self.logger.info("Loading datavars package '{}'".format(package_name))
try:
self._loader.load_variables_package(package_name)
except Exception as error:
@ -843,5 +850,5 @@ class Datavars:
dict_to_save = self.variables_to_save[target]
target_path = target_paths[target].value
saver.save_to_ini(target_path, dict_to_save)
self.output.set_info("Variables for '{}' is saved in the"
" file: {}".format(target, target_path))
self.logger.info("Variables for '{}' is saved in the"
" 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.
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.
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,
package=package,
output_module=io_module,
dbpkg=args.dbpkg)
dbpkg=args.dbpkg,
**group_packages)
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 shutil
import pytest
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.parameters import Parameters, BaseParameter, Integer,\
String, ValidationError,\
Description
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')
@ -24,78 +25,37 @@ class TestCommands:
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'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',
category='Test Category',
title='Test',
script=test_script(),
parameters=test_parameters,
script=test_script,
namespace='os',
rights=['group'])
command.initialize(datavars, IOModule())
command.script.run()
rights=['group'],
parameters=[
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 ==\
'os.calculate = test1 test2'
@ -104,57 +64,7 @@ class TestCommands:
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'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(
test_parameters = [
MyShinyParameter('my-shiny', 'The Shiniest ones',
Description(
short='The shiny parameter',
@ -165,23 +75,24 @@ class TestCommands:
Description(short='Not from this world.'),
shortname='o'),
OneMoreParameter('one-more', 'The Others',
Description(full='Plan 9 from outer space.')))
Description(full='Plan 9 from outer space.'))]
command = Command(command_id='test',
category='Test Category',
title='Test',
script=test_script(),
script=test_script,
parameters=test_parameters,
namespace='os',
gui=True,
icon=['icon_1', 'icon_2'],
setvars={'os.hashvar_0': {'value1': 'new_1',
'value2': 'new_2'},
setvars={'os.hashvar_0':
{'value1': 'new_1',
'value2': 'new_2'},
'.linux.test_5': 1349},
rights=['install'])
command.initialize(datavars, IOModule())
command.script.run()
command_runner = command.make_runner(datavars, IOModule())
command_runner.run_command()
assert datavars.os.hashvar_0.get_hash() == {'value1': 'new_1',
'value2': 'new_2'}

@ -2,29 +2,29 @@ import os
import shutil
import pytest
from collections import OrderedDict
from calculate.variables.parameters import BaseParameter, Integer, String,\
Description, Parameters,\
ValidationError, Choice, Bool,\
List, CyclicValidationError,\
ParameterError, Table, TableValue
from calculate.parameters.parameters import BaseParameter, Integer, String,\
Description, Parameters,\
ValidationError, Choice, Bool,\
List, CyclicValidationError,\
ParameterError, Table, TableValue
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
class TestParameters:
def test_to_make_testfiles(self):
shutil.copytree(os.path.join(TESTFILES_PATH, 'gentoo.backup'),
os.path.join(TESTFILES_PATH, 'gentoo'),
symlinks=True)
os.path.join(TESTFILES_PATH, 'gentoo'), 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):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17'))
'variables'))
PARAMS = Parameters().initialize(datavars)
PARAMS = Parameters(datavars)
# Описание классов параметров.
class MyShinyParameter(BaseParameter):
@ -75,6 +75,17 @@ class TestParameters:
OneMoreParameter('one-more', 'The Others',
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.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):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17'))
'variables'))
PARAMS = Parameters().initialize(datavars)
PARAMS = Parameters(datavars)
# Описание классов параметров.
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):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17'))
'variables'))
PARAMS = Parameters().initialize(datavars)
PARAMS = Parameters(datavars)
# Описание классов параметров.
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):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17'))
'variables'))
PARAMS = Parameters().initialize(datavars)
PARAMS = Parameters(datavars)
# Описание классов параметров.
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):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17'))
'variables'))
PARAMS = Parameters().initialize(datavars)
PARAMS = Parameters(datavars)
# Описание классов параметров.
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):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17'))
'variables'))
PARAMS = Parameters().initialize(datavars)
PARAMS = Parameters(datavars)
# Описание классов параметров.
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):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17'))
PARAMS = Parameters().initialize(datavars)
'variables'))
PARAMS = Parameters(datavars)
# Описание классов параметров.
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):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17'))
PARAMS = Parameters().initialize(datavars)
'variables'))
PARAMS = Parameters(datavars)
# Описание классов параметров.
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):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17'))
PARAMS = Parameters().initialize(datavars)
'variables'))
PARAMS = Parameters(datavars)
# Описание классов параметров.
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):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17'))
PARAMS = Parameters().initialize(datavars)
'variables'))
PARAMS = Parameters(datavars)
# Описание классов параметров.
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):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17'))
PARAMS = Parameters().initialize(datavars)
'variables'))
PARAMS = Parameters(datavars)
# Описание классов параметров.
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):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17'))
PARAMS = Parameters().initialize(datavars)
'variables'))
PARAMS = Parameters(datavars)
# Описание классов параметров.
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):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17'))
PARAMS = Parameters().initialize(datavars)
'variables'))
PARAMS = Parameters(datavars)
# Описание классов параметров.
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):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17'))
PARAMS = Parameters().initialize(datavars)
'variables'))
PARAMS = Parameters(datavars)
# Описание классов параметров.
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):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17'))
PARAMS = Parameters().initialize(datavars)
'variables'))
PARAMS = Parameters(datavars)
# Описание классов параметров.
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):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17'))
PARAMS = Parameters().initialize(datavars)
'variables'))
PARAMS = Parameters(datavars)
# Описание классов параметров.
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):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17'))
PARAMS = Parameters().initialize(datavars)
'variables'))
PARAMS = Parameters(datavars)
# Описание классов параметров.
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):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17'))
PARAMS = Parameters().initialize(datavars)
'variables'))
PARAMS = Parameters(datavars)
# Описание классов параметров.
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):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17'))
PARAMS = Parameters().initialize(datavars)
'variables'))
PARAMS = Parameters(datavars)
# Описание классов параметров.
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):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17'))
PARAMS = Parameters().initialize(datavars)
'variables'))
PARAMS = Parameters(datavars)
# Описание классов параметров.
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):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17'))
PARAMS = Parameters().initialize(datavars)
'variables'))
PARAMS = Parameters(datavars)
# Описание классов параметров.
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):
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_17'))
PARAMS = Parameters().initialize(datavars)
'variables'))
PARAMS = Parameters(datavars)
# Описание классов параметров.
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
from calculate.scripts.scripts import Var, Task, Static, TaskError, Done,\
DoneAny, Success, SuccessAny, Failed,\
FailedAny, Block, For, While, Until,\
Handler, Script, Run, ScriptError,\
ActionError
ActionError, ScriptLauncher, RunTemplate
from calculate.variables.datavars import Namespace, Variable, IntegerType,\
StringType, BooleanType, ListType,\
HashType
from calculate.variables.loader import Datavars
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():
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):
Namespace.reset()
datavars = Namespace.datavars
script_namespace = Script.make_script_variables('test_script', [],
datavars)
script_namespace = ScriptLauncher.make_script_variables('test_script',
[], datavars)
with Namespace('os'):
Variable('var_1', source='value', type=StringType)
@ -103,13 +117,15 @@ class TestTasks():
def action(output, arg1):
return f'os.var = {arg1}'
Script('test_script'
).tasks(
Task(id='task',
name="Task",
action=action,
args=["os.var"])
).initialize(IOModule(), datavars, None)()
script = Script('test_script'
).tasks(
Task(id='task',
name="Task",
action=action,
args=["os.var"])
)
launcher = script.make_launcher(IOModule(), datavars, None)
launcher()
assert 'test_script' in datavars.scripts
assert 'task' in datavars.scripts.test_script
@ -139,7 +155,7 @@ class TestTasks():
action=action,
args=[Static('static_value'), "os.var_1"],
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 'task' in datavars.scripts.test_script
@ -166,7 +182,7 @@ class TestTasks():
action=action,
args=[Static('value')],
when=~Var('os.linux.var_1'))
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts
assert 'task' not in datavars.scripts.test_script
@ -191,7 +207,7 @@ class TestTasks():
args=[Static('value')],
essential=False,
when=Var('os.linux.var_1'))
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts
assert 'task' in datavars.scripts.test_script
@ -222,7 +238,7 @@ class TestTasks():
args=[Static('value')],
essential=True,
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):
Namespace.reset()
@ -267,7 +283,7 @@ class TestTasks():
action=action,
args=[Static('value_4')],
when=DoneAny('task_1', 'task_3'))
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts
assert 'task_3' not in datavars.scripts.test_script
@ -345,7 +361,7 @@ class TestTasks():
args=[Static('value_10')],
when=(Var('os.linux.var_1') &
SuccessAny('task_3', 'task_4')))
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts
assert 'task_4' not in datavars.scripts.test_script
@ -425,7 +441,7 @@ class TestTasks():
args=[Static('value_9')],
when=(Var('os.linux.var_1') &
FailedAny('task_2', 'task_3')))
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts
assert not datavars.scripts.test_script.task_2.success
@ -457,7 +473,7 @@ class TestTasks():
name="Task 1",
action=action,
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):
Namespace.reset()
@ -480,7 +496,7 @@ class TestTasks():
action=action,
args=['os.linux.var_1'],
set='os.linux.var_2')
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts
assert 'task' in datavars.scripts.test_script
@ -510,7 +526,7 @@ class TestTasks():
action=action,
args=['os.linux.var_1'],
set=['os.var_1', 'os.linux.var_2'])
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts
assert 'task' in datavars.scripts.test_script
@ -541,7 +557,7 @@ class TestTasks():
action=action,
args=['os.linux.var_1'],
set=('os.var_1', 'os.linux.var_2'))
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts
assert 'task' in datavars.scripts.test_script
@ -571,7 +587,7 @@ class TestTasks():
action=action,
args=['os.linux.var_1', 'os.linux.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 'task' in datavars.scripts.test_script
@ -596,7 +612,7 @@ class TestTasks():
name="Task",
action=action,
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):
Namespace.reset()
@ -618,7 +634,7 @@ class TestTasks():
action=action,
args=['os.linux.var_1'],
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):
Namespace.reset()
@ -641,7 +657,7 @@ class TestTasks():
name="Task 2",
action=action,
args=[Static('value_2')]))
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts
assert 'task_1' in datavars.scripts.test_script
@ -671,7 +687,7 @@ class TestTasks():
action=action,
args=[Static('value_2')]),
when=Var('os.linux.var_1'))
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts
assert 'task_1' in datavars.scripts.test_script
@ -699,7 +715,7 @@ class TestTasks():
action=action,
args=[Static('value_2')]),
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_4' not in datavars.scripts.test_script
@ -735,7 +751,7 @@ class TestTasks():
name="Task 3",
action=action,
args=[Static('rescue_value')]))
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts
assert 'task_1' in datavars.scripts.test_script
@ -777,7 +793,7 @@ class TestTasks():
action=action_2,
args=['scripts.test_script.value']),
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):
assert l_value == r_value
@ -818,7 +834,7 @@ class TestTasks():
action=action_2,
args=['scripts.test_script.value']),
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):
assert l_value == r_value
@ -851,7 +867,7 @@ class TestTasks():
set={'counter': 'os.counter',
'output': 'os.output'}),
loop=While(Var('os.counter') < Var('os.ceiling')))
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert datavars.os.output == expected
@ -893,7 +909,7 @@ class TestTasks():
args=['os.counter', 'os.ceiling'],
set='os.flag'),
loop=While(~Var('os.flag')))
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert datavars.os.output == expected
@ -920,7 +936,7 @@ class TestTasks():
set={'counter': 'os.counter',
'output': 'os.output'}),
loop=Until(Var('os.flag')))
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert datavars.os.output == ['counter = 0']
@ -962,7 +978,7 @@ class TestTasks():
args=['os.counter', 'os.ceiling'],
set='os.flag'),
loop=Until(~Var('os.flag')))
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert datavars.os.output == expected
@ -994,7 +1010,7 @@ class TestTasks():
action=action,
args=[Static('value_2')])
)
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert 'task_1' in datavars.scripts.test_script
assert 'task_2' in datavars.scripts.test_script
@ -1047,7 +1063,7 @@ class TestTasks():
action=action,
args=[Static('value_8')])
)
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts
assert 'task_1' in datavars.scripts.test_script
@ -1112,7 +1128,7 @@ class TestTasks():
action=action,
args=[Static('value_8')])
)
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts
assert 'task_1' in datavars.scripts.test_script
@ -1177,7 +1193,7 @@ class TestTasks():
action=action,
args=[Static('value_8')])
)
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts
assert 'task_1' in datavars.scripts.test_script
@ -1242,7 +1258,7 @@ class TestTasks():
action=action,
args=[Static('value_8')])
)
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts
assert 'task_1' in datavars.scripts.test_script
@ -1311,7 +1327,7 @@ class TestTasks():
action=action,
args=[Static('value_8')])
)
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts
assert 'task_1' in datavars.scripts.test_script
@ -1340,6 +1356,16 @@ class TestTasks():
Script('test_script',
args=['arg1', 'arg_2'],
).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',
name="Task 1",
action=action,
@ -1366,18 +1392,8 @@ class TestTasks():
Task(id='task_6',
name="Task 6",
action=action,
args=[Static('value_6')]),
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')])
)
).initialize(IOModule(), datavars, None)(True, False)
args=[Static('value_6')])
).make_launcher(IOModule(), datavars, None)(True, False)
assert 'test_script' in datavars.scripts
assert 'task_1' in datavars.scripts.test_script
@ -1411,7 +1427,7 @@ class TestTasks():
name="Task",
action=action,
args=[Static('value_2')]),
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert 'test_script_1' in datavars.scripts
assert 'test_script_2' in datavars.scripts
@ -1448,7 +1464,7 @@ class TestTasks():
name="Task",
action=action,
args=[Static('value_2')]),
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert 'test_script_1' in datavars.scripts
assert 'test_script_2' in datavars.scripts
@ -1485,7 +1501,7 @@ class TestTasks():
name="Task",
action=action,
args=[Static('value_2')]),
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert 'test_script_1' in datavars.scripts
assert 'test_script_2' in datavars.scripts
@ -1527,7 +1543,7 @@ class TestTasks():
name="Task 3",
action=action,
args=[Static('value_3')]),
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert 'test_script_1' in datavars.scripts
assert 'test_script_2' in datavars.scripts
@ -1573,7 +1589,7 @@ class TestTasks():
name="Task 3",
action=action,
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):
Namespace.reset()
@ -1607,7 +1623,7 @@ class TestTasks():
name="Task 3",
action=action,
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):
Namespace.reset()
@ -1637,7 +1653,7 @@ class TestTasks():
args=[Static('value_2')]),
Run(script_1, namespace='os',
args=[Static(13)])
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert 'test_script_1' in datavars.scripts
assert 'test_script_2' in datavars.scripts
@ -1681,7 +1697,7 @@ class TestTasks():
name="Task 2",
action=action,
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):
Namespace.reset()
@ -1719,7 +1735,7 @@ class TestTasks():
name="Task 3",
action=action,
args=[Static('value_3')])
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert 'task_1' in datavars.scripts.test_script_1
assert not datavars.scripts.test_script_1.task_1.success
@ -1748,11 +1764,10 @@ class TestTasks():
name="Task 1",
action=action,
args=['os.var_1'])
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts
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):
Namespace.reset()
@ -1776,7 +1791,7 @@ class TestTasks():
essential=False,
action=action,
args=['os.var_1'])
).initialize(IOModule(), datavars, None)()
).make_launcher(IOModule(), datavars, None)()
assert 'test_script' in datavars.scripts
assert 'task_1' in datavars.scripts.test_script
@ -1797,18 +1812,19 @@ class TestTasks():
def action(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',
args=['arg']
).tasks(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')))
).tasks(*tasks_1)
Script('test_script_2',
).tasks(Task(id='task_1',
@ -1831,7 +1847,7 @@ class TestTasks():
when=Var('.linux.var_4')),
Run(script_1, namespace='os', args=[False],
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_2' in datavars.scripts
@ -1852,3 +1868,53 @@ class TestTasks():
assert 'task_2' in datavars.scripts.test_script_1
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