started work on backend
This commit is contained in:
@@ -0,0 +1,812 @@
|
||||
from __future__ import nested_scopes, generators, division, absolute_import, with_statement, \
|
||||
print_function, unicode_literals
|
||||
from .utilities.compatibility import backport
|
||||
|
||||
backport() # noqa
|
||||
|
||||
from future.utils import native_str
|
||||
|
||||
import numbers
|
||||
import operator
|
||||
import re
|
||||
import collections
|
||||
from collections import OrderedDict
|
||||
from copy import copy, deepcopy
|
||||
from itertools import chain
|
||||
from numbers import Number
|
||||
|
||||
try:
|
||||
from typing import Optional, Dict, Sequence, Tuple, Mapping, Union, Any, List
|
||||
except ImportError:
|
||||
Optional = Sequence = Dict = Tuple = Mapping = Union = Any = List = None
|
||||
|
||||
import serial
|
||||
from serial.utilities import qualified_name, properties_values, collections_abc
|
||||
from serial.abc.model import Model
|
||||
from serial.abc.properties import Property
|
||||
|
||||
_DOT_SYNTAX_RE = re.compile(
|
||||
r'^\d+(\.\d+)*$'
|
||||
)
|
||||
|
||||
|
||||
class Meta(object):
|
||||
|
||||
def __copy__(self):
|
||||
new_instance = self.__class__()
|
||||
for a in dir(self):
|
||||
if a[0] != '_':
|
||||
v = getattr(self, a)
|
||||
if not isinstance(v, collections.Callable):
|
||||
setattr(new_instance, a, v)
|
||||
return new_instance
|
||||
|
||||
def __deepcopy__(self, memo=None):
|
||||
# type: (Optional[dict]) -> Meta
|
||||
new_instance = self.__class__()
|
||||
for a, v in properties_values(self):
|
||||
setattr(new_instance, a, deepcopy(v, memo=memo))
|
||||
return new_instance
|
||||
|
||||
def __bool__(self):
|
||||
return True
|
||||
|
||||
def __repr__(self):
|
||||
return ('\n'.join(
|
||||
['%s(' % qualified_name(type(self))] +
|
||||
[
|
||||
' %s=%s,' % (p, repr(v))
|
||||
for p, v in properties_values(self)
|
||||
] +
|
||||
[')']
|
||||
))
|
||||
|
||||
|
||||
class Version(Meta):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
version_number=None, # type: Optional[str]
|
||||
specification=None, # type: Optional[Sequence[str]]
|
||||
equals=None, # type: Optional[Sequence[Union[str, Number]]]
|
||||
not_equals=None, # type: Optional[Sequence[Union[str, Number]]]
|
||||
less_than=None, # type: Optional[Sequence[Union[str, Number]]]
|
||||
less_than_or_equal_to=None, # type: Optional[Sequence[Union[str, Number]]]
|
||||
greater_than=None, # type: Optional[Sequence[Union[str, Number]]]
|
||||
greater_than_or_equal_to=None, # type: Optional[Sequence[Union[str, Number]]]
|
||||
):
|
||||
if isinstance(version_number, str) and (
|
||||
(specification is None) and
|
||||
(equals is None) and
|
||||
(not_equals is None) and
|
||||
(less_than is None) and
|
||||
(less_than_or_equal_to is None) and
|
||||
(greater_than is None) and
|
||||
(greater_than_or_equal_to is None)
|
||||
):
|
||||
specification = None
|
||||
for s in version_number.split('&'):
|
||||
if '==' in s:
|
||||
s, equals = s.split('==')
|
||||
elif '<=' in s:
|
||||
s, less_than_or_equal_to = s.split('<=')
|
||||
elif '>=' in s:
|
||||
s, greater_than_or_equal_to = s.split('>=')
|
||||
elif '<' in s:
|
||||
s, less_than = s.split('<')
|
||||
elif '>' in s:
|
||||
s, greater_than = s.split('>')
|
||||
elif '!=' in s:
|
||||
s, not_equals = s.split('!=')
|
||||
elif '=' in s:
|
||||
s, equals = s.split('=')
|
||||
if specification:
|
||||
if s != specification:
|
||||
raise ValueError(
|
||||
'Multiple specifications cannot be associated with an instance of ' +
|
||||
'`serial.meta.Version`: ' + repr(version_number)
|
||||
)
|
||||
elif s:
|
||||
specification = s
|
||||
self.specification = specification
|
||||
self.equals = equals
|
||||
self.not_equals = not_equals
|
||||
self.less_than = less_than
|
||||
self.less_than_or_equal_to = less_than_or_equal_to
|
||||
self.greater_than = greater_than
|
||||
self.greater_than_or_equal_to = greater_than_or_equal_to
|
||||
|
||||
def __eq__(self, other):
|
||||
# type: (Any) -> bool
|
||||
|
||||
compare_properties_functions = (
|
||||
('equals', operator.eq),
|
||||
('not_equals', operator.ne),
|
||||
('less_than', operator.lt),
|
||||
('less_than_or_equal_to', operator.le),
|
||||
('greater_than', operator.gt),
|
||||
('greater_than_or_equal_to', operator.ge),
|
||||
)
|
||||
|
||||
if (
|
||||
(isinstance(other, str) and _DOT_SYNTAX_RE.match(other)) or
|
||||
isinstance(other, (collections_abc.Sequence, int))
|
||||
):
|
||||
if isinstance(other, (native_str, bytes, numbers.Number)):
|
||||
other = str(other)
|
||||
if isinstance(other, str):
|
||||
|
||||
other = other.rstrip('.0')
|
||||
if other == '':
|
||||
other_components = (0,)
|
||||
else:
|
||||
other_components = tuple(int(other_component) for other_component in other.split('.'))
|
||||
else:
|
||||
other_components = tuple(other)
|
||||
for compare_property, compare_function in compare_properties_functions:
|
||||
compare_value = getattr(self, compare_property)
|
||||
if compare_value is not None:
|
||||
compare_values = tuple(int(n) for n in compare_value.split('.'))
|
||||
other_values = copy(other_components)
|
||||
ld = len(other_values) - len(compare_values)
|
||||
if ld < 0:
|
||||
other_values = tuple(chain(other_values, [0] * (-ld)))
|
||||
elif ld > 0:
|
||||
compare_values = tuple(chain(compare_values, [0] * ld))
|
||||
if not compare_function(other_values, compare_values):
|
||||
return False
|
||||
else:
|
||||
for compare_property, compare_function in compare_properties_functions:
|
||||
compare_value = getattr(self, compare_property)
|
||||
if (compare_value is not None) and not compare_function(other, compare_value):
|
||||
return False
|
||||
return True
|
||||
|
||||
def __str__(self):
|
||||
representation = []
|
||||
for property, operator in (
|
||||
('equals', '=='),
|
||||
('not_equals', '!='),
|
||||
('greater_than', '>'),
|
||||
('greater_than_or_equal_to', '>='),
|
||||
('less_than', '<'),
|
||||
('less_than_or_equal_to', '<='),
|
||||
):
|
||||
v = getattr(self, property)
|
||||
if v is not None:
|
||||
representation.append(
|
||||
self.specification + operator + v
|
||||
)
|
||||
return '&'.join(representation)
|
||||
|
||||
|
||||
class Object(Meta):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
properties=None, # type: Optional[Properties]
|
||||
):
|
||||
self._properties = None # type: Optional[Properties]
|
||||
self.properties = properties
|
||||
|
||||
@property
|
||||
def properties(self):
|
||||
# type: () -> Optional[Properties]
|
||||
return self._properties
|
||||
|
||||
@properties.setter
|
||||
def properties(
|
||||
self,
|
||||
properties_
|
||||
# type: Optional[Union[Dict[str, Property], Sequence[Tuple[str, Property]]]]
|
||||
):
|
||||
# type: (...) -> None
|
||||
self._properties = Properties(properties_)
|
||||
|
||||
|
||||
class Dictionary(Meta):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
value_types=None, # type: Optional[Sequence[Property, type]]
|
||||
):
|
||||
self._value_types = None # type: Optional[Tuple]
|
||||
self.value_types = value_types
|
||||
|
||||
@property
|
||||
def value_types(self):
|
||||
# type: () -> Optional[Dict[str, Union[type, Property, serial.model.Object]]]
|
||||
return self._value_types
|
||||
|
||||
@value_types.setter
|
||||
def value_types(self, value_types):
|
||||
# type: (Optional[Sequence[Union[type, Property, serial.model.Object]]]) -> None
|
||||
|
||||
if value_types is not None:
|
||||
|
||||
if isinstance(value_types, (type, Property)):
|
||||
value_types = (value_types,)
|
||||
|
||||
if native_str is not str:
|
||||
|
||||
if isinstance(value_types, collections.Callable):
|
||||
|
||||
_types = value_types
|
||||
|
||||
def value_types(d):
|
||||
# type: (Any) -> Any
|
||||
|
||||
ts = _types(d)
|
||||
|
||||
if (ts is not None) and (str in ts) and (native_str not in ts):
|
||||
ts = tuple(chain(*(
|
||||
((t, native_str) if (t is str) else (t,))
|
||||
for t in ts
|
||||
)))
|
||||
|
||||
return ts
|
||||
|
||||
elif (str in value_types) and (native_str is not str) and (native_str not in value_types):
|
||||
|
||||
value_types = chain(*(
|
||||
((t, native_str) if (t is str) else (t,))
|
||||
for t in value_types
|
||||
))
|
||||
|
||||
if not isinstance(value_types, collections_abc.Callable):
|
||||
value_types = tuple(value_types)
|
||||
|
||||
self._value_types = value_types
|
||||
|
||||
|
||||
class Array(Meta):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
item_types=None, # type: Optional[Sequence[Property, type]]
|
||||
):
|
||||
self._item_types = None # type: Optional[Tuple]
|
||||
self.item_types = item_types
|
||||
|
||||
@property
|
||||
def item_types(self):
|
||||
return self._item_types
|
||||
|
||||
@item_types.setter
|
||||
def item_types(self, item_types):
|
||||
# type: (Optional[Sequence[Union[type, Property, serial.model.Object]]]) -> None
|
||||
if item_types is not None:
|
||||
if isinstance(item_types, (type, Property)):
|
||||
item_types = (item_types,)
|
||||
if native_str is not str:
|
||||
if isinstance(item_types, collections.Callable):
|
||||
_types = item_types
|
||||
|
||||
def item_types(d):
|
||||
# type: (Any) -> Any
|
||||
ts = _types(d)
|
||||
if (ts is not None) and (str in ts) and (native_str not in ts):
|
||||
ts = tuple(chain(*(
|
||||
((t, native_str) if (t is str) else (t,))
|
||||
for t in ts
|
||||
)))
|
||||
return ts
|
||||
elif (str in item_types) and (native_str is not str) and (native_str not in item_types):
|
||||
item_types = chain(*(
|
||||
((t, native_str) if (t is str) else (t,))
|
||||
for t in item_types
|
||||
))
|
||||
if not callable(item_types):
|
||||
item_types = tuple(item_types)
|
||||
self._item_types = item_types
|
||||
|
||||
|
||||
class Properties(OrderedDict):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
items=(
|
||||
None
|
||||
) # type: Optional[Union[Dict[str, Property], List[Tuple[str, Property]]]]
|
||||
):
|
||||
if items is None:
|
||||
super().__init__()
|
||||
else:
|
||||
if isinstance(items, OrderedDict):
|
||||
items = items.items()
|
||||
elif isinstance(items, dict):
|
||||
items = sorted(items.items())
|
||||
super().__init__(items)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
# type: (str, Property) -> None
|
||||
if not isinstance(value, Property):
|
||||
raise ValueError(value)
|
||||
super().__setitem__(key, value)
|
||||
|
||||
def __copy__(self):
|
||||
# type: () -> Properties
|
||||
return self.__class__(self)
|
||||
|
||||
def __deepcopy__(self, memo=None):
|
||||
# type: (dict) -> Properties
|
||||
return self.__class__(
|
||||
tuple(
|
||||
(k, deepcopy(v, memo=memo))
|
||||
for k, v in self.items()
|
||||
)
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
representation = [
|
||||
qualified_name(type(self)) + '('
|
||||
]
|
||||
items = tuple(self.items())
|
||||
if len(items) > 0:
|
||||
representation[0] += '['
|
||||
for k, v in items:
|
||||
rv = (
|
||||
qualified_name(v) if isinstance(v, type) else
|
||||
repr(v)
|
||||
)
|
||||
rvls = rv.split('\n')
|
||||
if len(rvls) > 1:
|
||||
rvs = [rvls[0]]
|
||||
for rvl in rvls[1:]:
|
||||
rvs.append(' ' + rvl)
|
||||
rv = '\n'.join(rvs)
|
||||
representation += [
|
||||
' (',
|
||||
' %s,' % repr(k),
|
||||
' %s' % rv,
|
||||
' ),'
|
||||
]
|
||||
else:
|
||||
representation.append(
|
||||
' (%s, %s),' % (repr(k), rv)
|
||||
)
|
||||
representation[-1] = representation[-1][:-1]
|
||||
representation.append(
|
||||
']'
|
||||
)
|
||||
representation[-1] += ')'
|
||||
if len(representation) > 2:
|
||||
return '\n'.join(representation)
|
||||
else:
|
||||
return ''.join(representation)
|
||||
|
||||
|
||||
def read(
|
||||
model # type: Union[type, serial.abc.model.Model]
|
||||
):
|
||||
# type: (...) -> Optional[Meta]
|
||||
|
||||
if isinstance(
|
||||
model,
|
||||
Model
|
||||
):
|
||||
|
||||
return model._meta or read(type(model))
|
||||
|
||||
elif isinstance(model, type) and issubclass(model, Model):
|
||||
|
||||
return model._meta
|
||||
|
||||
else:
|
||||
|
||||
try:
|
||||
|
||||
repr_model = repr(model)
|
||||
|
||||
except:
|
||||
|
||||
repr_model = object.__repr__(model)
|
||||
|
||||
raise TypeError(
|
||||
'%s requires a parameter which is an instance or sub-class of `%s`, not%s' % (
|
||||
serial.utilities.calling_function_qualified_name(),
|
||||
qualified_name(Model),
|
||||
(
|
||||
':\n' + repr_model
|
||||
if '\n' in repr_model else
|
||||
' `%s`' % repr_model
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def writable(
|
||||
model # type: Union[type, serial.abc.model.Model]
|
||||
):
|
||||
# type: (...) -> Optional[Meta]
|
||||
if isinstance(model, Model):
|
||||
if model._meta is None:
|
||||
model._meta = deepcopy(writable(type(model)))
|
||||
elif isinstance(model, type) and issubclass(model, Model):
|
||||
if model._meta is None:
|
||||
model._meta = (
|
||||
Object()
|
||||
if issubclass(model, serial.model.Object) else
|
||||
Array()
|
||||
if issubclass(model, serial.model.Array) else
|
||||
Dictionary()
|
||||
if issubclass(model, serial.model.Dictionary)
|
||||
else None
|
||||
)
|
||||
else:
|
||||
for b in model.__bases__:
|
||||
if hasattr(b, '_meta') and (model._meta is b._meta):
|
||||
model._meta = deepcopy(model._meta)
|
||||
break
|
||||
else:
|
||||
repr_model = repr(model)
|
||||
raise TypeError(
|
||||
'%s requires a parameter which is an instance or sub-class of `%s`, not%s' % (
|
||||
serial.utilities.calling_function_qualified_name(),
|
||||
qualified_name(Model),
|
||||
(
|
||||
':\n' + repr_model
|
||||
if '\n' in repr_model else
|
||||
' `%s`' % repr_model
|
||||
)
|
||||
)
|
||||
)
|
||||
return model._meta
|
||||
|
||||
|
||||
def write(
|
||||
model, # type: Union[type, serial.model.Object]
|
||||
meta # type: Meta
|
||||
):
|
||||
# type: (...) -> None
|
||||
if isinstance(model, Model):
|
||||
model_type = type(model)
|
||||
elif isinstance(model, type) and issubclass(model, Model):
|
||||
model_type = model
|
||||
else:
|
||||
repr_model = repr(model)
|
||||
raise TypeError(
|
||||
'%s requires a value for the parameter `model` which is an instance or sub-class of `%s`, not%s' % (
|
||||
serial.utilities.calling_function_qualified_name(),
|
||||
qualified_name(Model),
|
||||
(
|
||||
':\n' + repr_model
|
||||
if '\n' in repr_model else
|
||||
' `%s`' % repr_model
|
||||
)
|
||||
)
|
||||
)
|
||||
metadata_type = (
|
||||
Object
|
||||
if issubclass(model_type, serial.model.Object) else
|
||||
Array
|
||||
if issubclass(model_type, serial.model.Array) else
|
||||
Dictionary
|
||||
if issubclass(model_type, serial.model.Dictionary)
|
||||
else None
|
||||
)
|
||||
if not isinstance(meta, metadata_type):
|
||||
raise ValueError(
|
||||
'Metadata assigned to `%s` must be of type `%s`' % (
|
||||
qualified_name(model_type),
|
||||
qualified_name(metadata_type)
|
||||
)
|
||||
)
|
||||
model._meta = meta
|
||||
|
||||
|
||||
_UNIDENTIFIED = None
|
||||
|
||||
|
||||
def xpath(model, xpath_=_UNIDENTIFIED):
|
||||
# type: (serial.abc.model.Model, Optional[str]) -> Optional[str]
|
||||
"""
|
||||
Return the xpath at which the element represented by this object was found, relative to the root document. If
|
||||
the parameter `xpath_` is provided--set the value
|
||||
"""
|
||||
|
||||
if not isinstance(model, Model):
|
||||
|
||||
raise TypeError(
|
||||
'`model` must be an instance of `%s`, not %s.' % (qualified_name(Model), repr(model))
|
||||
)
|
||||
|
||||
if xpath_ is not _UNIDENTIFIED:
|
||||
|
||||
if not isinstance(xpath_, str):
|
||||
|
||||
if isinstance(xpath_, native_str):
|
||||
xpath_ = str(xpath_)
|
||||
else:
|
||||
raise TypeError(
|
||||
'`xpath_` must be a `str`, not %s.' % repr(xpath_)
|
||||
)
|
||||
|
||||
model._xpath = xpath_
|
||||
|
||||
if isinstance(model, serial.model.Dictionary):
|
||||
for k, v in model.items():
|
||||
if isinstance(v, Model):
|
||||
xpath(v, '%s/%s' % (xpath_, k))
|
||||
elif isinstance(model, serial.model.Object):
|
||||
for pn, p in read(model).properties.items():
|
||||
k = p.name or pn
|
||||
v = getattr(model, pn)
|
||||
if isinstance(v, Model):
|
||||
xpath(v, '%s/%s' % (xpath_, k))
|
||||
elif isinstance(model, serial.model.Array):
|
||||
for i in range(len(model)):
|
||||
v = model[i]
|
||||
if isinstance(v, Model):
|
||||
xpath(v, '%s[%s]' % (xpath_, str(i)))
|
||||
return model._xpath
|
||||
|
||||
|
||||
def pointer(model, pointer_=_UNIDENTIFIED):
|
||||
# type: (serial.abc.model.Model, Optional[str]) -> Optional[str]
|
||||
|
||||
if not isinstance(model, Model):
|
||||
raise TypeError(
|
||||
'`model` must be an instance of `%s`, not %s.' % (qualified_name(Model), repr(model))
|
||||
)
|
||||
|
||||
if pointer_ is not _UNIDENTIFIED:
|
||||
|
||||
if not isinstance(pointer_, (str, native_str)):
|
||||
raise TypeError(
|
||||
'`pointer_` must be a `str`, not %s (of type `%s`).' % (repr(pointer_), type(pointer_).__name__)
|
||||
)
|
||||
|
||||
model._pointer = pointer_
|
||||
|
||||
if isinstance(model, serial.model.Dictionary):
|
||||
|
||||
for k, v in model.items():
|
||||
if isinstance(v, Model):
|
||||
pointer(v, '%s/%s' % (pointer_, k.replace('~', '~0').replace('/', '~1')))
|
||||
|
||||
elif isinstance(model, serial.model.Object):
|
||||
|
||||
for pn, property in read(model).properties.items():
|
||||
|
||||
k = property.name or pn
|
||||
v = getattr(model, pn)
|
||||
|
||||
if isinstance(v, Model):
|
||||
pointer(v, '%s/%s' % (pointer_, k.replace('~', '~0').replace('/', '~1')))
|
||||
|
||||
elif isinstance(model, serial.model.Array):
|
||||
|
||||
for i in range(len(model)):
|
||||
|
||||
v = model[i]
|
||||
|
||||
if isinstance(v, Model):
|
||||
pointer(v, '%s[%s]' % (pointer_, str(i)))
|
||||
|
||||
return model._pointer
|
||||
|
||||
|
||||
def url(model, url_=_UNIDENTIFIED):
|
||||
# type: (serial.abc.model.Model, Optional[str]) -> Optional[str]
|
||||
if not isinstance(model, serial.abc.model.Model):
|
||||
raise TypeError(
|
||||
'`model` must be an instance of `%s`, not %s.' % (qualified_name(Model), repr(model))
|
||||
)
|
||||
if url_ is not _UNIDENTIFIED:
|
||||
if not isinstance(url_, str):
|
||||
raise TypeError(
|
||||
'`url_` must be a `str`, not %s.' % repr(url_)
|
||||
)
|
||||
model._url = url_
|
||||
if isinstance(model, serial.model.Dictionary):
|
||||
for v in model.values():
|
||||
if isinstance(v, Model):
|
||||
url(v, url_)
|
||||
elif isinstance(model, serial.model.Object):
|
||||
for pn in read(model).properties.keys():
|
||||
v = getattr(model, pn)
|
||||
if isinstance(v, Model):
|
||||
url(v, url_)
|
||||
elif isinstance(model, serial.model.Array):
|
||||
for v in model:
|
||||
if isinstance(v, Model):
|
||||
url(v, url_)
|
||||
return model._url
|
||||
|
||||
|
||||
def format_(model, serialization_format=_UNIDENTIFIED):
|
||||
|
||||
# type: (serial.abc.model.Model, Optional[str]) -> Optional[str]
|
||||
if not isinstance(model, Model):
|
||||
|
||||
raise TypeError(
|
||||
'`model` must be an instance of `%s`, not %s.' % (qualified_name(Model), repr(model))
|
||||
)
|
||||
|
||||
if serialization_format is not _UNIDENTIFIED:
|
||||
|
||||
if not isinstance(serialization_format, str):
|
||||
|
||||
if isinstance(serialization_format, native_str):
|
||||
serialization_format = str(serialization_format)
|
||||
else:
|
||||
raise TypeError(
|
||||
'`serialization_format` must be a `str`, not %s.' % repr(serialization_format)
|
||||
)
|
||||
|
||||
model._format = serialization_format
|
||||
|
||||
if isinstance(model, serial.model.Dictionary):
|
||||
for v in model.values():
|
||||
if isinstance(v, Model):
|
||||
format_(v, serialization_format)
|
||||
elif isinstance(model, serial.model.Object):
|
||||
for pn in read(model).properties.keys():
|
||||
v = getattr(model, pn)
|
||||
if isinstance(v, Model):
|
||||
format_(v, serialization_format)
|
||||
elif isinstance(model, serial.model.Array):
|
||||
for v in model:
|
||||
if isinstance(v, Model):
|
||||
format_(v, serialization_format)
|
||||
|
||||
return model._format
|
||||
|
||||
|
||||
def version(data, specification, version_number):
|
||||
# type: (serial.abc.model.Model, str, Union[str, int, Sequence[int]]) -> None
|
||||
"""
|
||||
Recursively alters model class or instance metadata based on version number metadata associated with an
|
||||
object's properties. This allows one data model to represent multiple versions of a specification and dynamically
|
||||
change based on the version of a specification represented.
|
||||
|
||||
Arguments:
|
||||
|
||||
- data (serial.abc.model.Model)
|
||||
|
||||
- specification (str):
|
||||
|
||||
The specification to which the `version_number` argument applies.
|
||||
|
||||
- version_number (str|int|[int]):
|
||||
|
||||
A version number represented as text (in the form of integers separated by periods), an integer, or a
|
||||
sequence of integers.
|
||||
"""
|
||||
if not isinstance(data, serial.abc.model.Model):
|
||||
raise TypeError(
|
||||
'The data provided is not an instance of serial.abc.model.Model: ' + repr(data)
|
||||
)
|
||||
|
||||
def version_match(property_):
|
||||
# type: (serial.properties.Property) -> bool
|
||||
if property_.versions is not None:
|
||||
version_matched = False
|
||||
specification_matched = False
|
||||
for applicable_version in property_.versions:
|
||||
if applicable_version.specification == specification:
|
||||
specification_matched = True
|
||||
if applicable_version == version_number:
|
||||
version_matched = True
|
||||
break
|
||||
if specification_matched and (not version_matched):
|
||||
return False
|
||||
return True
|
||||
|
||||
def version_properties(properties_):
|
||||
# type: (Sequence[serial.properties.Property]) -> Optional[Sequence[Meta]]
|
||||
changed = False
|
||||
nps = []
|
||||
|
||||
for property in properties_:
|
||||
|
||||
if isinstance(property, serial.properties.Property):
|
||||
if version_match(property):
|
||||
np = version_property(property)
|
||||
if np is not property:
|
||||
changed = True
|
||||
nps.append(np)
|
||||
else:
|
||||
changed = True
|
||||
else:
|
||||
nps.append(property)
|
||||
if changed:
|
||||
return tuple(nps)
|
||||
else:
|
||||
return None
|
||||
|
||||
def version_property(property):
|
||||
# type: (serial.properties.Property) -> Meta
|
||||
changed = False
|
||||
if isinstance(property, serial.properties.Array) and (property.item_types is not None):
|
||||
item_types = version_properties(property.item_types)
|
||||
if item_types is not None:
|
||||
if not changed:
|
||||
property = deepcopy(property)
|
||||
property.item_types = item_types
|
||||
changed = True
|
||||
elif isinstance(property, serial.properties.Dictionary) and (property.value_types is not None):
|
||||
value_types = version_properties(property.value_types)
|
||||
if value_types is not None:
|
||||
if not changed:
|
||||
property = deepcopy(property)
|
||||
property.value_types = value_types
|
||||
changed = True
|
||||
if property.types is not None:
|
||||
types = version_properties(property.types)
|
||||
if types is not None:
|
||||
if not changed:
|
||||
property = deepcopy(property)
|
||||
property.types = types
|
||||
return property
|
||||
|
||||
instance_meta = read(data)
|
||||
class_meta = read(type(data))
|
||||
|
||||
if isinstance(data, serial.abc.model.Object):
|
||||
|
||||
for property_name in tuple(instance_meta.properties.keys()):
|
||||
|
||||
property = instance_meta.properties[property_name]
|
||||
|
||||
if version_match(property):
|
||||
np = version_property(property)
|
||||
if np is not property:
|
||||
if instance_meta is class_meta:
|
||||
instance_meta = writable(data)
|
||||
instance_meta.properties[property_name] = np
|
||||
else:
|
||||
if instance_meta is class_meta:
|
||||
instance_meta = writable(data)
|
||||
del instance_meta.properties[property_name]
|
||||
version_ = getattr(data, property_name)
|
||||
if version_ is not None:
|
||||
raise serial.errors.VersionError(
|
||||
'%s - the property `%s` is not applicable in %s version %s:\n%s' % (
|
||||
qualified_name(type(data)),
|
||||
property_name,
|
||||
specification,
|
||||
version_number,
|
||||
str(data)
|
||||
)
|
||||
)
|
||||
|
||||
value = getattr(data, property_name)
|
||||
|
||||
if isinstance(value, serial.abc.model.Model):
|
||||
version(value, specification, version_number)
|
||||
|
||||
elif isinstance(data, serial.abc.model.Dictionary):
|
||||
|
||||
if instance_meta and instance_meta.value_types:
|
||||
|
||||
new_value_types = version_properties(instance_meta.value_types)
|
||||
|
||||
if new_value_types:
|
||||
|
||||
if instance_meta is class_meta:
|
||||
instance_meta = writable(data)
|
||||
|
||||
instance_meta.value_types = new_value_types
|
||||
|
||||
for value in data.values():
|
||||
if isinstance(value, serial.abc.model.Model):
|
||||
version(value, specification, version_number)
|
||||
|
||||
elif isinstance(data, serial.abc.model.Array):
|
||||
|
||||
if instance_meta and instance_meta.item_types:
|
||||
|
||||
new_item_types = version_properties(instance_meta.item_types)
|
||||
|
||||
if new_item_types:
|
||||
|
||||
if instance_meta is class_meta:
|
||||
instance_meta = writable(data)
|
||||
|
||||
instance_meta.item_types = new_item_types
|
||||
|
||||
for item in data:
|
||||
if isinstance(item, serial.abc.model.Model):
|
||||
version(item, specification, version_number)
|
||||
Reference in New Issue
Block a user