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 class CommandCreationError(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 = '', script: Union[Callable, Script, None] = None, parameters: Union[Parameters, None] = None, namespace: Union[str, NamespaceNode, None] = None, command: str = '', gui: bool = False, icon: Union[str, List[str]] = '', setvars: dict = {}, rights: List[str] = []): self._datavars: Union[Datavars, NamespaceNode, None] = None if not command_id: raise CommandCreationError('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 CommandCreationError("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 # Пространство имен относительно которого, будет выполняться скрипт. self._namespace: Union[str, NamespaceNode, None] = namespace # Параметры, указываемые при вызове этой команды. if parameters is None: raise CommandCreationError("parameters is not set", command_title=title) self._parameters: Parameters = 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 # Параметр указывающий, должена ли данная команда отображаться в # графическом интерфейсе. 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 # Словарь с переменными и значениями, которые нужно им присвоить. self._setvars = setvars # Права, необходимые данной команде. if not rights: raise CommandCreationError("rights is not set", title=title) self._rights = rights @property def script(self) -> Script: return self._script def initialize(self, datavars: Union[Datavars, NamespaceNode], output: IOModule) -> '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) # Инициализируем параметры. self._parameters.initialize(datavars) # Инициализируем скрипт. self._script.initialize(output, datavars, self._namespace) # Устанавливаем переменные, если нужно. if self._setvars: self._set_variables(self._setvars, datavars, self._namespace) return self 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}'.")