示例#1
0
def writable(
    model  # type: Union[type, serial.model.Model]
):
    # type: (...) -> Union[Object, Mapping, str]
    if isinstance(model, serial.model.Model):
        if model._meta is None:
            model._meta = deepcopy(writable(type(model)))
    elif issubclass(model, serial.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(serial.model.Model),
                (
                    ':\n' + repr_model
                    if '\n' in repr_model else
                    ' `%s`' % repr_model
                )
            )
        )
    return model._meta
def format_(model, serialization_format=_UNIDENTIFIED):
    # type: (serial.model.Model, Optional[str]) -> Optional[str]
    if not isinstance(model, serial.model.Model):
        raise TypeError('`model` must be an instance of `%s`, not %s.' %
                        (qualified_name(serial.model.Model), repr(model)))
    if serialization_format is not _UNIDENTIFIED:
        if not isinstance(serialization_format, str):
            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, serial.model.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, serial.model.Model):
                    format_(v, serialization_format)
        elif isinstance(model, serial.model.Array):
            for v in model:
                if isinstance(v, serial.model.Model):
                    format_(v, serialization_format)
    return model._format
示例#3
0
def read(
    model  # type: Union[type, serial.model.Model]
):
    # type: (...) -> Union[Object, Mapping, str]
    if isinstance(
        model,
        serial.model.Model
    ):
        return model._meta or read(type(model))
    elif issubclass(model, serial.model.Model):
        return model._meta
    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(serial.model.Model),
                (
                    ':\n' + repr_model
                    if '\n' in repr_model else
                    ' `%s`' % repr_model
                )
            )
        )
示例#4
0
def json_object(
        o,  # type: Union[model.Object, typing.Sequence]
        raise_validation_errors=True,  # type: bool
):
    # type: (...) -> None
    if isinstance(o, model.Object):
        errors = model.validate(o, raise_errors=raise_validation_errors)
        if errors:
            warn('\n' + '\n'.join(errors))
        t = type(o)
        string = str(o)
        assert string != ''
        #print(t)
        #print('string: ' + string)
        reloaded = t(string)
        try:
            assert o == reloaded
        except AssertionError as e:
            message = [
                'Discrepancies were found between the instance of `%s` provided and '
                % qualified_name(type(o)) + 'a serialized/deserialized clone:'
            ]
            for k, a_b in _object_discrepancies(o, reloaded).items():
                a, b = a_b
                sa = model.serialize(a)
                sb = model.serialize(b)
                message.append(
                    '\n    %s().%s:\n\n        %s\n        %s\n        %s' %
                    (qualified_name(
                        type(o)), k, sa, '==' if sa == sb else '!=', sb))
                ra = ''.join(l.strip() for l in repr(a).split('\n'))
                rb = ''.join(l.strip() for l in repr(b).split('\n'))
                message.append('\n        %s\n        %s\n        %s' %
                               (ra, '==' if ra == rb else '!=', rb))
            e.args = tuple(
                chain((e.args[0] + '\n' +
                       '\n'.join(message) if e.args else '\n'.join(message), ),
                      e.args[1:] if e.args else tuple()))
            raise e
        reloaded_string = str(reloaded)
        try:
            assert string == reloaded_string
        except AssertionError as e:
            m = '\n%s\n!=\n%s' % (string, reloaded_string)
            if e.args:
                e.args = tuple(chain((e.args[0] + '\n' + m, ), e.args[1:]))
            else:
                e.args = (m, )
            raise e
        reloaded_json = json.loads(string,
                                   object_hook=collections.OrderedDict,
                                   object_pairs_hook=collections.OrderedDict)
        keys = set()
        for n, p in meta.writable(o).properties.items():
            keys.add(p.name or n)
            json_object(getattr(o, n),
                        raise_validation_errors=raise_validation_errors)
        for k in reloaded_json.keys():
            if k not in keys:
                raise KeyError(
                    '"%s" not found in serialized/re-deserialized data: %s' %
                    (k, string))
    elif (
            # ``isinstance(o, collections.Iterable)`` produces a recursion error in Python 2x, so we test for the
            # existence of an '__iter__' method directly
            hasattr(o, '__iter__')
            and not isinstance(o, (str, native_str, bytes))):
        if isinstance(o, (dict, collections.OrderedDict, model.Dictionary)):
            for k, v in o.items():
                json_object(v, raise_validation_errors=raise_validation_errors)
        else:
            for oo in o:
                json_object(oo,
                            raise_validation_errors=raise_validation_errors)
 def __init__(
     self,
     property,  # type: Property
     items=(
         None
     )  # type: Optional[Union[Sequence[Union[type, Property], Types], type, Property]
 ):
     # (...) -> None
     if not isinstance(property, Property):
         raise TypeError(
             'The parameter `property` must be a `type`, or an instance of `%s`.' % qualified_name(Property)
         )
     self.property = property
     if isinstance(items, (type, Property)):
         items = (items,)
     if items is None:
         super().__init__()
     else:
         super().__init__(items)
示例#6
0
def version(data, specification, version_number):
    # type: (Any, str, Union[str, int, typing.Sequence[int]]) -> Any
    """
    Recursively alters instances of ``serial.model.Object`` according to version_number metadata associated with that
    object's serial.properties.

    Arguments:

        - data

        - 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.
    """
    def version_match(p):
        if p.versions is not None:
            vm = False
            sm = False
            for v in p.versions:
                if v.specification == specification:
                    sm = True
                    if v == version_number:
                        vm = True
                        break
            if sm and (not vm):
                return False
        return True

    def version_properties(ps):
        # type: (typing.Sequence[serial.properties.Property]) -> Optional[typing.Sequence[serial.meta.Meta]]
        changed = False
        nps = []
        for p in ps:
            if isinstance(p, serial.properties.Property):
                if version_match(p):
                    np = version_property(p)
                    if np is not p:
                        changed = True
                    nps.append(np)
                else:
                    changed = True
            else:
                nps.append(p)
        if changed:
            return tuple(nps)
        else:
            return None

    def version_property(p):
        # type: (serial.properties.Property) -> serial.meta.Meta
        changed = False
        if isinstance(p, serial.properties.Array) and (p.item_types
                                                       is not None):
            item_types = version_properties(p.item_types)
            if item_types is not None:
                if not changed:
                    p = deepcopy(p)
                p.item_types = item_types
                changed = True
        elif isinstance(p, serial.properties.Dictionary) and (p.value_types
                                                              is not None):
            value_types = version_properties(p.value_types)
            if value_types is not None:
                if not changed:
                    p = deepcopy(p)
                p.value_types = value_types
                changed = True
        if p.types is not None:
            types = version_properties(p.types)
            if types is not None:
                if not changed:
                    p = deepcopy(p)
                p.types = types
        return p

    if isinstance(data, Model):
        im = serial.meta.read(data)
        cm = serial.meta.read(type(data))
        if isinstance(data, Object):
            for n in tuple(im.properties.keys()):
                p = im.properties[n]
                if version_match(p):
                    np = version_property(p)
                    if np is not p:
                        if im is cm:
                            im = serial.meta.writable(data)
                        im.properties[n] = np
                else:
                    if im is cm:
                        im = serial.meta.writable(data)
                    del im.properties[n]
                    v = getattr(data, n)
                    if v is not None:
                        raise serial.errors.VersionError(
                            '%s - the property `%s` is not applicable in %s version %s:\n%s'
                            % (qualified_name(type(data)), n, specification,
                               version_number, str(data)))
                version(getattr(data, n), specification, version_number)
        elif isinstance(data, Dictionary):
            if im.value_types:
                new_value_types = version_properties(im.value_types)
                if new_value_types:
                    if im is cm:
                        im = serial.meta.writable(data)
                    im.value_types = new_value_types
            for v in data.values():
                version(v, specification, version_number)
        elif isinstance(data, Array):
            if im.item_types:
                new_item_types = version_properties(im.item_types)
                if new_item_types:
                    if im is cm:
                        im = serial.meta.writable(data)
                    im.item_types = new_item_types
            for v in data:
                version(v, specification, version_number)
    elif isinstance(
            data,
        (collections.Set,
         collections.Sequence)) and not isinstance(data, (str, bytes)):
        # for d in data:
        #     version(d, specification, version_number)
        raise ValueError()
    elif isinstance(data, (dict, OrderedDict)):
        # for k, v in data.items():
        #     version(v, specification, version_number)
        raise ValueError()
示例#7
0
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)
 def __repr__(self):
     return ('\n'.join(
         ['%s(' % qualified_name(type(self))] +
         ['    %s=%s,' % (p, repr(v))
          for p, v in properties_values(self)] + [')']))
示例#9
0
def model(
        model_instance,  # type: serial.model.Model
        format_,  # type: str
        raise_validation_errors=True,  # type: bool
):
    # type: (...) -> None
    """
    Tests an instance of a `serial.model.Model` sub-class.

    Parameters:

        - model_instance (serial.model.Model):

            An instance of a `serial.model.Model` sub-class.

        - format_ (str):

            The serialization format being tested: 'json', 'yaml' or 'xml'.

        - raise_validation_errors (bool):

            The function `serial.model.validate` verifies that all required attributes are present, as well as any
            additional validations implemented using the model's validation hooks `after_validate` and/or
            `before_validate`.

                - If `True`, errors resulting from `serial.model.validate` are raised.

                - If `False`, errors resulting from `serial.model.validate` are expressed only as warnings.
    """
    if not isinstance(model_instance, Model):

        value_representation = repr(model_instance)

        raise TypeError(
            '`%s` requires an instance of `%s` for the parameter `model_instance`, not%s'
            % (calling_function_qualified_name(), qualified_name(Model),
               ((':\n%s' if '\n' in value_representation else ' `%s`') %
                value_representation)))

    serial.meta.format_(model_instance, format_)

    if isinstance(model_instance, serial.model.Object):

        errors = serial.marshal.validate(model_instance,
                                         raise_errors=raise_validation_errors)

        if errors:
            warn('\n' + '\n'.join(errors))

        model_type = type(model_instance)

        string = str(model_instance)

        assert string != ''

        reloaded_model_instance = model_type(string)
        qualified_model_name = qualified_name(type(model_instance))

        try:

            assert model_instance == reloaded_model_instance

        except AssertionError as e:

            sa = serial.marshal.serialize(model_instance)
            sb = serial.marshal.serialize(reloaded_model_instance)

            ra = ''.join(l.strip() for l in repr(model_instance).split('\n'))
            rb = ''.join(l.strip()
                         for l in repr(reloaded_model_instance).split('\n'))

            message = [
                'Discrepancies were found between the instance of `%s` provided and '
                % qualified_model_name + 'a serialized/deserialized clone:'
            ]

            if sa != sb:

                message.append(
                    '\n    :\n\n        %s\n        !=\n        %s' % (sa, sb))

            if ra != rb:

                message.append('\n        %s\n        !=\n        %s\n' %
                               (ra, rb))

            for k, a_b in _object_discrepancies(
                    model_instance, reloaded_model_instance).items():

                a, b = a_b

                assert a != b

                sa = serial.marshal.serialize(a)
                sb = serial.marshal.serialize(b)

                if sa != sb:

                    message.append(
                        '\n    %s().%s:\n\n        %s\n        %s\n        %s'
                        % (qualified_model_name, k, sa,
                           '==' if sa == sb else '!=', sb))

                ra = ''.join(l.strip() for l in repr(a).split('\n'))
                rb = ''.join(l.strip() for l in repr(b).split('\n'))

                if ra != rb:

                    message.append('\n        %s\n        %s\n        %s' %
                                   (ra, '==' if ra == rb else '!=', rb))

            e.args = tuple(
                chain((e.args[0] + '\n' +
                       '\n'.join(message) if e.args else '\n'.join(message), ),
                      e.args[1:] if e.args else tuple()))

            raise e

        reloaded_string = str(reloaded_model_instance)

        try:
            assert string == reloaded_string
        except AssertionError as e:
            m = '\n%s\n!=\n%s' % (string, reloaded_string)
            if e.args:
                e.args = tuple(chain((e.args[0] + '\n' + m, ), e.args[1:]))
            else:
                e.args = (m, )
            raise e

        if format_ == 'json':
            reloaded_marshalled_data = _json.loads(
                string,
                object_hook=collections.OrderedDict,
                object_pairs_hook=collections.OrderedDict)
        elif format_ == 'yaml':
            reloaded_marshalled_data = _yaml.load(string)
        elif format_ == 'xml':
            raise NotImplementedError()
        else:
            format_representation = repr(format_)
            raise ValueError(
                'Valid serialization types for parameter `format_` are "json", "yaml", or "xml'
                ", not" +
                ((':\n%s' if '\n' in format_representation else ' %s.') %
                 format_representation))

        keys = set()

        for property_name, property in serial.meta.read(
                model_instance).properties.items():

            keys.add(property.name or property_name)
            property_value = getattr(model_instance, property_name)

            if isinstance(
                    property_value,
                    Model) or (hasattr(property_value, '__iter__') and
                               (not isinstance(property_value,
                                               (str, native_str, bytes)))):
                model(property_value,
                      format_=format_,
                      raise_validation_errors=raise_validation_errors)

        for k in reloaded_marshalled_data.keys():

            if k not in keys:
                raise KeyError(
                    '"%s" not found in serialized/re-deserialized data: %s' %
                    (k, string))

    elif isinstance(model_instance, serial.model.Array):

        serial.marshal.validate(model_instance)

        for item in model_instance:

            if isinstance(
                    item,
                    Model) or (hasattr(item, '__iter__') and
                               (not isinstance(item,
                                               (str, native_str, bytes)))):
                model(item,
                      format_=format_,
                      raise_validation_errors=raise_validation_errors)

    elif isinstance(model_instance, serial.model.Dictionary):

        serial.marshal.validate(model_instance)

        for key, value in model_instance.items():

            if isinstance(
                    value,
                    Model) or (hasattr(value, '__iter__') and
                               (not isinstance(value,
                                               (str, native_str, bytes)))):
                model(value,
                      format_=format_,
                      raise_validation_errors=raise_validation_errors)
示例#10
0
 def __repr__(self):
     representation = [
         qualified_name(type(self)) + '('
     ]
     if len(self) > 0:
         representation.append('    [')
         for i in self:
             ri = (
                 qualified_name(i) if isinstance(i, type) else
                 repr(i)
             )
             rils = ri.split('\n')
             if len(rils) > 1:
                 ris = [rils[0]]
                 ris += [
                     '        ' + rvl
                     for rvl in rils[1:]
                 ]
                 ri = '\n'.join(ris)
             representation.append(
                 '        %s,' % ri
             )
         im = serial.meta.read(self)
         cm = serial.meta.read(type(self))
         m = None if (im is cm) else im
         representation.append(
             '    ]' + (''
             if m is None or m.item_types is None
             else ',')
         )
     im = serial.meta.read(self)
     cm = serial.meta.read(type(self))
     if im is not cm:
         if im.item_types:
             representation.append(
                 '    item_types=(',
             )
             for it in im.item_types:
                 ri = (
                     qualified_name(it) if isinstance(it, type) else
                     repr(it)
                 )
                 rils = ri.split('\n')
                 if len(rils) > 2:
                     ris = [rils[0]]
                     ris += [
                         '        ' + rvl
                         for rvl in rils[1:-1]
                     ]
                     ris.append('        ' + rils[-1])
                     ri = '\n'.join(ris)
                 representation.append('        %s,' % ri)
             m = serial.meta.read(self)
             if len(m.item_types) > 1:
                 representation[-1] = representation[-1][:-1]
             representation.append('    )')
     representation.append(')')
     if len(representation) > 2:
         return '\n'.join(representation)
     else:
         return ''.join(representation)
示例#11
0
 def __repr__(self):
     representation = [
         qualified_name(type(self)) + '('
     ]
     items = tuple(self.items())
     if len(items) > 0:
         representation.append('    [')
         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)
                 # rvs.append('            ' + rvs[-1])
                 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(
             '    ]'
             if self._meta is None or self._meta.value_types is None else
             '    ],'
         )
     cm = serial.meta.read(type(self))
     im = serial.meta.read(self)
     if cm is not im:
         if self._meta.value_types:
             representation.append(
                 '    value_types=(',
             )
             for vt in im.value_types:
                 rv = (
                     qualified_name(vt) if isinstance(vt, type) else
                     repr(vt)
                 )
                 rvls = rv.split('\n')
                 if len(rvls) > 1:
                     rvs = [rvls[0]]
                     rvs += [
                         '        ' + rvl
                         for rvl in rvls[1:]
                     ]
                     rv = '\n'.join(rvs)
                 representation.append('        %s,' % rv)
             if len(self._meta.value_types) > 1:
                 representation[-1] = representation[-1][:-1]
             representation.append('    )')
     representation.append(')')
     if len(representation) > 2:
         return '\n'.join(representation)
     else:
         return ''.join(representation)