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