Source code for aztk.core.models.model

import yaml

from aztk.error import InvalidModelError, InvalidModelFieldError, AztkError, AztkAttributeError
from aztk.core.models import fields


# pylint: disable=W0212,no-member
class ModelMeta(type):
    """
    Model Meta class. This takes all the class definition and build the attributes form all the fields definitions.
    """

    def __new__(mcs, name, bases, attrs):
        attrs["_fields"] = {}

        for base in bases:
            if hasattr(base, "_fields"):
                for k, v in base._fields.items():
                    attrs["_fields"][k] = v
            for k, v in base.__dict__.items():
                if isinstance(v, fields.Field):
                    attrs["_fields"][k] = v

        for k, v in attrs.items():
            if isinstance(v, fields.Field):
                attrs["_fields"][k] = v

        return super().__new__(mcs, name, bases, attrs)


[docs]class Model(metaclass=ModelMeta): """ Base class for all aztk models To implement model wide validation implement `__validate__` method """ def __new__(cls, *_args, **_kwargs): model = super().__new__(cls) model._data = {} model._defaults = {} return model def __init__(self, **kwargs): self._update(kwargs) def __getitem__(self, k): if k not in self._fields: raise AztkAttributeError("{0} doesn't have an attribute called {1}".format(self.__class__.__name__, k)) return getattr(self, k) def __setitem__(self, k, v): if k not in self._fields: raise AztkAttributeError("{0} doesn't have an attribute called {1}".format(self.__class__.__name__, k)) try: setattr(self, k, v) except InvalidModelFieldError as e: self._process_field_error(e, k)
[docs] def __getstate__(self): """ For pickle serialization. This return the state of the model """ return self.to_dict()
[docs] def __setstate__(self, state): """ For pickle serialization. This update the current model with the given state """ self._update(state, ignore_missing=True)
[docs] def validate(self): """ Validate the entire model """ for name, field in self._fields.items(): try: field.validate(getattr(self, name)) except InvalidModelFieldError as e: self._process_field_error(e, name) except InvalidModelError as e: e.model = self raise e if hasattr(self, "__validate__"): self.__validate__()
[docs] def merge(self, other): if not isinstance(other, self.__class__): raise AztkError("Cannot merge {0} as is it not an instance of {1}".format(other, self.__class__.__name__)) for field in other._fields.values(): if field in other._data: field.merge(self, other._data[field]) return self
[docs] @classmethod def from_dict(cls, val: dict): return cls(**val)
[docs] def to_dict(self): output = dict() for name, field in self._fields.items(): output[name] = field.serialize(self) return output
def __str__(self): return yaml.dump(self.to_dict(), default_flow_style=False) def _update(self, values, ignore_missing=False): for k, v in values.items(): try: self[k] = v except AztkAttributeError as e: if not ignore_missing: raise e def _process_field_error(self, e: InvalidModelFieldError, field: str): if not e.field: e.field = field if not e.model: e.model = self raise e