Implemented dynamic subscribing for variables of the table and hash types.

packages
Иванов Денис 4 years ago
parent 0a5776ff85
commit 91d3f4f3cb

@ -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]

@ -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

Loading…
Cancel
Save