You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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