def from_dict(cls, d): """ Convert a dictionary to an instance of this model. Args: d (dict): a serialized version of this model. Returns: Model: an instance of this model. """ model = cls.__new__(cls) model_cls = None tag = model.__class__.__tag__ while tag and model_cls is not model.__class__: model_cls = model.__class__ with add_context(tag): model, d = tag._deserialize_with(model, d) tag = model.__class__.__tag__ for field in reversed(model.__class__.__fields__.values()): with add_context(field): model, d = field._deserialize_with(model, d) model._normalize() model._validate() return model
def _apply(self, stage, element): """ Apply the element field stage to the corresponding element value. """ field, (index, value) = element with add_context(index): return getattr(field, stage)(value)
def _apply(self, stage, element): """ Apply a stage to a particular element in the container. """ index, value = element with add_context(index): return getattr(self.element, stage)(value)
def _apply(self, stage, element): """ Apply the key stage to each key, and the value stage to each value. """ key, value = element with add_context(key): return (getattr(self.key, stage)(key), getattr(self.value, stage)(value))
def to_dict(self): """ Convert this model to a dictionary. Returns: ~collections.OrderedDict: the model serialized as a dictionary. """ d = OrderedDict() for field in self.__class__.__fields__.values(): with add_context(field): d = field._serialize_with(self, d) for tag in reversed(self.__class__.__tags__): with add_context(tag): d = tag._serialize_with(self, d) return d
def _validate(self): """ Validate all fields on this model, and the model itself. This is called by the model constructor and on deserialization, so this is only needed if you modify attributes directly and want to revalidate the model instance. """ for field in self.__class__.__fields__.values(): with add_context(field): field._validate_with(self) self.validate()
def __init__(self, *args, **kwargs): """ Create a new model. Args: *args: positional arguments values for each field on the model. If these are given they will be interpreted as corresponding to the fields in the order they are defined on the model class. **kwargs: keyword argument values for each field on the model. """ if self.__class__.__abstract__: raise TypeError( f'unable to instantiate abstract model {self.__class__.__name__!r}' ) try: for name, value in zip_until_right( self.__class__.__fields__.keys(), args): if name in kwargs: raise TypeError( f'__init__() got multiple values for keyword argument {name!r}' ) kwargs[name] = value except ValueError: max_args = len(self.__class__.__fields__) + 1 given_args = len(args) + 1 raise TypeError( f'__init__() takes a maximum of {max_args!r} ' f'positional arguments but {given_args!r} were given') for field in self.__class__.__fields__.values(): with add_context(field): field._instantiate_with(self, kwargs) if kwargs: kwarg = next(iter(kwargs.keys())) raise TypeError( f'__init__() got an unexpected keyword argument {kwarg!r}') self._normalize() self._validate()
def test_add_context(): field = object() with pytest.raises(ValidationError) as e: with add_context(field): raise ValidationError('something failed') assert e.value._fields == [field]