|
|
|
@ -3,11 +3,11 @@
|
|
|
|
|
import re
|
|
|
|
|
import ast
|
|
|
|
|
import dis
|
|
|
|
|
from typing import List, Any, Union
|
|
|
|
|
from contextlib import contextmanager
|
|
|
|
|
from inspect import signature, getsource
|
|
|
|
|
from types import FunctionType, LambdaType
|
|
|
|
|
from calculate.utils.tools import Singleton
|
|
|
|
|
from typing import List, Any, Union, Generator, Callable
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DependenceError(Exception):
|
|
|
|
@ -27,25 +27,25 @@ class VariableNotFoundError(VariableError):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CyclicVariableError(VariableError):
|
|
|
|
|
def __init__(self, *queue):
|
|
|
|
|
self.queue = queue
|
|
|
|
|
def __init__(self, *queue: List[str]):
|
|
|
|
|
self.queue: List[str] = queue
|
|
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
|
def __str__(self) -> str:
|
|
|
|
|
return "Cyclic dependence in variables: {}".format(", ".join(
|
|
|
|
|
self.queue[:-1]))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableType:
|
|
|
|
|
'''Базовый класс для типов.'''
|
|
|
|
|
name = 'undefined'
|
|
|
|
|
python_type = None
|
|
|
|
|
name: str = 'undefined'
|
|
|
|
|
python_type: type = None
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def process_value(cls, value, variable):
|
|
|
|
|
def process_value(cls, value: Any, variable: "VariableNode") -> Any:
|
|
|
|
|
return value
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def readonly(cls, variable_object) -> None:
|
|
|
|
|
def readonly(cls, variable_object: "VariableNode") -> None:
|
|
|
|
|
variable_object.variable_type = cls
|
|
|
|
|
variable_object.readonly = True
|
|
|
|
|
|
|
|
|
@ -53,21 +53,21 @@ class VariableType:
|
|
|
|
|
class IniType(VariableType):
|
|
|
|
|
'''Класс, соответствующий типу переменных созданных в calculate.ini файлах.
|
|
|
|
|
'''
|
|
|
|
|
name = 'ini'
|
|
|
|
|
name: str = 'ini'
|
|
|
|
|
python_type: type = str
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def process_value(cls, value, variable):
|
|
|
|
|
def process_value(cls, value: Any, variable: "VariableNode") -> Any:
|
|
|
|
|
return value
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class StringType(VariableType):
|
|
|
|
|
'''Класс, соответствующий типу переменных с строковым значением.'''
|
|
|
|
|
name = 'string'
|
|
|
|
|
name: str = 'string'
|
|
|
|
|
python_type: type = str
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def process_value(cls, value, variable) -> str:
|
|
|
|
|
def process_value(cls, value: Any, variable: "VariableNode") -> str:
|
|
|
|
|
if isinstance(value, str):
|
|
|
|
|
return value
|
|
|
|
|
else:
|
|
|
|
@ -82,11 +82,11 @@ class StringType(VariableType):
|
|
|
|
|
|
|
|
|
|
class IntegerType(VariableType):
|
|
|
|
|
'''Класс, соответствующий типу переменных с целочисленным значением.'''
|
|
|
|
|
name = 'integer'
|
|
|
|
|
name: str = 'integer'
|
|
|
|
|
python_type: type = int
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def process_value(cls, value, variable) -> int:
|
|
|
|
|
def process_value(cls, value: Any, variable: "VariableNode") -> int:
|
|
|
|
|
if isinstance(value, int):
|
|
|
|
|
return value
|
|
|
|
|
else:
|
|
|
|
@ -101,11 +101,11 @@ class IntegerType(VariableType):
|
|
|
|
|
|
|
|
|
|
class FloatType(VariableType):
|
|
|
|
|
'''Класс, соответствующий типу переменных с вещественным значением.'''
|
|
|
|
|
name = 'float'
|
|
|
|
|
name: str = 'float'
|
|
|
|
|
python_type: type = float
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def process_value(cls, value, variable) -> float:
|
|
|
|
|
def process_value(cls, value: Any, variable: "VariableNode") -> float:
|
|
|
|
|
if isinstance(value, float):
|
|
|
|
|
return value
|
|
|
|
|
else:
|
|
|
|
@ -120,13 +120,13 @@ class FloatType(VariableType):
|
|
|
|
|
|
|
|
|
|
class BooleanType(VariableType):
|
|
|
|
|
'''Класс, соответствующий типу переменных с булевым значением.'''
|
|
|
|
|
name = 'bool'
|
|
|
|
|
name: str = 'bool'
|
|
|
|
|
python_type: type = bool
|
|
|
|
|
true_values = {'True', 'true'}
|
|
|
|
|
false_values = {'False', 'false'}
|
|
|
|
|
true_values: set = {'True', 'true'}
|
|
|
|
|
false_values: set = {'False', 'false'}
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def process_value(cls, value, variable) -> bool:
|
|
|
|
|
def process_value(cls, value: Any, variable: "VariableNode") -> bool:
|
|
|
|
|
if isinstance(value, bool):
|
|
|
|
|
return value
|
|
|
|
|
elif isinstance(value, str):
|
|
|
|
@ -148,7 +148,7 @@ class ListType(VariableType):
|
|
|
|
|
python_type: type = list
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def process_value(cls, value, variable) -> list:
|
|
|
|
|
def process_value(cls, value: Any, variable: "VariableNode") -> list:
|
|
|
|
|
if isinstance(value, list):
|
|
|
|
|
return value
|
|
|
|
|
elif isinstance(value, str):
|
|
|
|
@ -171,18 +171,20 @@ class HashValue:
|
|
|
|
|
хозяина, что позволяет инвалидировать подписки переменной хозяина при любом
|
|
|
|
|
изменении хэша или инвалидировать весь хэш при изменении одной из
|
|
|
|
|
зависимостей.'''
|
|
|
|
|
def __init__(self, key: str, value, master_variable, parent):
|
|
|
|
|
self.key = key
|
|
|
|
|
self.value = value
|
|
|
|
|
self.master_variable = master_variable
|
|
|
|
|
self.parent = parent
|
|
|
|
|
def __init__(self, key: Any, value: Any,
|
|
|
|
|
master_variable: "VariableNode",
|
|
|
|
|
parent: "Hash"):
|
|
|
|
|
self.key: Any = key
|
|
|
|
|
self.value: Any = value
|
|
|
|
|
self.master_variable: "VariableNode" = master_variable
|
|
|
|
|
self.parent: "Hash" = parent
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def subscriptions(self):
|
|
|
|
|
def subscriptions(self) -> set:
|
|
|
|
|
return self.master_variable.subscriptions
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def subscribers(self):
|
|
|
|
|
def subscribers(self) -> set:
|
|
|
|
|
return self.master_variable.subscribers
|
|
|
|
|
|
|
|
|
|
def get_value(self) -> str:
|
|
|
|
@ -191,7 +193,7 @@ class HashValue:
|
|
|
|
|
self = self.master_variable.get_value()[self.key]
|
|
|
|
|
return self.value
|
|
|
|
|
|
|
|
|
|
def set(self, value) -> None:
|
|
|
|
|
def set(self, value: Any) -> None:
|
|
|
|
|
'''Метод для задания одного значения хэша без изменения источника
|
|
|
|
|
значения.'''
|
|
|
|
|
current_hash = self.master_variable.get_value().get_hash()
|
|
|
|
@ -206,17 +208,18 @@ class HashValue:
|
|
|
|
|
class Hash:
|
|
|
|
|
'''Класс реализующий контейнер для хранения хэша в переменной
|
|
|
|
|
соответствующего типа.'''
|
|
|
|
|
def __init__(self, values: dict, master_variable, parent=None):
|
|
|
|
|
self.fixed = master_variable.fixed
|
|
|
|
|
self._values = dict()
|
|
|
|
|
self._fields = set()
|
|
|
|
|
def __init__(self, values: dict,
|
|
|
|
|
master_variable: "VariableNode"):
|
|
|
|
|
self.fixed: bool = master_variable.fixed
|
|
|
|
|
self._values: dict = dict()
|
|
|
|
|
self._fields: set = set()
|
|
|
|
|
for key, value in values.items():
|
|
|
|
|
self._values.update({key: HashValue(key, value, master_variable,
|
|
|
|
|
self)})
|
|
|
|
|
if self.fixed:
|
|
|
|
|
self._fields.add(key)
|
|
|
|
|
self.master_variable = master_variable
|
|
|
|
|
self.row_index = None
|
|
|
|
|
self.master_variable: "VariableNode" = master_variable
|
|
|
|
|
self.row_index: Union[int, None] = None
|
|
|
|
|
|
|
|
|
|
def get_hash(self) -> dict:
|
|
|
|
|
'''Метод для получения словаря из значений хэша.'''
|
|
|
|
@ -258,7 +261,7 @@ class Hash:
|
|
|
|
|
key=key,
|
|
|
|
|
hash_name=self.master_variable.get_fullname()))
|
|
|
|
|
|
|
|
|
|
def __iter__(self):
|
|
|
|
|
def __iter__(self) -> Generator[Any, None, None]:
|
|
|
|
|
for key in self._values.keys():
|
|
|
|
|
yield key
|
|
|
|
|
|
|
|
|
@ -271,11 +274,11 @@ class Hash:
|
|
|
|
|
|
|
|
|
|
class HashType(VariableType):
|
|
|
|
|
'''Класс, соответствующий типу переменных хэшей.'''
|
|
|
|
|
name = 'hash'
|
|
|
|
|
name: str = 'hash'
|
|
|
|
|
python_type: type = dict
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def process_value(cls, values, variable) -> Hash:
|
|
|
|
|
def process_value(cls, values: Any, variable: "VariableNode") -> Hash:
|
|
|
|
|
if not isinstance(values, dict):
|
|
|
|
|
raise VariableTypeError("can not set value with type '{_type}' to"
|
|
|
|
|
" hash variable: value must be 'dict' type"
|
|
|
|
@ -287,7 +290,7 @@ class HashType(VariableType):
|
|
|
|
|
return updated_hash
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def fixed(cls, variable_object) -> None:
|
|
|
|
|
def fixed(cls, variable_object: "VariableNode") -> None:
|
|
|
|
|
'''Метод, который передается переменной вместо типа, если нужно задать
|
|
|
|
|
тип фиксированного хэша.'''
|
|
|
|
|
variable_object.variable_type = cls
|
|
|
|
@ -296,11 +299,12 @@ class HashType(VariableType):
|
|
|
|
|
|
|
|
|
|
class Table:
|
|
|
|
|
'''Класс, соответствующий типу переменных таблиц.'''
|
|
|
|
|
def __init__(self, values: List[dict], master_variable, fields=None):
|
|
|
|
|
self._rows = list()
|
|
|
|
|
self.master_variable = master_variable
|
|
|
|
|
def __init__(self, values: List[dict], master_variable: "VariableNode",
|
|
|
|
|
fields: Union[List[str], None] = None):
|
|
|
|
|
self._rows: List[dict] = list()
|
|
|
|
|
self.master_variable: "VariableNode" = master_variable
|
|
|
|
|
|
|
|
|
|
self.columns = set()
|
|
|
|
|
self.columns: set = set()
|
|
|
|
|
if fields is not None:
|
|
|
|
|
self.columns.update(fields)
|
|
|
|
|
else:
|
|
|
|
@ -319,7 +323,7 @@ class Table:
|
|
|
|
|
'''Метод для получения всего списка строк таблицы.'''
|
|
|
|
|
return self._rows
|
|
|
|
|
|
|
|
|
|
def add_row(self, row: dict):
|
|
|
|
|
def add_row(self, row: dict) -> None:
|
|
|
|
|
'''Метод для добавления строк в таблицу.'''
|
|
|
|
|
self._check_columns(row)
|
|
|
|
|
self._rows.append(row)
|
|
|
|
@ -353,7 +357,7 @@ class Table:
|
|
|
|
|
else:
|
|
|
|
|
raise VariableError("Table value is not subscriptable")
|
|
|
|
|
|
|
|
|
|
def __iter__(self):
|
|
|
|
|
def __iter__(self) -> Generator[dict, None, None]:
|
|
|
|
|
for row in self._rows:
|
|
|
|
|
yield row
|
|
|
|
|
|
|
|
|
@ -362,17 +366,18 @@ class Table:
|
|
|
|
|
return key in self._values
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def __len__(self):
|
|
|
|
|
def __len__(self) -> int:
|
|
|
|
|
return len(self._rows)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TableType(VariableType):
|
|
|
|
|
name = 'table'
|
|
|
|
|
name: str = 'table'
|
|
|
|
|
# TODO Сомнительно.
|
|
|
|
|
python_type: type = list
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def process_value(self, value: List[dict], variable) -> Table:
|
|
|
|
|
def process_value(self, value: List[dict],
|
|
|
|
|
variable: "VariableNode") -> Table:
|
|
|
|
|
if not isinstance(value, list) and not isinstance(value, Table):
|
|
|
|
|
raise VariableTypeError("can not set value with type '{_type}' to"
|
|
|
|
|
" hash variable: value must be 'dict' type"
|
|
|
|
@ -390,10 +395,10 @@ class TableType(VariableType):
|
|
|
|
|
class Static:
|
|
|
|
|
'''Класс для указания в качестве аргументов зависимостей статичных
|
|
|
|
|
значений, а не только переменных.'''
|
|
|
|
|
def __init__(self, value):
|
|
|
|
|
self.value = value
|
|
|
|
|
def __init__(self, value: Any):
|
|
|
|
|
self.value: Any = value
|
|
|
|
|
|
|
|
|
|
def get_value(self):
|
|
|
|
|
def get_value(self) -> Any:
|
|
|
|
|
return self.value
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -401,9 +406,10 @@ class VariableWrapper:
|
|
|
|
|
'''Класс обертки для переменных, с помощью которого отслеживается
|
|
|
|
|
применение переменной в образовании значения переменной от нее зависящей.
|
|
|
|
|
'''
|
|
|
|
|
def __init__(self, variable, subscriptions):
|
|
|
|
|
self._variable = variable
|
|
|
|
|
self._subscriptions = subscriptions
|
|
|
|
|
def __init__(self, variable: "VariableNode",
|
|
|
|
|
subscriptions: set):
|
|
|
|
|
self._variable: "VariableNode" = variable
|
|
|
|
|
self._subscriptions: set = subscriptions
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def value(self):
|
|
|
|
@ -429,12 +435,12 @@ class VariableWrapper:
|
|
|
|
|
|
|
|
|
|
class DependenceSource:
|
|
|
|
|
'''Класс зависимости как источника значения переменной.'''
|
|
|
|
|
def __init__(self, variables: tuple, depend=None):
|
|
|
|
|
self.error = None
|
|
|
|
|
self._args = variables
|
|
|
|
|
self.depend_function = depend
|
|
|
|
|
self._subscriptions = set()
|
|
|
|
|
self._args_founded = False
|
|
|
|
|
def __init__(self, variables: tuple,
|
|
|
|
|
depend: Union[Callable, None] = None):
|
|
|
|
|
self._args: Union[tuple, list] = variables
|
|
|
|
|
self.depend_function: Union[Callable, None] = depend
|
|
|
|
|
self._subscriptions: set = set()
|
|
|
|
|
self._args_founded: bool = False
|
|
|
|
|
|
|
|
|
|
def check(self) -> None:
|
|
|
|
|
'''Метод для запуска проверки корректности функции зависимости, а также
|
|
|
|
@ -469,7 +475,7 @@ class DependenceSource:
|
|
|
|
|
in self._args]),
|
|
|
|
|
str(error)))
|
|
|
|
|
|
|
|
|
|
def _get_args(self, namespace):
|
|
|
|
|
def _get_args(self, namespace: "NamespaceNode") -> None:
|
|
|
|
|
'''Метод для преобразования списка аргументов функции зависимости,
|
|
|
|
|
содержащего переменные и строки, в список аргументов состоящий только
|
|
|
|
|
из нод переменных и значений хэшей.'''
|
|
|
|
@ -477,7 +483,8 @@ class DependenceSource:
|
|
|
|
|
self._args = self.find_variables(self._args, namespace)
|
|
|
|
|
self._args_founded = True
|
|
|
|
|
|
|
|
|
|
def find_variables(self, variables, namespace):
|
|
|
|
|
def find_variables(self, variables: Union[tuple, list],
|
|
|
|
|
namespace: "NamespaceNode") -> List["VariableNode"]:
|
|
|
|
|
'''Метод для поиска переменных по заданным путям к ним.'''
|
|
|
|
|
output = []
|
|
|
|
|
for variable in variables:
|
|
|
|
@ -514,7 +521,8 @@ class DependenceSource:
|
|
|
|
|
self.check_signature(function_to_check, self._args)
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def check_signature(function_to_check, arguments):
|
|
|
|
|
def check_signature(function_to_check: Callable,
|
|
|
|
|
arguments: Union[tuple, list]) -> None:
|
|
|
|
|
'''Метод для проверки соответствия сигнатуры функции и заданного для
|
|
|
|
|
нее набора аргументов.'''
|
|
|
|
|
function_signature = signature(function_to_check)
|
|
|
|
@ -524,12 +532,12 @@ class DependenceSource:
|
|
|
|
|
len(function_signature.parameters),
|
|
|
|
|
len(arguments)))
|
|
|
|
|
|
|
|
|
|
def __ne__(self, other) -> bool:
|
|
|
|
|
def __ne__(self, other: Any) -> bool:
|
|
|
|
|
if not isinstance(other, DependenceSource):
|
|
|
|
|
return True
|
|
|
|
|
return not self == other
|
|
|
|
|
|
|
|
|
|
def __eq__(self, other) -> bool:
|
|
|
|
|
def __eq__(self, other: Any) -> bool:
|
|
|
|
|
if not isinstance(other, DependenceSource):
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
@ -568,44 +576,44 @@ class DependenceSource:
|
|
|
|
|
class VariableNode:
|
|
|
|
|
'''Класс ноды соответствующей переменной в дереве переменных.'''
|
|
|
|
|
def __init__(self, name: str, namespace: "NamespaceNode",
|
|
|
|
|
variable_type=VariableType,
|
|
|
|
|
source=None, fields: list = []):
|
|
|
|
|
self.name = name
|
|
|
|
|
variable_type: type = VariableType,
|
|
|
|
|
source: Any = None, fields: list = []):
|
|
|
|
|
self.name: str = name
|
|
|
|
|
if issubclass(variable_type, VariableType):
|
|
|
|
|
self.variable_type = variable_type
|
|
|
|
|
self.variable_type: type = variable_type
|
|
|
|
|
else:
|
|
|
|
|
raise VariableTypeError('variable_type must be VariableType'
|
|
|
|
|
', but not {}'.format(type(variable_type)))
|
|
|
|
|
self.calculating = False
|
|
|
|
|
self.fields = fields
|
|
|
|
|
self.calculating: bool = False
|
|
|
|
|
self.fields: list = fields
|
|
|
|
|
|
|
|
|
|
self.namespace = namespace
|
|
|
|
|
self.namespace: "NamespaceNode" = namespace
|
|
|
|
|
self.namespace.add_variable(self)
|
|
|
|
|
|
|
|
|
|
self.subscribers = set()
|
|
|
|
|
self.subscribers: set = set()
|
|
|
|
|
# Список текущих подписок, для проверки их актуальности при
|
|
|
|
|
# динамическом связывании.
|
|
|
|
|
self._subscriptions = set()
|
|
|
|
|
self._subscriptions: set = set()
|
|
|
|
|
|
|
|
|
|
self.value = None
|
|
|
|
|
self._invalidated = True
|
|
|
|
|
self.value: Any = None
|
|
|
|
|
self._invalidated: bool = True
|
|
|
|
|
|
|
|
|
|
# Флаг имеющий значение только для переменных типа HashType.
|
|
|
|
|
# Предназначен для включения проверки соответствия полей хэша при
|
|
|
|
|
# установке значения.
|
|
|
|
|
self._fixed = False
|
|
|
|
|
self._fixed: bool = False
|
|
|
|
|
|
|
|
|
|
# Источник значения переменной, может быть значением, а может быть
|
|
|
|
|
# зависимостью.
|
|
|
|
|
self._source = source
|
|
|
|
|
self._source: Any = source
|
|
|
|
|
if source is not None:
|
|
|
|
|
self.update_value()
|
|
|
|
|
|
|
|
|
|
# Флаг, указывающий, что значение было изменено в процессе работы
|
|
|
|
|
# утилит или с помощью тега set из шаблона.
|
|
|
|
|
self.set_by_user = False
|
|
|
|
|
self.set_by_user: bool = False
|
|
|
|
|
|
|
|
|
|
self._readonly = False
|
|
|
|
|
self._readonly: bool = False
|
|
|
|
|
|
|
|
|
|
def update_value(self) -> None:
|
|
|
|
|
'''Метод для обновления значения переменной с помощью указанного
|
|
|
|
@ -646,7 +654,8 @@ class VariableNode:
|
|
|
|
|
self._invalidated = False
|
|
|
|
|
|
|
|
|
|
def set_variable_type(self, variable_type: VariableType,
|
|
|
|
|
readonly: str = None, fixed: str = None) -> None:
|
|
|
|
|
readonly: Union[bool, None] = None,
|
|
|
|
|
fixed: Union[bool, None] = None) -> None:
|
|
|
|
|
'''Метод для установки типа переменной.'''
|
|
|
|
|
if readonly is not None and isinstance(readonly, bool):
|
|
|
|
|
self._readonly = readonly
|
|
|
|
@ -664,7 +673,7 @@ class VariableNode:
|
|
|
|
|
elif callable(variable_type):
|
|
|
|
|
variable_type(self)
|
|
|
|
|
|
|
|
|
|
def set(self, value):
|
|
|
|
|
def set(self, value: Any) -> None:
|
|
|
|
|
'''Метод для установки временного пользовательского значения
|
|
|
|
|
переменной.'''
|
|
|
|
|
# Сбрасываем флаги, чтобы провести инвалидацию.
|
|
|
|
@ -674,7 +683,7 @@ class VariableNode:
|
|
|
|
|
self.value = self.variable_type.process_value(value, self)
|
|
|
|
|
self._invalidate(set_by_user=True)
|
|
|
|
|
|
|
|
|
|
def reset(self):
|
|
|
|
|
def reset(self) -> None:
|
|
|
|
|
'''Метод для сброса пользовательского значения.'''
|
|
|
|
|
if self.set_by_user:
|
|
|
|
|
self._invalidated = False
|
|
|
|
@ -682,11 +691,11 @@ class VariableNode:
|
|
|
|
|
self._invalidate()
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def source(self):
|
|
|
|
|
def source(self) -> None:
|
|
|
|
|
return self._source
|
|
|
|
|
|
|
|
|
|
@source.setter
|
|
|
|
|
def source(self, source) -> None:
|
|
|
|
|
def source(self, source: Any) -> None:
|
|
|
|
|
# Если источники не совпадают или текущее значение переменной было
|
|
|
|
|
# установлено пользователем, то инвалидируем переменную и меняем
|
|
|
|
|
# источник.
|
|
|
|
@ -712,10 +721,10 @@ class VariableNode:
|
|
|
|
|
return self._fixed
|
|
|
|
|
|
|
|
|
|
@fixed.setter
|
|
|
|
|
def fixed(self, value) -> bool:
|
|
|
|
|
def fixed(self, value: bool) -> bool:
|
|
|
|
|
self._fixed = value
|
|
|
|
|
|
|
|
|
|
def _invalidate(self, set_by_user=False) -> None:
|
|
|
|
|
def _invalidate(self, set_by_user: bool = False) -> None:
|
|
|
|
|
'''Метод для инвалидации данной переменной и всех зависящих от нее
|
|
|
|
|
переменных.'''
|
|
|
|
|
if not self._invalidated and not self.set_by_user:
|
|
|
|
@ -763,7 +772,7 @@ class VariableNode:
|
|
|
|
|
raise VariableError("'{}' variable type is not subscriptable.".
|
|
|
|
|
format(self.variable_type.name))
|
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
|
def __repr__(self) -> str:
|
|
|
|
|
return '<Variable: {} with value: {}>'.format(self.get_fullname(),
|
|
|
|
|
self.value or
|
|
|
|
|
'INVALIDATED')
|
|
|
|
@ -771,7 +780,8 @@ class VariableNode:
|
|
|
|
|
|
|
|
|
|
class NamespaceNode:
|
|
|
|
|
'''Класс ноды соответствующей пространству имен в дереве переменных.'''
|
|
|
|
|
def __init__(self, name='', parent=None):
|
|
|
|
|
def __init__(self, name: str = '',
|
|
|
|
|
parent: Union["NamespaceNode", None] = None):
|
|
|
|
|
self._name = name
|
|
|
|
|
self._variables = dict()
|
|
|
|
|
self._namespaces = dict()
|
|
|
|
@ -787,7 +797,7 @@ class NamespaceNode:
|
|
|
|
|
self._variables.update({variable.name: variable})
|
|
|
|
|
variable.namespace = self
|
|
|
|
|
|
|
|
|
|
def add_namespace(self, namespace) -> None:
|
|
|
|
|
def add_namespace(self, namespace: "NamespaceNode") -> None:
|
|
|
|
|
'''Метод для добавления пространства имен в пространство имен.'''
|
|
|
|
|
if namespace._name in self._variables:
|
|
|
|
|
raise VariableError("variable with the name '{}' is already in"
|
|
|
|
@ -797,7 +807,7 @@ class NamespaceNode:
|
|
|
|
|
self._namespaces.update({namespace._name: namespace})
|
|
|
|
|
namespace._parent = self
|
|
|
|
|
|
|
|
|
|
def clear(self):
|
|
|
|
|
def clear(self) -> None:
|
|
|
|
|
'''Метод для очистки пространства имен. Очищает и пространства имен
|
|
|
|
|
и переменные. Предназначен только для использования в calculate.ini.'''
|
|
|
|
|
for namespace_name in self._namespaces.keys():
|
|
|
|
@ -818,7 +828,7 @@ class NamespaceNode:
|
|
|
|
|
else:
|
|
|
|
|
return self._parent.get_package_name()
|
|
|
|
|
|
|
|
|
|
def __getattr__(self, name: str):
|
|
|
|
|
def __getattr__(self, name: str) -> Any:
|
|
|
|
|
'''Метод возвращает ноду пространства имен или значение переменной.'''
|
|
|
|
|
if name in self._namespaces:
|
|
|
|
|
return self._namespaces[name]
|
|
|
|
@ -849,13 +859,13 @@ class NamespaceNode:
|
|
|
|
|
variable_name=name,
|
|
|
|
|
namespace_name=self._name))
|
|
|
|
|
|
|
|
|
|
def __contains__(self, name):
|
|
|
|
|
def __contains__(self, name: str) -> bool:
|
|
|
|
|
return name in self._namespaces or name in self._variables
|
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
|
def __repr__(self) -> str:
|
|
|
|
|
return '<Namespace: {}>'.format(self.get_fullname())
|
|
|
|
|
|
|
|
|
|
def __deepcopy__(self, memo):
|
|
|
|
|
def __deepcopy__(self, memo: dict) -> "NamespaceNode":
|
|
|
|
|
'''Пространство имен не копируется даже при глубоком копировании.'''
|
|
|
|
|
return self
|
|
|
|
|
|
|
|
|
@ -863,10 +873,11 @@ class NamespaceNode:
|
|
|
|
|
class DependenceAPI(metaclass=Singleton):
|
|
|
|
|
'''Класс образующий интерфейс для создания зависимостей.'''
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self.current_namespace = None
|
|
|
|
|
self.current_namespace: NamespaceNode = None
|
|
|
|
|
self.datavars_root = None
|
|
|
|
|
|
|
|
|
|
def __call__(self, *variables, depend=None):
|
|
|
|
|
def __call__(self, *variables: list,
|
|
|
|
|
depend: Union[Callable, None] = None) -> DependenceSource:
|
|
|
|
|
subscriptions = list()
|
|
|
|
|
for variable in variables:
|
|
|
|
|
if not (isinstance(variable, str) or
|
|
|
|
@ -876,7 +887,9 @@ class DependenceAPI(metaclass=Singleton):
|
|
|
|
|
subscriptions.append(variable)
|
|
|
|
|
return DependenceSource(subscriptions, depend=depend)
|
|
|
|
|
|
|
|
|
|
def _get_variable(self, variable_name, current_namespace=None):
|
|
|
|
|
def _get_variable(self, variable_name: str,
|
|
|
|
|
current_namespace: Union[NamespaceNode, None] = None
|
|
|
|
|
) -> VariableNode:
|
|
|
|
|
'''Метод для поиска переменной в пространствах имен.'''
|
|
|
|
|
if current_namespace is None:
|
|
|
|
|
current_namespace = self.current_namespace
|
|
|
|
@ -884,7 +897,10 @@ class DependenceAPI(metaclass=Singleton):
|
|
|
|
|
current_namespace=current_namespace)
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def find_variable(variable_name, datavars_root, current_namespace=None):
|
|
|
|
|
def find_variable(variable_name: str,
|
|
|
|
|
datavars_root,
|
|
|
|
|
current_namespace: Union[NamespaceNode, None] = None
|
|
|
|
|
) -> VariableNode:
|
|
|
|
|
'''Метод для поиска переменных по строковому пути от корня переменных
|
|
|
|
|
или от текущего пространства имен.'''
|
|
|
|
|
name_parts = variable_name.split('.')
|
|
|
|
@ -912,7 +928,7 @@ class DependenceAPI(metaclass=Singleton):
|
|
|
|
|
class CopyAPI(metaclass=Singleton):
|
|
|
|
|
'''Класс для создания зависимостей представляющих собой простое копирование
|
|
|
|
|
значения переменной в зависимую переменную.'''
|
|
|
|
|
def __call__(self, variable: Union[str, VariableNode]):
|
|
|
|
|
def __call__(self, variable: Union[str, VariableNode]) -> "Dependence":
|
|
|
|
|
return Dependence(variable)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -923,7 +939,7 @@ class FormatAPI(metaclass=Singleton):
|
|
|
|
|
pattern = re.compile(
|
|
|
|
|
r'{\s*([a-zA-Z][a-zA-Z_0-9]+)?(.[a-zA-Z][a-zA-Z_0-9]+)+\s*}')
|
|
|
|
|
|
|
|
|
|
def __call__(self, string: str):
|
|
|
|
|
def __call__(self, string: str) -> "Dependence":
|
|
|
|
|
vars_list = []
|
|
|
|
|
|
|
|
|
|
def subfunc(matchobj):
|
|
|
|
@ -942,7 +958,7 @@ class CalculateAPI(metaclass=Singleton):
|
|
|
|
|
'''Метод для создания зависимостей, представляющих собой функцию для
|
|
|
|
|
вычисления значения зависимой переменной на основе значений указанных
|
|
|
|
|
переменных.'''
|
|
|
|
|
def __call__(self, *args):
|
|
|
|
|
def __call__(self, *args) -> "Dependence":
|
|
|
|
|
depend_function = args[0]
|
|
|
|
|
return Dependence(*args[1:], depend=depend_function)
|
|
|
|
|
|
|
|
|
@ -951,12 +967,17 @@ class VariableAPI(metaclass=Singleton):
|
|
|
|
|
'''Класс для создания переменных при задании их через
|
|
|
|
|
python-скрипты.'''
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self.current_namespace = None
|
|
|
|
|
self.current_namespace: Union[NamespaceNode, None] = None
|
|
|
|
|
# TODO Продумать другой способ обработки ошибок.
|
|
|
|
|
self.errors = []
|
|
|
|
|
|
|
|
|
|
def __call__(self, name: str, source=None, type=VariableType,
|
|
|
|
|
readonly=False, fixed=False, force=False, fields=None):
|
|
|
|
|
self.errors: list = []
|
|
|
|
|
|
|
|
|
|
def __call__(self, name: str,
|
|
|
|
|
source: Any = None,
|
|
|
|
|
type=VariableType,
|
|
|
|
|
readonly: bool = False,
|
|
|
|
|
fixed: bool = False,
|
|
|
|
|
force: bool = False,
|
|
|
|
|
fields: Union[list, None] = None) -> "Dependence":
|
|
|
|
|
'''Метод для создания переменных внутри with Namespace('name').'''
|
|
|
|
|
if name not in self.current_namespace._variables:
|
|
|
|
|
variable = VariableNode(name, self.current_namespace)
|
|
|
|
@ -1011,13 +1032,13 @@ class NamespaceAPI(metaclass=Singleton):
|
|
|
|
|
можно получить доступ к переменным.'''
|
|
|
|
|
return self._datavars
|
|
|
|
|
|
|
|
|
|
def set_datavars(self, datavars):
|
|
|
|
|
def set_datavars(self, datavars) -> None:
|
|
|
|
|
'''Метод для установки корневого пространства имен, которое пока что
|
|
|
|
|
будет использоваться для предоставления доступа к переменным.'''
|
|
|
|
|
self._datavars = datavars
|
|
|
|
|
self._variables_fabric.current_namespace = self._datavars
|
|
|
|
|
|
|
|
|
|
def reset(self):
|
|
|
|
|
def reset(self) -> None:
|
|
|
|
|
'''Метод для сброса корневого пространства имен.'''
|
|
|
|
|
if isinstance(self._datavars, NamespaceNode):
|
|
|
|
|
self._datavars = NamespaceNode('<root>')
|
|
|
|
@ -1028,7 +1049,7 @@ class NamespaceAPI(metaclass=Singleton):
|
|
|
|
|
self._variables_fabric.current_namespace = self._datavars
|
|
|
|
|
self._dependence_fabric.current_namespace = self._datavars
|
|
|
|
|
|
|
|
|
|
def set_current_namespace(self, namespace: NamespaceNode):
|
|
|
|
|
def set_current_namespace(self, namespace: NamespaceNode) -> None:
|
|
|
|
|
'''Метод для установки текущего пространства имен, в которое будут
|
|
|
|
|
добавляться далее переменные и пространства имен.'''
|
|
|
|
|
self.current_namespace = namespace
|
|
|
|
@ -1036,7 +1057,7 @@ class NamespaceAPI(metaclass=Singleton):
|
|
|
|
|
self._dependence_fabric.current_namespace = namespace
|
|
|
|
|
|
|
|
|
|
@contextmanager
|
|
|
|
|
def __call__(self, namespace_name):
|
|
|
|
|
def __call__(self, namespace_name: str):
|
|
|
|
|
'''Метод для создания пространств имен с помощью with.'''
|
|
|
|
|
if namespace_name not in self.current_namespace._namespaces:
|
|
|
|
|
namespace = NamespaceNode(namespace_name,
|
|
|
|
|