Added variables invalidation, subscription

packages
Иванов Денис 4 years ago
parent df9e70a0d8
commit 653f5064ff

@ -1,4 +1,7 @@
import re
import ast
from inspect import signature, getsource
from types import FunctionType, LambdaType
class VariableType:
@ -49,38 +52,87 @@ class ReadonlyType(VariableType):
class VariableNode:
def __init__(self, name, namespace, variable_type=StringType):
def __init__(self, name, namespace, variable_type=StringType, value=None):
self.name = name
if issubclass(variable_type, VariableType):
self.variable_type = variable_type
else:
raise VariableTypeError('variable_type must be VariableType'
', but not {}'.format(type(variable_type)))
self.namespace = namespace
self.set_function = None
# Если значение переменной None -- значит она обнулена.
self.value = None
self.subscribers = set()
self.subscriptions = tuple()
if value is not None:
self.update_value(value)
self.namespace = namespace
namespace.add_variable(self)
def update_value(self, value=None):
if self.set_function is not None:
if self.subscriptions:
self.value = self.set_function(*(subscription.value for
subscription in
self.subscriptions))
elif value is not None:
self.value = self.set_function(value)
else:
# Пока что полагаем ее обнуленной в этой ситуации.
# TODO Обдумать поведение.
self.value = None
self._invalidate()
if value is not None:
self.value = value
elif self.set_function is not None and self.subscriptions:
self.value = self.set_function(*(subscription.value for
subscription in
self.subscriptions))
elif self.subscriptions:
# Пока что будем полагать, что в такой ситуации берем значение из
# последней подписки.
self.value = self.subscriptions[-1]
self.value = self.subscriptions[-1].get_value()
else:
self.value = value
raise VariableError("No sources to update variable '{}'".
format(self.fullname))
def _invalidate(self):
self.value = None
for subscriber in self.subscribers:
subscriber._invalidate()
def get_value(self):
if self.value is None:
self.update_value()
return self.value
def subscribe(self, *variables, set_function=None):
self.subscriptions = variables
for subscription in self.subscriptions:
subscription.subscribers.add(self)
self._check_function(set_function)
self.set_function = set_function
def subscribe(self, *variables, set_function=None):
def _check_function(self, function_to_check: FunctionType):
'''Метод для проверки того, возращает ли функция какое-либо значение, а
также того, совпадает ли число подписок с числом аргументов этой
функции.'''
if not isinstance(function_to_check, LambdaType):
# Если функция не лямбда, проверяем есть ли у нее возвращаемое
# значение.
for node in ast.walk(ast.parse(getsource(function_to_check))):
if isinstance(node, ast.Return):
break
else:
raise VariableError("Variable set function does not return"
" anything in variable '{}'.".format(
self.fullname))
# Проверяем совпадение количества аргументов функции и заданных для
# функции переменных.
function_signature = signature(function_to_check)
if not len(self.subscriptions) == len(function_signature.parameters):
raise VariableError("Variable set function takes {} arguments,"
" while {} is given for in variable '{}'."
.format(len(function_signature.parameters),
len(self.subscriptions),
self.fullname))
@property
def fullname(self) -> str:
@ -130,7 +182,7 @@ class NamespaceNode:
if name in self.namespaces:
return self.namespaces[name]
elif name in self.variables:
return self.variables[name].value
return self.variables[name].get_value()
else:
raise VariableError("'{variable_name}' is not found in the"
" namespace '{namespace_name}'".format(

@ -29,13 +29,8 @@ class TestNamespace:
def test_if_two_VariableNode_objects_are_initialized_and_added_to_a_namespace__the_NamespaceNode_object_contains_this_variables_and_can_be_used_to_get_variables_values_and_variables_have_namespace_name_in_their_fullnames(self):
namespace_1 = NamespaceNode('namespace_1')
variable_1 = VariableNode('var_1', namespace_1)
variable_1.update_value(value='value_1')
variable_2 = VariableNode('var_2', namespace_1)
variable_2.update_value(value='value_2')
namespace_1.add_variable(variable_1)
namespace_1.add_variable(variable_2)
variable_1 = VariableNode('var_1', namespace_1, value='value_1')
variable_2 = VariableNode('var_2', namespace_1, value='value_2')
assert namespace_1.namespaces == dict()
assert namespace_1.variables == {'var_1': variable_1,
@ -49,5 +44,18 @@ class TestNamespace:
assert namespace_1.get_variable_node('var_2').fullname ==\
'namespace_1.var_2'
def test_updating_variable_using_set_function(self):
pass
def test_if_a_variable_subscribed_to_two_other_variables_using_set_function__the_(self):
namespace_1 = NamespaceNode('namespace_1')
variable_1 = VariableNode('var_1', namespace_1, value=2)
variable_2 = VariableNode('var_2', namespace_1, value=4)
namespace_2 = NamespaceNode('namespace_2')
variable = VariableNode('var_1', namespace_2)
variable.subscribe(variable_1, variable_2,
set_function=lambda var_1, var_2:
'greater' if var_1 > var_2 else 'less')
assert namespace_2.var_1 == 'less'
namespace_1.get_variable_node('var_1').update_value(5)
assert namespace_2.var_1 == 'greater'

Loading…
Cancel
Save