forked from Qiskit/qiskit
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor shallow schema (Qiskit#1350)
* `shallow_schema` refactor to make its generation more maintainable. Overview ======== This PR introduces a kind of _mixin field_ `ModelValidation` with model type validation. All the Marshmallow fields are imported into `qiskit.validation.fields` and extended to include the mixin. The same happens with polymorphic fields and containers. Validation is possible thanks to a special copy of the model schema, the _shallow schema_, which replaces the `_deserialize` private method of its fields (at instance-level) to use the new `validate_model` from the `ModelValidator` mixin. Validating models consist into checking that the values are of the corresponding types. Specifically: 1. For regular fields, the type they deserialize to (deserialization type). 2. For fields containers (such as lists), the deserialization type of the contained field. 3. For schema containers (such as nested schemas), the type of the schema's model. 4. For polymorphic fields, one of the deserialization types of the choices. 5. For polymorphic schemas, one of the types of the possible schemas' model. While validating, nothing more than the type of the value is considered. Validation assumes that the values of the inner properties are already valid or they would have failed during their instantiation. For instance, consider: ```python Book(title="The Terror", author=Person(name="Dan Simmons")) ``` `Person` instantiation is executed first. If successful, validating `Book` does not require `author`'s internal structure (i.e. `name`) to be validated again since otherwise, construction of `Person` would have failed. --- This refactor continues patching the method `_deserialize` of the schema fields but, instead of deciding how to patch it according to the type, it makes `_deserialize` to call a new custom method `_validate_model` with the desired behaviour. The different strategies from the if/elif/else block of the original implementation are now spread over the fields defined in qiskit.validation.fields This package also contains versions of marshmallow.fields that include their own `_validate_model` method. * Organize the fields into several modules and factor out validation. This PR split the fields into general fields, containers and polymorphic. Each module has the definition of the different kind of fields. It also provides a base field for those able of model type validation and a general mechanism to perform this validation. * Polishing docstrings and reviewing container and polymorphic validations. * Rename to make intentions clear and fixed fields hierarchy * Moving tests to its own folder * Split tests into different categories * Docstrings fixes and clarifications. * Style, convention and doc tweaks * Update CHANGELOG
- Loading branch information
1 parent
6525615
commit b7865d6
Showing
17 changed files
with
777 additions
and
504 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
# Copyright 2018, IBM. | ||
# | ||
# This source code is licensed under the Apache License, Version 2.0 found in | ||
# the LICENSE.txt file in the root directory of this source tree. | ||
|
||
"""Fields to be used with Qiskit validated classes. | ||
When extending this module with new Fields: | ||
1. Distinguish a new type, like the ``Complex`` number in this module. | ||
2. Use a new Marshmallow field not used in ``qiskit`` yet. | ||
Marshamallow fields does not allow model validation so you need to create a new | ||
field, make it subclass of the Marshamallow field *and* ``ModelTypeValidator``, | ||
and redefine ``valid_types`` to be the list of valid types. Usually, **the | ||
same types this field deserializes to**. For instance:: | ||
class Boolean(marshmallow.fields.Boolean, ModelTypeValidator): | ||
__doc__ = _fields.Boolean.__doc__ | ||
valid_types = (bool, ) | ||
See ``ModelTypeValidator`` for more subclassing options. | ||
""" | ||
from datetime import date, datetime | ||
|
||
from marshmallow import fields as _fields | ||
from marshmallow.utils import is_collection | ||
|
||
from qiskit.validation import ModelTypeValidator | ||
from qiskit.validation.fields.polymorphic import ByAttribute, ByType, TryFrom | ||
from qiskit.validation.fields.containers import Nested, List | ||
|
||
|
||
class Complex(ModelTypeValidator): | ||
"""Field for complex numbers. | ||
Field for parsing complex numbers: | ||
* deserializes to Python's `complex`. | ||
* serializes to a tuple of 2 decimals `(real, imaginary)` | ||
""" | ||
|
||
valid_types = (complex, ) | ||
|
||
default_error_messages = { | ||
'invalid': '{input} cannot be parsed as a complex number.', | ||
'format': '"{input}" cannot be formatted as complex number.', | ||
} | ||
|
||
def _serialize(self, value, attr, obj): | ||
try: | ||
return [value.real, value.imag] | ||
except AttributeError: | ||
self.fail('format', input=value) | ||
|
||
def _deserialize(self, value, attr, data): | ||
if not is_collection(value) or len(value) != 2: | ||
self.fail('invalid', input=value) | ||
|
||
try: | ||
return complex(*value) | ||
except (ValueError, TypeError): | ||
self.fail('invalid', input=value) | ||
|
||
|
||
class String(_fields.String, ModelTypeValidator): | ||
# pylint: disable=missing-docstring | ||
__doc__ = _fields.String.__doc__ | ||
|
||
valid_types = (str, ) | ||
|
||
|
||
class Date(_fields.Date, ModelTypeValidator): | ||
# pylint: disable=missing-docstring | ||
__doc__ = _fields.Date.__doc__ | ||
|
||
valid_types = (date, ) | ||
|
||
|
||
class DateTime(_fields.DateTime, ModelTypeValidator): | ||
# pylint: disable=missing-docstring | ||
__doc__ = _fields.DateTime.__doc__ | ||
|
||
valid_types = (datetime, ) | ||
|
||
|
||
class Email(_fields.Email, String): | ||
# pylint: disable=missing-docstring | ||
__doc__ = _fields.Email.__doc__ | ||
|
||
|
||
class Url(_fields.Url, String): | ||
# pylint: disable=missing-docstring | ||
__doc__ = _fields.Url.__doc__ | ||
|
||
|
||
class Number(_fields.Number, ModelTypeValidator): | ||
# pylint: disable=missing-docstring | ||
__doc__ = _fields.Number.__doc__ | ||
|
||
def _expected_types(self): | ||
return self.num_type | ||
|
||
|
||
class Integer(_fields.Integer, Number): | ||
# pylint: disable=missing-docstring | ||
__doc__ = _fields.Integer.__doc__ | ||
|
||
|
||
class Float(_fields.Float, Number): | ||
# pylint: disable=missing-docstring | ||
__doc__ = _fields.Float.__doc__ | ||
|
||
|
||
class Boolean(_fields.Boolean, ModelTypeValidator): | ||
# pylint: disable=missing-docstring | ||
__doc__ = _fields.Boolean.__doc__ | ||
|
||
valid_types = (bool, ) | ||
|
||
|
||
class Raw(_fields.Raw, ModelTypeValidator): | ||
# pylint: disable=missing-docstring | ||
__doc__ = _fields.Boolean.__doc__ |
Oops, something went wrong.