Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

254 рядки
10 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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}'.")