Works with errors. Some changes to the dependencies and interface architecture and errors processing.

packages
Иванов Денис 4 years ago
parent c8517ec59c
commit d7048baecd

@ -78,11 +78,12 @@ class ReadonlyType(VariableType):
pass
class Dependence:
class DependenceSource:
current_namespace = None
datavars_root = None
def __init__(self, *variables, depend=None):
def __init__(self, variables, depend=None):
self.error = None
self._subscriptions = variables
self.depend_function = depend
@ -138,43 +139,20 @@ class Dependence:
# Проверяем совпадение количества аргументов функции и заданных для
# функции переменных.
function_signature = signature(function_to_check)
print('subscribers: {}'.format(self._subscriptions))
if not len(self._subscriptions) == len(function_signature.parameters):
raise VariableError("the depend function takes {} arguments,"
" while {} is given".format(
len(function_signature.parameters),
len(self._subscriptions)))
def _get_variables(self, variables_names):
print('current_namespace: {}'.format(self.current_namespace.fullname))
print('root:')
for namespace_name, namespace in self.datavars_root:
print(namespace_name)
variables = list()
for variable_name in variables_names:
result = None
name_parts = variable_name.split('.')
if not name_parts[0]:
namespace = self.current_namespace
index = 1
while not name_parts[index]:
namespace = namespace.parent
index += 1
name_parts = name_parts[index:]
else:
namespace = self.datavars_root
result = namespace
for part in name_parts:
result = result[part]
variables.append(result)
return variables
def __ne__(self, other):
if not isinstance(other, DependenceSource):
return True
return not self == other
def __eq__(self, other):
if not isinstance(other, Dependence):
if not isinstance(other, DependenceSource):
return False
if not self._compare_depend_functions(self.depend_function,
@ -205,6 +183,7 @@ class Dependence:
class VariableNode:
'''Класс ноды соответствующей переменной в дереве переменных.'''
def __init__(self, name, namespace, variable_type=StringType, source=None):
self.name = name
if issubclass(variable_type, VariableType):
@ -228,6 +207,8 @@ class VariableNode:
self.update_value()
def update_value(self):
'''Метод для обновления значения переменной с помощью указанного
источника ее значения.'''
if self.calculating:
raise CyclicVariableError(self.name)
@ -235,7 +216,7 @@ class VariableNode:
raise VariableError("No sources to update variable '{}'".
format(self.fullname))
if isinstance(self._source, Dependence):
if isinstance(self._source, DependenceSource):
with self._start_calculate():
try:
self.value = self._source.calculate_value()
@ -253,7 +234,7 @@ class VariableNode:
if self._source != source:
self._invalidate()
self._source = source
if isinstance(source, Dependence):
if isinstance(source, DependenceSource):
for subscription in source._subscriptions:
subscription.subscribers.add(self)
@ -293,6 +274,7 @@ class VariableNode:
class NamespaceNode:
'''Класс ноды соответствующей пространству имен в дереве переменных.'''
def __init__(self, name='', parent=None):
self.name = name
self.variables = dict()
@ -300,10 +282,12 @@ class NamespaceNode:
self.parent = parent
def add_variable(self, variable: VariableNode):
'''Метод для добавления переменной в пространство имен.'''
self.variables.update({variable.name: variable})
variable.namespace = self
def add_namespace(self, namespace):
'''Метод для добавления пространства имен в пространство имен.'''
self.namespaces.update({namespace.name: namespace})
namespace.parent = self
@ -339,12 +323,54 @@ class NamespaceNode:
namespace_name=self.name))
def __contains__(self, name):
return name in self.childs or name in self.variables
return name in self.namespaces or name in self.variables
def __repr__(self):
return '<Namespace: {}>'.format(self.fullname)
class DependenceAPI:
def __init__(self):
self.current_namespace = None
self.datavars_root = None
def __call__(self, *variables, depend=None):
subscriptions = list()
for variable in variables:
if isinstance(variable, str):
variable = self._find_variable(variable)
if variable is None:
raise DependenceError("variable '{}' not found".format(
variable))
elif not isinstance(variable, VariableNode):
raise DependenceError("dependence variables must be 'str' or"
" 'VariableNode' not '{}'".format(
type(variable)))
subscriptions.append(variable)
return DependenceSource(subscriptions, depend=depend)
def _find_variable(self, variable_name):
'''Метод для поиска переменной в пространстве'''
variable = None
name_parts = variable_name.split('.')
if not name_parts[0]:
namespace = self.current_namespace
index = 1
while not name_parts[index]:
namespace = namespace.parent
index += 1
name_parts = name_parts[index:]
else:
namespace = self.datavars_root
variable = namespace
for part in name_parts:
variable = variable[part]
return variable
class VariableAPI:
'''Класс для создания переменных при задании их через
python-скрипты.'''
@ -360,15 +386,16 @@ class VariableAPI:
else:
variable = self.current_namespace[name]
if isinstance(source, Dependence):
if isinstance(source, DependenceSource):
try:
source.check()
variable.source = source
except DependenceError as error:
self.errors.append('Dependence error: {} in variable: {}'.
format(str(error), variable.fullname))
raise VariableError('Dependence error: {} in variable: {}'.
format(str(error), variable.fullname))
else:
variable.source = source
return variable
@ -376,14 +403,19 @@ class NamespaceAPI:
'''Класс для создания пространств имен при задании переменных через
python-скрипты.'''
def __init__(self, var_fabric: VariableAPI,
dependence_fabric: DependenceAPI(),
datavars_root=NamespaceNode('<root>')):
self._datavars = datavars_root
self._variables_fabric = var_fabric
self.namespace_stack = [datavars_root]
# Привязываем фабрику переменных.
self._variables_fabric = var_fabric
self._variables_fabric.current_namespace = self._datavars
Dependence.current_namespace = self._datavars
Dependence.datavars_root = self._datavars
# Привязываем фабрику зависимостей.
self._dependence_fabric = dependence_fabric
self._dependence_fabric.current_namespace = self._datavars
self._dependence_fabric.datavars_root = self._datavars
@property
def datavars(self):
@ -402,9 +434,9 @@ class NamespaceAPI:
'''Метод для сброса корневого пространства имен.'''
self._datavars = NamespaceNode('<root>')
self.namespace_stack = [self._datavars]
self._variables_fabric.current_namespace = self._datavars
Dependence.current_namespace = self._datavars
self._variables_fabric.current_namespace = self._datavars
self._dependence_fabric.current_namespace = self._datavars
@contextmanager
def __call__(self, namespace_name):
@ -417,17 +449,21 @@ class NamespaceAPI:
self.namespace_stack[-1].add_namespace(namespace)
self.namespace_stack.append(namespace)
# Устанавливаем текущее пространство имен фабрике переменных.
self._variables_fabric.current_namespace = namespace
Dependence.datavars_root = self._datavars
Dependence.current_namespace = namespace
# Устанавливаем текущее пространство имен фабрике зависимостей.
self._dependence_fabric.current_namespace = namespace
print('current_namespace in DependenceAPI: {}'.format(
namespace.fullname))
try:
yield self
finally:
current_namespace = self.namespace_stack.pop()
self._variables_fabric.current_namespace = current_namespace
Dependence.current_namespace = current_namespace
self._dependence_fabric.current_namespace = current_namespace
Dependence = DependenceAPI()
Variable = VariableAPI()
Namespace = NamespaceAPI(Variable)
Namespace = NamespaceAPI(Variable, Dependence)

@ -51,6 +51,28 @@ class TestNamespace:
assert namespace_1['var_1'].fullname == 'namespace_1.var_1'
assert namespace_1['var_2'].fullname == 'namespace_1.var_2'
def test_find_vars(self):
Namespace.reset()
datavars = Namespace.datavars
var_1 = Variable('var', source='null')
with Namespace('namespace_1'):
Variable('var_1', source='val_1')
Variable('var_2', source=2)
Variable('var_3', source=4)
with Namespace('namespace_1_1'):
Variable('var_1', source='val_1')
assert Dependence(var_1)._find_variable('.var_1') ==\
datavars.namespace_1.namespace_1_1['var_1']
assert Dependence(var_1)._find_variable('..var_3') ==\
datavars.namespace_1['var_3']
with Namespace('namespace_2'):
assert Dependence(var_1)._find_variable('namespace_1.var_2') ==\
datavars.namespace_1['var_2']
def test_compare_two_dependencies_equal(self):
namespace_1 = NamespaceNode('namespace_1')
variable_1 = VariableNode('var_1', namespace_1, source=2)
@ -137,11 +159,11 @@ class TestNamespace:
var_1 = Variable('var_2', source=2)
var_2 = Variable('var_3', source=4)
Variable('var_3', source=4)
with Namespace('namespace_2'):
Variable('var_1', source=Dependence(
var_1, var_2,
var_1, '.var_2',
depend=lambda arg_1, arg_2:
'greater' if arg_1 > arg_2 else 'less'))
with Namespace('namespace_2_1'):
@ -171,7 +193,7 @@ class TestNamespace:
assert datavars.namespace_1.var_2 == 'from var_1: value_1'
with Namespace('namespace_1'):
var_1 = Variable('var_1', source='value_2')
Variable('var_1', source='value_2')
assert datavars.namespace_1.var_1 == 'value_2'
assert datavars.namespace_1.var_2 == 'from var_1: value_2'
@ -181,17 +203,17 @@ class TestNamespace:
datavars = Namespace.datavars
with Namespace('namespace_1'):
var_1 = Variable('var_1', source='value_1')
Variable('var_2', source=Dependence(var_1, depend=lambda arg_1:
Variable('var_1', source='value_1')
Variable('var_2', source=Dependence('.var_1', depend=lambda arg_1:
'from var_1: {}'.
format(arg_1)))
var_3 = Variable('var_3', source='value_3')
Variable('var_3', source='value_3')
assert datavars.namespace_1.var_1 == 'value_1'
assert datavars.namespace_1.var_2 == 'from var_1: value_1'
with Namespace('namespace_1'):
Variable('var_2', source=Dependence(var_3, depend=lambda arg_1:
Variable('var_2', source=Dependence('.var_3', depend=lambda arg_1:
'from var_3: {}'.
format(arg_1)))
@ -202,47 +224,19 @@ class TestNamespace:
datavars = Namespace.datavars
with Namespace('namespace_1'):
var_1 = Variable('var_1', source='value_1')
var_2 = Variable('var_2',
source=Dependence(var_1,
Variable('var_1', source='value_1')
Variable('var_2', source=Dependence(
'namespace_1.var_1',
depend=lambda arg_1:
'from var_1: {}'.format(arg_1)))
def comparator(arg_1):
return 'from var_2: {}'.format(arg_1)
var_3 = Variable('var_3',
source=Dependence(var_2, depend=comparator))
Variable('var_3', source=Dependence('.var_2', depend=comparator))
Variable('var_1',
source=Dependence(var_3,
source=Dependence('.var_3',
depend=lambda arg_1:
'from var_3: {}'.format(arg_1)))
with pytest.raises(CyclicVariableError):
datavars.namespace_1.var_3
def test_find_vars(self):
Namespace.reset()
datavars = Namespace.datavars
var_1 = Variable('var', source='null')
with Namespace('namespace_1'):
Variable('var_1', source='val_1')
Variable('var_2', source=2)
Variable('var_3', source=4)
with Namespace('namespace_1_1'):
Variable('var_1', source='val_1')
for var in Dependence(var_1)._get_variables(['.var_1',
'..var_3']):
print(var.fullname)
with Namespace('namespace_2'):
print('current_datavars:')
for namespace_name, namespace in datavars.namespaces.items():
print(namespace_name)
for var in Dependence(var_1)._get_variables(['namespace_1.var_2']):
print(var.fullname)
assert False

Loading…
Cancel
Save