Source code for cis_interface.metaschema.datatypes.ContainerMetaschemaType

from cis_interface.metaschema.datatypes import (
    get_type_class, complete_typedef, encode_data, encode_data_readable)
from cis_interface.metaschema.datatypes.MetaschemaType import MetaschemaType


[docs]class ContainerMetaschemaType(MetaschemaType): r"""Type associated with a container of subtypes.""" name = 'container' description = 'A container of other types.' python_types = [] _container_type = None _json_type = None _json_property = None def __init__(self, *args, **kwargs): self._typecls = self._container_type() super(ContainerMetaschemaType, self).__init__(*args, **kwargs) @classmethod def _iterate(cls, container): r"""Iterate over the contents of the container. Each element returned should be a tuple including an index and a value. Args: container (obj): Object to be iterated over. Returns: iterator: Iterator over elements in the container. """ raise NotImplementedError("This must be overwritten by the subclass.") @classmethod def _assign(cls, container, index, value): r"""Assign an element in the container to the specified value. Args: container (obj): Object that element will be assigned to. index (obj): Index in the container object where element will be assigned. value (obj): Value that will be assigned to the element in the container object. """ raise NotImplementedError("This must be overwritten by the subclass.") @classmethod def _has_element(cls, container, index): r"""Check to see if an index is in the container. Args: container (obj): Object that should be checked for index. index (obj): Index that should be checked for. Returns: bool: True if the index is in the container. """ raise NotImplementedError("This must be overwritten by the subclass.") @classmethod def _get_element(cls, container, index, default): r"""Get an element from the container if it exists, otherwise return the default. Args: container (obj): Object that should be returned from. index (obj): Index of element that should be returned. default (obj): Default that should be returned if the index is not in the container. Returns: object: Container contents at specified element. """ out = default if cls._has_element(container, index): out = container[index] return out
[docs] @classmethod def encode_data(cls, obj, typedef): r"""Encode an object's data. Args: obj (object): Object to encode. typedef (dict): Type definition that should be used to encode the object. Returns: string: Encoded object. """ container = cls._container_type() for k, v in cls._iterate(obj): vtypedef = None if cls._json_property in typedef: vtypedef = cls._get_element(typedef[cls._json_property], k, None) vbytes = encode_data(v, typedef=vtypedef) cls._assign(container, k, vbytes) return container
[docs] @classmethod def encode_data_readable(cls, obj, typedef): r"""Encode an object's data in a readable format. Args: obj (object): Object to encode. typedef (dict): Type definition that should be used to encode the object. Returns: string: Encoded object. """ container = cls._container_type() for k, v in cls._iterate(obj): if cls._json_property in typedef: vtypedef = cls._get_element(typedef[cls._json_property], k, None) else: vtypedef = None vbytes = encode_data_readable(v, typedef=vtypedef) cls._assign(container, k, vbytes) return container
[docs] @classmethod def decode_data(cls, obj, typedef): r"""Decode an object. Args: obj (string): Encoded object to decode. typedef (dict): Type definition that should be used to decode the object. Returns: object: Decoded object. """ container = cls._container_type() for k, v in cls._iterate(obj): vtypedef = cls._get_element(typedef[cls._json_property], k, {}) vcls = get_type_class(vtypedef['type']) cls._assign(container, k, vcls.decode_data(v, vtypedef)) return container
[docs] @classmethod def extract_typedef(cls, metadata): r"""Extract the minimum typedef required for this type from the provided metadata. Args: metadata (dict): Message metadata. Returns: dict: Encoded type definition with unncessary properties removed. """ out = super(ContainerMetaschemaType, cls).extract_typedef(metadata) if cls._json_property in out: contents = out[cls._json_property] if isinstance(contents, cls.python_types): for k, v in cls._iterate(contents): if 'type' in v: vcls = get_type_class(v['type']) cls._assign(contents, k, vcls.extract_typedef(v)) out[cls._json_property] = contents return out
[docs] def update_typedef(self, **kwargs): r"""Update the current typedef with new values. Args: **kwargs: All keyword arguments are considered to be new type definitions. If they are a valid definition property, they will be copied to the typedef associated with the instance. Returns: dict: A dictionary of keyword arguments that were not added to the type definition. """ map = kwargs.get(self._json_property, None) map_out = self._container_type() if isinstance(map, self.python_types): for k, v in self._iterate(map): v_typedef = complete_typedef(v) if self._has_element(self._typecls, k): self._assign(map_out, k, self._typecls[k].update_typedef(**v_typedef)) else: self._assign(self._typecls, k, get_type_class(v_typedef['type'])(**v_typedef)) self._assign(map, k, self._typecls[k]._typedef) kwargs[self._json_property] = map out = super(ContainerMetaschemaType, self).update_typedef(**kwargs) if map_out: out[self._json_property] = map_out return out