generated from daniil-berg/boilerplate-py
370 lines
13 KiB
Python
370 lines
13 KiB
Python
"""
|
|
Definition of the `GenericSchema` base class.
|
|
|
|
For details about the inherited methods and attributes, see the official
|
|
documentation of [`marshmallow.Schema`][marshmallow.Schema].
|
|
"""
|
|
|
|
from collections.abc import Iterable, Mapping, Sequence
|
|
from typing import TYPE_CHECKING, Any, Literal, Optional, TypeVar, Union, overload
|
|
from warnings import warn
|
|
|
|
from marshmallow import Schema
|
|
|
|
from ._util import GenericInsightMixin1
|
|
from .decorators import post_load
|
|
|
|
Model = TypeVar("Model")
|
|
|
|
|
|
class GenericSchema(GenericInsightMixin1[Model], Schema):
|
|
"""
|
|
Generic schema parameterized by a **`Model`** class.
|
|
|
|
Data will always be deserialized to instances of that **`Model`** class.
|
|
|
|
!!! note
|
|
The **`Model`** referred to throughout the documentation is a
|
|
**type variable**, not any concrete class. For more information about
|
|
type variables, see the "Generics" section in
|
|
[PEP 484](https://peps.python.org/pep-0484/#generics).
|
|
|
|
Registers a `post_load` hook to pass validated data to the constructor
|
|
of the specified **`Model`**.
|
|
|
|
Requires a specific (non-generic) class to be passed as the **`Model`**
|
|
type argument for deserialization to work properly:
|
|
|
|
```python
|
|
class Foo: # Model
|
|
...
|
|
|
|
class FooSchema(GenericSchema[Foo]):
|
|
...
|
|
```
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
*,
|
|
only: Union[Sequence[str], set[str], None] = None,
|
|
exclude: Union[Sequence[str], set[str]] = (),
|
|
context: Union[dict[str, Any], None] = None,
|
|
load_only: Union[Sequence[str], set[str]] = (),
|
|
dump_only: Union[Sequence[str], set[str]] = (),
|
|
partial: Union[bool, Sequence[str], set[str]] = False,
|
|
unknown: Optional[str] = None,
|
|
many: bool = False, # usage discouraged
|
|
) -> None:
|
|
"""
|
|
Emits a warning, if the `many` argument is not `False`.
|
|
|
|
Otherwise the same as in [`marshmallow.Schema`][marshmallow.Schema].
|
|
|
|
Args:
|
|
only:
|
|
Whitelist of the declared fields to select when instantiating
|
|
the Schema. If `None`, all fields are used. Nested fields can
|
|
be represented with dot delimiters.
|
|
exclude:
|
|
Blacklist of the declared fields to exclude when instantiating
|
|
the Schema. If a field appears in both `only` and `exclude`,
|
|
it is not used. Nested fields can be represented with dot
|
|
delimiters.
|
|
context:
|
|
Optional context passed to
|
|
[`Method`][marshmallow.fields.Method] and
|
|
[`Function`][marshmallow.fields.Function] fields.
|
|
load_only:
|
|
Fields to skip during serialization (write-only fields)
|
|
dump_only:
|
|
Fields to skip during deserialization (read-only fields)
|
|
partial:
|
|
Whether to ignore missing fields and not require any fields
|
|
declared. Propagates down to
|
|
[`Nested`][marshmallow.fields.Nested] fields as well. If its
|
|
value is an iterable, only missing fields listed in that
|
|
iterable will be ignored. Use dot delimiters to specify nested
|
|
fields.
|
|
unknown:
|
|
Whether to exclude, include, or raise an error for unknown
|
|
fields in the data. Use `EXCLUDE`, `INCLUDE` or `RAISE`.
|
|
many:
|
|
!!! warning
|
|
Changing this option schema-wide undermines the type
|
|
safety that this class aims to provide. Passing `True`
|
|
will therefore trigger a warning. You should instead use
|
|
the method-specific `many` parameter, when calling
|
|
[`dump`][marshmallow_generic.GenericSchema.dump]/
|
|
[`dumps`][marshmallow_generic.GenericSchema.dumps] or
|
|
[`load`][marshmallow_generic.GenericSchema.load]/
|
|
[`loads`][marshmallow_generic.GenericSchema.loads].
|
|
"""
|
|
super().__init__(
|
|
only=only,
|
|
exclude=exclude,
|
|
many=many,
|
|
context=context,
|
|
load_only=load_only,
|
|
dump_only=dump_only,
|
|
partial=partial,
|
|
unknown=unknown,
|
|
)
|
|
|
|
def __setattr__(self, name: str, value: Any) -> None:
|
|
"""
|
|
Warns, when trying to set `many` to anything other than `False`.
|
|
|
|
Otherwise the same the normal
|
|
[`object.__setattr__`](https://docs.python.org/3/reference/datamodel.html#object.__setattr__).
|
|
"""
|
|
if name == "many" and value is not False:
|
|
warn(
|
|
"Changing `many` schema-wide breaks type safety. Use the the "
|
|
"`many` parameter of specific methods (like `load`) instead."
|
|
)
|
|
super().__setattr__(name, value)
|
|
|
|
@post_load
|
|
def instantiate(self, data: dict[str, Any], **_kwargs: Any) -> Model:
|
|
"""
|
|
Unpacks `data` into the constructor of the specified **`Model`**.
|
|
|
|
Registered as a
|
|
[`@post_load`][marshmallow_generic.decorators.post_load]
|
|
hook for the schema.
|
|
|
|
!!! warning
|
|
You should probably not use this method directly. No parsing,
|
|
transformation or validation of any kind is done in this method.
|
|
The `data` is passed to the **`Model`** constructor "as is".
|
|
|
|
Args:
|
|
data:
|
|
The validated data after deserialization; will be unpacked
|
|
into the constructor of the specified **`Model`** class.
|
|
|
|
Returns:
|
|
Instance of the schema's **`Model`** initialized with `**data`
|
|
"""
|
|
return self._get_type_arg(0)(**data)
|
|
|
|
if TYPE_CHECKING:
|
|
|
|
@overload # type: ignore[override]
|
|
def dump(
|
|
self,
|
|
obj: Iterable[Model],
|
|
*,
|
|
many: Literal[True],
|
|
) -> list[dict[str, Any]]:
|
|
...
|
|
|
|
@overload
|
|
def dump(
|
|
self,
|
|
obj: Model,
|
|
*,
|
|
many: Optional[Literal[False]] = None,
|
|
) -> dict[str, Any]:
|
|
...
|
|
|
|
def dump(
|
|
self,
|
|
obj: Union[Model, Iterable[Model]],
|
|
*,
|
|
many: Optional[bool] = None,
|
|
) -> Union[dict[str, Any], list[dict[str, Any]]]:
|
|
"""
|
|
Serializes **`Model`** objects to native Python data types.
|
|
|
|
Same as
|
|
[`marshmallow.Schema.dump`][marshmallow.schema.Schema.dump]
|
|
at runtime.
|
|
|
|
Annotations ensure that type checkers will infer the return type
|
|
correctly based on the `many` argument, and also enforce the `obj`
|
|
argument to be an a `list` of **`Model`** instances, if `many` is
|
|
set to `True` or a single instance of it, if `many` is `False`
|
|
(or omitted).
|
|
|
|
Args:
|
|
obj:
|
|
The object or iterable of objects to serialize
|
|
many:
|
|
Whether to serialize `obj` as a collection. If `None`, the
|
|
value for `self.many` is used.
|
|
|
|
Returns:
|
|
(dict[str, Any]): if `many` is set to `False`
|
|
(list[dict[str, Any]]): if `many` is set to `True`
|
|
"""
|
|
...
|
|
|
|
@overload # type: ignore[override]
|
|
def dumps(
|
|
self,
|
|
obj: Iterable[Model],
|
|
*args: Any,
|
|
many: Literal[True],
|
|
**kwargs: Any,
|
|
) -> str:
|
|
...
|
|
|
|
@overload
|
|
def dumps(
|
|
self,
|
|
obj: Model,
|
|
*args: Any,
|
|
many: Optional[Literal[False]] = None,
|
|
**kwargs: Any,
|
|
) -> str:
|
|
...
|
|
|
|
def dumps(
|
|
self,
|
|
obj: Union[Model, Iterable[Model]],
|
|
*args: Any,
|
|
many: Optional[bool] = None,
|
|
**kwargs: Any,
|
|
) -> str:
|
|
"""Same as [`dump`][marshmallow_generic.GenericSchema.dump], but returns a JSON-encoded string."""
|
|
...
|
|
|
|
@overload # type: ignore[override]
|
|
def load(
|
|
self,
|
|
data: Union[Mapping[str, Any], Iterable[Mapping[str, Any]]],
|
|
*,
|
|
many: Literal[True],
|
|
partial: Union[bool, Sequence[str], set[str], None] = None,
|
|
unknown: Optional[str] = None,
|
|
) -> list[Model]:
|
|
...
|
|
|
|
@overload
|
|
def load(
|
|
self,
|
|
data: Union[Mapping[str, Any], Iterable[Mapping[str, Any]]],
|
|
*,
|
|
many: Optional[Literal[False]] = None,
|
|
partial: Union[bool, Sequence[str], set[str], None] = None,
|
|
unknown: Optional[str] = None,
|
|
) -> Model:
|
|
...
|
|
|
|
def load(
|
|
self,
|
|
data: Union[Mapping[str, Any], Iterable[Mapping[str, Any]]],
|
|
*,
|
|
many: Optional[bool] = None,
|
|
partial: Union[bool, Sequence[str], set[str], None] = None,
|
|
unknown: Optional[str] = None,
|
|
) -> Union[list[Model], Model]:
|
|
"""
|
|
Deserializes data to objects of the specified **`Model`** class.
|
|
|
|
Same as
|
|
[`marshmallow.Schema.load`][marshmallow.schema.Schema.load] at
|
|
runtime, but data will always pass through the
|
|
[`instantiate`][marshmallow_generic.schema.GenericSchema.instantiate]
|
|
hook after deserialization.
|
|
|
|
Annotations ensure that type checkers will infer the return type
|
|
correctly based on the **`Model`** type argument of the class.
|
|
|
|
Args:
|
|
data:
|
|
The data to deserialize
|
|
many:
|
|
Whether to deserialize `data` as a collection. If `None`,
|
|
the value for `self.many` is used.
|
|
partial:
|
|
Whether to ignore missing fields and not require any
|
|
fields declared. Propagates down to
|
|
[`Nested`][marshmallow.fields.Nested] fields as well. If
|
|
its value is an iterable, only missing fields listed in
|
|
that iterable will be ignored. Use dot delimiters to
|
|
specify nested fields.
|
|
unknown:
|
|
Whether to exclude, include, or raise an error for unknown
|
|
fields in the data. Use `EXCLUDE`, `INCLUDE` or `RAISE`.
|
|
If `None`, the value for `self.unknown` is used.
|
|
|
|
Returns:
|
|
(Model): if `many` is set to `False`
|
|
(list[Model]): if `many` is set to `True`
|
|
"""
|
|
...
|
|
|
|
@overload # type: ignore[override]
|
|
def loads(
|
|
self,
|
|
json_data: str,
|
|
*,
|
|
many: Literal[True],
|
|
partial: Union[bool, Sequence[str], set[str], None] = None,
|
|
unknown: Optional[str] = None,
|
|
**kwargs: Any,
|
|
) -> list[Model]:
|
|
...
|
|
|
|
@overload
|
|
def loads(
|
|
self,
|
|
json_data: str,
|
|
*,
|
|
many: Optional[Literal[False]] = None,
|
|
partial: Union[bool, Sequence[str], set[str], None] = None,
|
|
unknown: Optional[str] = None,
|
|
**kwargs: Any,
|
|
) -> Model:
|
|
...
|
|
|
|
def loads(
|
|
self,
|
|
json_data: str,
|
|
*,
|
|
many: Optional[bool] = None,
|
|
partial: Union[bool, Sequence[str], set[str], None] = None,
|
|
unknown: Optional[str] = None,
|
|
**kwargs: Any,
|
|
) -> Union[list[Model], Model]:
|
|
"""
|
|
Deserializes data to objects of the specified **`Model`** class.
|
|
|
|
Same as
|
|
[`marshmallow.Schema.loads`][marshmallow.schema.Schema.loads] at
|
|
runtime, but data will always pass through the
|
|
[`instantiate`][marshmallow_generic.schema.GenericSchema.instantiate]
|
|
hook after deserialization.
|
|
|
|
Annotations ensure that type checkers will infer the return type
|
|
correctly based on the **`Model`** type argument of the class.
|
|
|
|
Args:
|
|
json_data:
|
|
A JSON string of the data to deserialize
|
|
many:
|
|
Whether to deserialize `data` as a collection. If `None`,
|
|
the value for `self.many` is used.
|
|
partial:
|
|
Whether to ignore missing fields and not require any
|
|
fields declared. Propagates down to
|
|
[`Nested`][marshmallow.fields.Nested] fields as well. If
|
|
its value is an iterable, only missing fields listed in
|
|
that iterable will be ignored. Use dot delimiters to
|
|
specify nested fields.
|
|
unknown:
|
|
Whether to exclude, include, or raise an error for unknown
|
|
fields in the data. Use `EXCLUDE`, `INCLUDE` or `RAISE`.
|
|
If `None`, the value for `self.unknown` is used.
|
|
**kwargs:
|
|
Passed to the JSON decoder
|
|
|
|
Returns:
|
|
(Model): if `many` is set to `False`
|
|
(list[Model]): if `many` is set to `True`
|
|
"""
|
|
...
|