diff --git a/calculate/vars/alt_datavars.py b/calculate/vars/alt_datavars.py index 831ea0d..42d9fa8 100644 --- a/calculate/vars/alt_datavars.py +++ b/calculate/vars/alt_datavars.py @@ -1,6 +1,6 @@ import ast import dis -from typing import List, Union +from typing import List, Union, Any from contextlib import contextmanager from inspect import signature, getsource from types import FunctionType, LambdaType @@ -160,12 +160,10 @@ class HashValue: def subscribers(self): return self.master_variable.subscribers - def get_value(self): - if self.master_variable.variable_type is HashType: - self = self.master_variable.get_value()[self.key] - elif self.master_variable.variable_type is TableType: - self = self.master_variable.get_value()[ - self.parent.row_index][self.key] + def get_value(self) -> str: + '''Метод для получения значения хэша. Перед возвращением значения + обновляет себя на наиболее актуальную версию значения хэша.''' + self = self.master_variable.get_value()[self.key] return self.value @@ -180,6 +178,12 @@ class Hash: self.master_variable = master_variable self.row_index = None + def get_hash(self) -> dict: + dict_value = {} + for key in self._values.keys(): + dict_value.update({key: self._values[key].get_value()}) + return dict_value + def __getattr__(self, key: str): '''Метод возвращает ноду пространства имен или значение переменной.''' if key in self._values: @@ -222,8 +226,7 @@ class HashType(VariableType): class Table: '''Класс, соответствующий типу переменных таблиц.''' - def __init__(self, values: Union[List[Hash], List[dict]], - master_variable, fields=None): + def __init__(self, values: List[dict], master_variable, fields=None): self._rows = list() self.master_variable = master_variable @@ -231,21 +234,20 @@ class Table: if fields is not None: self.columns.update(self.fields) else: - self.columns = set(values[0]) + self.columns = set(values[0].keys()) for row in values: - if isinstance(row, Hash): + if isinstance(row, dict): self._check_columns(row) self._rows.append(row) - elif isinstance(row, dict): - self._check_columns(row) - row_hash = Hash(row, master_variable) - self._rows.append(row_hash) else: raise VariableTypeError("can not create table using value '{}'" " with type '{}'".format(row, type(row))) + def get_table(self): + return self._rows + def _check_columns(self, value: Union[List[Hash], List[dict]]) -> None: '''Метод для проверки наличия в хэше только тех полей, которые соответствуют заданным для таблицы колонкам.''' @@ -280,8 +282,7 @@ class TableType(VariableType): name = 'table' @classmethod - def process_value(self, value: Union[Table, List[dict], List[Hash]], - variable) -> Table: + def process_value(self, value: List[dict], variable) -> Table: print('PROCESS TABLE') if not isinstance(value, list) and not isinstance(value, Table): raise VariableTypeError("can not set value with type '{_type}' to" @@ -294,14 +295,22 @@ class TableType(VariableType): class VariableWrapper: + '''Класс обертки для переменных, с помощью которого отслеживается + применение переменной в образовании значения переменной от нее зависящей. + ''' def __init__(self, variable, subscriptions): self._variable = variable self._subscriptions = subscriptions @property def value(self): + '''Метод возвращающий значение переменной и при этом добавляющий его в + подписки.''' self._subscriptions.add(self._variable) - return self._variable.get_value() + value = self._variable.get_value() + if isinstance(value, Hash): + value = value.get_hash() + return value @property def subscriptions(self): @@ -313,9 +322,7 @@ class VariableWrapper: class DependenceSource: - current_namespace = None - datavars_root = None - + '''Класс зависимости как источника значения переменной.''' def __init__(self, variables, depend=None): self.error = None self._args = variables @@ -335,7 +342,7 @@ class DependenceSource: else: self._check_function(self.depend_function) - def calculate_value(self): + def calculate_value(self) -> Any: '''Метод для расчета значения переменной с использованием зависимостей и заданной для них функции.''' self._subscriptions = set() @@ -435,6 +442,8 @@ class VariableNode: self.namespace.add_variable(self) self.subscribers = set() + # Список текущих подписок, для проверки их актуальности при + # динамическом связывании. self._subscriptions = set() # Если значение переменной None -- значит она обнулена. @@ -446,6 +455,10 @@ class VariableNode: if source is not None: self.update_value() + # Флаг, указывающий, что значение было изменено в процессе работы + # утилит или с помощью тега set из шаблона. + self.set_by_user = False + self._readonly = False def update_value(self) -> None: @@ -463,10 +476,12 @@ class VariableNode: with self._start_calculate(): try: value = self._source.calculate_value() - # Обновляем подписки + # Обновляем подписки. Сначала убираем лишние. for subscription in self._subscriptions: if subscription not in self._source.subscriptions: subscription.subscribers.remove(self) + + # Теперь добавляем новые. for subscription in self._source.subscriptions: subscription.subscribers.add(self) self._subscriptions = self._source.subscriptions @@ -521,7 +536,7 @@ class VariableNode: finally: self.calculating = False - def get_value(self): + def get_value(self) -> Any: '''Метод для получения значения переменной.''' if self.value is None: self.update_value() @@ -559,12 +574,12 @@ class NamespaceNode: self.namespaces = dict() self.parent = parent - def add_variable(self, variable: VariableNode): + def add_variable(self, variable: VariableNode) -> None: '''Метод для добавления переменной в пространство имен.''' self.variables.update({variable.name: variable}) variable.namespace = self - def add_namespace(self, namespace): + def add_namespace(self, namespace) -> None: '''Метод для добавления пространства имен в пространство имен.''' self.namespaces.update({namespace.name: namespace}) namespace.parent = self @@ -576,7 +591,7 @@ class NamespaceNode: else: return self.name - def __getattr__(self, name: str): + def __getattr__(self, name: str) -> None: '''Метод возвращает ноду пространства имен или значение переменной.''' if name in self.namespaces: return self.namespaces[name] @@ -588,7 +603,7 @@ class NamespaceNode: variable_name=name, namespace_name=self.name)) - def __getitem__(self, name: str): + def __getitem__(self, name: str) -> None: '''Метод возвращает ноду пространства имен или ноду переменной.''' if name in self.namespaces: return self.namespaces[name] diff --git a/tests/vars/test_alt_vars.py b/tests/vars/test_alt_vars.py index 077cd2e..7e9fc33 100644 --- a/tests/vars/test_alt_vars.py +++ b/tests/vars/test_alt_vars.py @@ -280,6 +280,23 @@ class TestNamespace: assert datavars.namespace_1.var_4.key_2 == 'other_value' assert datavars.namespace_1.var_4.key_3 == 'another_value' + def test_getting_all_hash_to_depend_function(self): + Namespace.reset() + datavars = Namespace.datavars + + with Namespace('namespace_1'): + Variable('var_1', source='value_1', type=StringType) + Variable('var_2', type=HashType, source={'key_1': 'value_1', + 'key_2': 'value_2'}) + Variable('var_3', source='value_3', type=StringType) + + def depend_hash(arg_1): + return arg_1.value['key_1'] + Variable('var_4', type=StringType, + source=Dependence('.var_2', depend=depend_hash)) + + assert datavars.namespace_1.var_4 == 'value_1' + def test_readonly(self): Namespace.reset() datavars = Namespace.datavars