|
|
|
|
from typing import Union, Callable, List
|
|
|
|
|
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 CommandDescriptionError(Exception):
|
|
|
|
|
'''Исключение кидаемое при наличии ошибок во время создания объекта
|
|
|
|
|
команды.'''
|
|
|
|
|
def __init__(self, message: str, command_id: str = '', title: str = ''):
|
|
|
|
|
self.message: str = message
|
|
|
|
|
self.command_id: str = command_id
|
|
|
|
|
self.command_title: str = title
|
|
|
|
|
|
|
|
|
|
def __str__(self) -> str:
|
|
|
|
|
if self.command_title:
|
|
|
|
|
return (f'can not create command {self.command_title}:'
|
|
|
|
|
f' {self.message}')
|
|
|
|
|
if self.command_id:
|
|
|
|
|
return f'can not create command {self.command_id}: {self.message}'
|
|
|
|
|
return f'can not create command: {self.message}'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CommandInitializationError(Exception):
|
|
|
|
|
'''Исключение кидаемое при наличии ошибок во время инициализации.'''
|
|
|
|
|
def __init__(self, message: str, title: str):
|
|
|
|
|
self.message: str = message
|
|
|
|
|
self.command_title: str = title
|
|
|
|
|
|
|
|
|
|
def __str__(self) -> str:
|
|
|
|
|
if self.command_title:
|
|
|
|
|
return (f'can not initialize command {self.command_title}:'
|
|
|
|
|
f' {self.message}')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Command:
|
|
|
|
|
'''Класс описания команды, предназначен для хранения информации о команде
|
|
|
|
|
и создания по данному описанию объекта лаунчера команды.'''
|
|
|
|
|
def __init__(self, command_id: str = '',
|
|
|
|
|
category: str = '',
|
|
|
|
|
title: str = '',
|
|
|
|
|
command: str = '',
|
|
|
|
|
script: Union[Callable, Script, None] = None,
|
|
|
|
|
args: Union[tuple, list] = tuple(),
|
|
|
|
|
parameters: Union[List[BaseParameter], None] = None,
|
|
|
|
|
namespace: Union[str, NamespaceNode, None] = None,
|
|
|
|
|
gui: bool = False,
|
|
|
|
|
icon: Union[str, List[str]] = '',
|
|
|
|
|
setvars: dict = {},
|
|
|
|
|
rights: List[str] = []):
|
|
|
|
|
# Идентификатор команды обязателен.
|
|
|
|
|
if not command_id:
|
|
|
|
|
raise CommandDescriptionError('command ID is not set')
|
|
|
|
|
self.__id: str = command_id
|
|
|
|
|
|
|
|
|
|
# Если собственно команда не была указана, получаем ее из ID.
|
|
|
|
|
if command:
|
|
|
|
|
self.__command: str = command
|
|
|
|
|
else:
|
|
|
|
|
self.__command: str = f'cl_{self.__id}'
|
|
|
|
|
|
|
|
|
|
# Название команды.
|
|
|
|
|
if not title:
|
|
|
|
|
raise CommandDescriptionError("title is not set",
|
|
|
|
|
command_id=command_id)
|
|
|
|
|
self.__title: str = title
|
|
|
|
|
|
|
|
|
|
# Категория, к которой относится команда.
|
|
|
|
|
if not category:
|
|
|
|
|
raise CommandDescriptionError("category is not set",
|
|
|
|
|
command_title=title)
|
|
|
|
|
self.__category: str = category
|
|
|
|
|
|
|
|
|
|
# Пространство имен относительно которого, будет выполняться скрипт.
|
|
|
|
|
self.__namespace: Union[str, NamespaceNode, None] = namespace
|
|
|
|
|
|
|
|
|
|
# Параметры, указываемые при вызове этой команды.
|
|
|
|
|
if parameters is None:
|
|
|
|
|
raise CommandDescriptionError("parameters is not set",
|
|
|
|
|
command_title=title)
|
|
|
|
|
self.__parameters: List[BaseParameter] = parameters
|
|
|
|
|
|
|
|
|
|
# Скрипт выполняемый при вызове этой команды.
|
|
|
|
|
if not script:
|
|
|
|
|
raise CommandDescriptionError("script is not set",
|
|
|
|
|
command_title=title)
|
|
|
|
|
self.__script: Script = script
|
|
|
|
|
|
|
|
|
|
# Аргументы, с которыми будет запущен скрипт. Пока только статичные
|
|
|
|
|
# значения.
|
|
|
|
|
self.__args = args
|
|
|
|
|
|
|
|
|
|
# Параметр указывающий, должена ли данная команда отображаться в
|
|
|
|
|
# графическом интерфейсе.
|
|
|
|
|
self.__gui: bool = gui
|
|
|
|
|
|
|
|
|
|
# Устанавливаем название иконки.
|
|
|
|
|
if not icon and self.__gui:
|
|
|
|
|
raise CommandDescriptionError("icon is not set",
|
|
|
|
|
command_title=title)
|
|
|
|
|
self.__icon: Union[str, List[str]] = icon
|
|
|
|
|
|
|
|
|
|
# Словарь с переменными и значениями, которые нужно им присвоить.
|
|
|
|
|
self.__setvars: dict = setvars
|
|
|
|
|
|
|
|
|
|
# Права, необходимые данной команде.
|
|
|
|
|
# TODO разобраться с тем, как использовать.
|
|
|
|
|
if not 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
|
|
|
|
|
|
|
|
|
|
@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):
|
|
|
|
|
'''Класс инкапcулирующий все данные о команде, а также необходимые для
|
|
|
|
|
нее параметры, модуль ввода-вывода, переменные и прочее. Предназначен
|
|
|
|
|
для конфигурации команды и запуска ее в воркере.'''
|
|
|
|
|
self._command = command
|
|
|
|
|
self._datavars = datavars
|
|
|
|
|
self._output = output
|
|
|
|
|
|
|
|
|
|
# Ищем пространство имен.
|
|
|
|
|
if self._command.namespace is not None:
|
|
|
|
|
if isinstance(self._command.namespace, str):
|
|
|
|
|
self._namespace = self._find_namespace(
|
|
|
|
|
self._command.namespace,
|
|
|
|
|
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):
|
|
|
|
|
args = self._command.args
|
|
|
|
|
self._script_launcher(*args)
|
|
|
|
|
|
|
|
|
|
def set_parameters(self, values: dict):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def get_parameters(self, group: Union[str, None] = None):
|
|
|
|
|
'''Метод для установки параметров.'''
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def _find_namespace(self, namespace_path: str,
|
|
|
|
|
datavars: Union[Datavars, NamespaceNode]
|
|
|
|
|
) -> NamespaceNode:
|
|
|
|
|
'''Метод для поиска пространств имен.'''
|
|
|
|
|
# TODO сделать где-нибудь единственный статический вариант.
|
|
|
|
|
parts = namespace_path.split('.')
|
|
|
|
|
namespace = datavars
|
|
|
|
|
for part in parts:
|
|
|
|
|
namespace = namespace[part]
|
|
|
|
|
return namespace
|
|
|
|
|
|
|
|
|
|
def _set_variables(self, setvars: dict,
|
|
|
|
|
datavars: Union[Datavars, NamespaceNode],
|
|
|
|
|
namespace: Union[NamespaceNode, None]) -> None:
|
|
|
|
|
'''Метод для установки указанных значений, указанным переменным.'''
|
|
|
|
|
force = False
|
|
|
|
|
|
|
|
|
|
for varname, value in setvars.items():
|
|
|
|
|
if varname.endswith('!'):
|
|
|
|
|
varname = varname[:-1]
|
|
|
|
|
force = True
|
|
|
|
|
else:
|
|
|
|
|
force = False
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
variable = DependenceAPI.find_variable(
|
|
|
|
|
varname, datavars,
|
|
|
|
|
current_namespace=namespace)
|
|
|
|
|
except VariableNotFoundError:
|
|
|
|
|
self._output.set_error(f"Can not set variable '{varname}':"
|
|
|
|
|
" variable does not exist.")
|
|
|
|
|
|
|
|
|
|
if not variable.readonly or force:
|
|
|
|
|
variable.set(value)
|
|
|
|
|
else:
|
|
|
|
|
self._output.set_error("Can not set readonly variable "
|
|
|
|
|
f"'{variable.get_fullname}'.")
|