|
|
import re
|
|
|
|
|
|
|
|
|
class VariableType:
|
|
|
'''Базовый класс для типов.'''
|
|
|
@classmethod
|
|
|
def check(self, value: str) -> bool:
|
|
|
return True
|
|
|
|
|
|
|
|
|
class VariableError(Exception):
|
|
|
pass
|
|
|
|
|
|
|
|
|
class VariableTypeError(Exception):
|
|
|
pass
|
|
|
|
|
|
|
|
|
class StringType(VariableType):
|
|
|
pass
|
|
|
|
|
|
|
|
|
class IntegerType(VariableType):
|
|
|
integer_pattern = re.compile(r"^-?\d+$")
|
|
|
|
|
|
@classmethod
|
|
|
def check(self, value: str) -> bool:
|
|
|
return self.integer_pattern.match(value)
|
|
|
|
|
|
|
|
|
class FloatType(VariableType):
|
|
|
float_pattern = re.compile(r"^-?\d+(\.\d+)?$")
|
|
|
|
|
|
@classmethod
|
|
|
def check(self, value: str) -> bool:
|
|
|
return self.integer_pattern.match(value)
|
|
|
|
|
|
|
|
|
class BooleanType(VariableType):
|
|
|
pass
|
|
|
|
|
|
|
|
|
class ListType(VariableType):
|
|
|
pass
|
|
|
|
|
|
|
|
|
class ReadonlyType(VariableType):
|
|
|
pass
|
|
|
|
|
|
|
|
|
class VariableNode:
|
|
|
def __init__(self, name, namespace, variable_type=StringType):
|
|
|
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
|
|
|
self.value = None
|
|
|
|
|
|
self.subscribers = set()
|
|
|
self.subscriptions = tuple()
|
|
|
|
|
|
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
|
|
|
elif self.subscriptions:
|
|
|
# Пока что будем полагать, что в такой ситуации берем значение из
|
|
|
# последней подписки.
|
|
|
self.value = self.subscriptions[-1]
|
|
|
else:
|
|
|
self.value = value
|
|
|
|
|
|
@property
|
|
|
def fullname(self) -> str:
|
|
|
if self.namespace:
|
|
|
return "{}.{}".format(self.namespace.fullname, self.name)
|
|
|
else:
|
|
|
return self.name
|
|
|
|
|
|
def __repr__(self):
|
|
|
return '<Variable: {} with value: {}>'.format(self.fullname,
|
|
|
self.value or
|
|
|
'INVALIDATED')
|
|
|
|
|
|
|
|
|
class NamespaceNode:
|
|
|
def __init__(self, name='', parent=None):
|
|
|
self.name = name
|
|
|
self.variables = dict()
|
|
|
self.namespaces = dict()
|
|
|
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
|
|
|
|
|
|
@property
|
|
|
def fullname(self) -> str:
|
|
|
if self.parent is not None:
|
|
|
return '{}.{}'.format(self.parent.fullname, self.name)
|
|
|
else:
|
|
|
return self.name
|
|
|
|
|
|
def get_variable_node(self, name):
|
|
|
if name in self.variables:
|
|
|
return self.variables[name]
|
|
|
else:
|
|
|
raise VariableError("Variable '{variable_name}' is not found in"
|
|
|
" the namespace '{namespace_name}'".format(
|
|
|
variable_name=name,
|
|
|
namespace_name=self.name))
|
|
|
|
|
|
def __getattr__(self, name: str):
|
|
|
if name in self.namespaces:
|
|
|
return self.namespaces[name]
|
|
|
elif name in self.variables:
|
|
|
return self.variables[name].value
|
|
|
else:
|
|
|
raise VariableError("'{variable_name}' is not found in the"
|
|
|
" namespace '{namespace_name}'".format(
|
|
|
variable_name=name,
|
|
|
namespace_name=self.name))
|
|
|
|
|
|
def __repr__(self):
|
|
|
return '<Namespace: {}>'.format(self.fullname)
|