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