generated from daniil-berg/boilerplate-py
Compare commits
3 Commits
21c201b681
...
fe5decad4f
Author | SHA1 | Date | |
---|---|---|---|
fe5decad4f | |||
712f7fca7b | |||
84fa2d2cd9 |
@ -30,8 +30,11 @@ plugins:
|
|||||||
handlers:
|
handlers:
|
||||||
python:
|
python:
|
||||||
options:
|
options:
|
||||||
|
line_length: 80
|
||||||
show_source: false
|
show_source: false
|
||||||
show_root_toc_entry: false
|
show_root_toc_entry: false
|
||||||
|
separate_signature: true
|
||||||
|
show_signature_annotations: true
|
||||||
import:
|
import:
|
||||||
- https://marshmallow.readthedocs.io/en/stable/objects.inv
|
- https://marshmallow.readthedocs.io/en/stable/objects.inv
|
||||||
|
|
||||||
|
@ -1,10 +1,28 @@
|
|||||||
from typing import Any, Generic, Optional, TypeVar, get_args, get_origin
|
from typing import (
|
||||||
|
Any,
|
||||||
|
Generic,
|
||||||
|
Literal,
|
||||||
|
Optional,
|
||||||
|
TypeVar,
|
||||||
|
Union,
|
||||||
|
get_args,
|
||||||
|
get_origin,
|
||||||
|
overload,
|
||||||
|
)
|
||||||
|
|
||||||
_T = TypeVar("_T")
|
_T0 = TypeVar("_T0")
|
||||||
|
_T1 = TypeVar("_T1")
|
||||||
|
_T2 = TypeVar("_T2")
|
||||||
|
_T3 = TypeVar("_T3")
|
||||||
|
_T4 = TypeVar("_T4")
|
||||||
|
|
||||||
|
|
||||||
class GenericInsightMixin(Generic[_T]):
|
class GenericInsightMixin(Generic[_T0, _T1, _T2, _T3, _T4]):
|
||||||
_type_arg: Optional[type[_T]] = None
|
_type_arg_0: Optional[type[_T0]] = None
|
||||||
|
_type_arg_1: Optional[type[_T1]] = None
|
||||||
|
_type_arg_2: Optional[type[_T2]] = None
|
||||||
|
_type_arg_3: Optional[type[_T3]] = None
|
||||||
|
_type_arg_4: Optional[type[_T4]] = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def __init_subclass__(cls, **kwargs: Any) -> None:
|
def __init_subclass__(cls, **kwargs: Any) -> None:
|
||||||
@ -14,17 +32,70 @@ class GenericInsightMixin(Generic[_T]):
|
|||||||
origin = get_origin(base)
|
origin = get_origin(base)
|
||||||
if origin is None or not issubclass(origin, GenericInsightMixin):
|
if origin is None or not issubclass(origin, GenericInsightMixin):
|
||||||
continue
|
continue
|
||||||
type_arg = get_args(base)[0]
|
type_args = get_args(base)
|
||||||
# Do not set the attribute for GENERIC subclasses!
|
for idx, arg in enumerate(type_args):
|
||||||
if not isinstance(type_arg, TypeVar):
|
# Do not set the attribute for generics:
|
||||||
cls._type_arg = type_arg
|
if isinstance(arg, TypeVar):
|
||||||
return
|
continue
|
||||||
|
# Do not set `NoneType`:
|
||||||
|
if isinstance(arg, type) and isinstance(None, arg):
|
||||||
|
continue
|
||||||
|
setattr(cls, f"_type_arg_{idx}", arg)
|
||||||
|
return
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_type_arg(cls) -> type[_T]:
|
@overload
|
||||||
|
def _get_type_arg(cls, idx: Literal[0]) -> type[_T0]:
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@overload
|
||||||
|
def _get_type_arg(cls, idx: Literal[1]) -> type[_T1]:
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@overload
|
||||||
|
def _get_type_arg(cls, idx: Literal[2]) -> type[_T2]:
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@overload
|
||||||
|
def _get_type_arg(cls, idx: Literal[3]) -> type[_T3]:
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@overload
|
||||||
|
def _get_type_arg(cls, idx: Literal[4]) -> type[_T4]:
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _get_type_arg(
|
||||||
|
cls,
|
||||||
|
idx: Literal[0, 1, 2, 3, 4],
|
||||||
|
) -> Union[type[_T0], type[_T1], type[_T2], type[_T3], type[_T4]]:
|
||||||
"""Returns the type argument of the class (if specified)."""
|
"""Returns the type argument of the class (if specified)."""
|
||||||
if cls._type_arg is None:
|
if idx == 0:
|
||||||
|
type_ = cls._type_arg_0
|
||||||
|
elif idx == 1:
|
||||||
|
type_ = cls._type_arg_1
|
||||||
|
elif idx == 2: # noqa: PLR2004
|
||||||
|
type_ = cls._type_arg_2
|
||||||
|
elif idx == 3: # noqa: PLR2004
|
||||||
|
type_ = cls._type_arg_3
|
||||||
|
elif idx == 4: # noqa: PLR2004
|
||||||
|
type_ = cls._type_arg_4
|
||||||
|
else:
|
||||||
|
raise ValueError("Only 5 type parameters available")
|
||||||
|
if type_ is None:
|
||||||
raise AttributeError(
|
raise AttributeError(
|
||||||
f"{cls.__name__} is generic; type argument unspecified"
|
f"{cls.__name__} is generic; type argument {idx} unspecified"
|
||||||
)
|
)
|
||||||
return cls._type_arg
|
return type_
|
||||||
|
|
||||||
|
|
||||||
|
class GenericInsightMixin1(GenericInsightMixin[_T0, None, None, None, None]):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class GenericInsightMixin2(GenericInsightMixin[_T0, _T1, None, None, None]):
|
||||||
|
pass
|
||||||
|
@ -7,16 +7,17 @@ documentation of [`marshmallow.Schema`][marshmallow.Schema].
|
|||||||
|
|
||||||
from collections.abc import Iterable, Mapping, Sequence
|
from collections.abc import Iterable, Mapping, Sequence
|
||||||
from typing import TYPE_CHECKING, Any, Literal, Optional, TypeVar, Union, overload
|
from typing import TYPE_CHECKING, Any, Literal, Optional, TypeVar, Union, overload
|
||||||
|
from warnings import warn
|
||||||
|
|
||||||
from marshmallow import Schema
|
from marshmallow import Schema
|
||||||
|
|
||||||
from ._util import GenericInsightMixin
|
from ._util import GenericInsightMixin1
|
||||||
from .decorators import post_load
|
from .decorators import post_load
|
||||||
|
|
||||||
Model = TypeVar("Model")
|
Model = TypeVar("Model")
|
||||||
|
|
||||||
|
|
||||||
class GenericSchema(GenericInsightMixin[Model], Schema):
|
class GenericSchema(GenericInsightMixin1[Model], Schema):
|
||||||
"""
|
"""
|
||||||
Generic schema parameterized by a **`Model`** class.
|
Generic schema parameterized by a **`Model`** class.
|
||||||
|
|
||||||
@ -43,6 +44,79 @@ class GenericSchema(GenericInsightMixin[Model], Schema):
|
|||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
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: Optional[bool] = None,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Emits a warning, if the `many` argument is not `None`.
|
||||||
|
|
||||||
|
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
|
||||||
|
Specifying this option schema-wide undermines the type
|
||||||
|
safety that this class aims to provide and passing any
|
||||||
|
value other than `None` will trigger a warning. 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] instead.
|
||||||
|
"""
|
||||||
|
if many is not None:
|
||||||
|
warn(
|
||||||
|
"Setting `many` schema-wide breaks type safety. Use the the "
|
||||||
|
"`many` parameter of specific methods (like `load`) instead."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
many = bool(many)
|
||||||
|
super().__init__(
|
||||||
|
only=only,
|
||||||
|
exclude=exclude,
|
||||||
|
many=many,
|
||||||
|
context=context,
|
||||||
|
load_only=load_only,
|
||||||
|
dump_only=dump_only,
|
||||||
|
partial=partial,
|
||||||
|
unknown=unknown,
|
||||||
|
)
|
||||||
|
|
||||||
@post_load
|
@post_load
|
||||||
def instantiate(self, data: dict[str, Any], **_kwargs: Any) -> Model:
|
def instantiate(self, data: dict[str, Any], **_kwargs: Any) -> Model:
|
||||||
"""
|
"""
|
||||||
@ -52,10 +126,9 @@ class GenericSchema(GenericInsightMixin[Model], Schema):
|
|||||||
[marshmallow_generic.decorators.post_load] hook for the schema.
|
[marshmallow_generic.decorators.post_load] hook for the schema.
|
||||||
|
|
||||||
!!! warning
|
!!! warning
|
||||||
You should probably **not** use this method directly;
|
You should probably not use this method directly. No parsing,
|
||||||
no parsing, transformation or validation of any kind is done
|
transformation or validation of any kind is done in this method.
|
||||||
in this method. The `data` passed to the **`Model`** constructor
|
The `data` is passed to the **`Model`** constructor "as is".
|
||||||
"as is".
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
data:
|
data:
|
||||||
@ -65,10 +138,89 @@ class GenericSchema(GenericInsightMixin[Model], Schema):
|
|||||||
Returns:
|
Returns:
|
||||||
Instance of the schema's **`Model`** initialized with `**data`
|
Instance of the schema's **`Model`** initialized with `**data`
|
||||||
"""
|
"""
|
||||||
return self._get_type_arg()(**data)
|
return self._get_type_arg(0)(**data)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
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]
|
@overload # type: ignore[override]
|
||||||
def load(
|
def load(
|
||||||
self,
|
self,
|
||||||
|
@ -12,7 +12,11 @@ class GenericInsightMixinTestCase(TestCase):
|
|||||||
mock_super.return_value = MagicMock(__init_subclass__=mock_super_meth)
|
mock_super.return_value = MagicMock(__init_subclass__=mock_super_meth)
|
||||||
|
|
||||||
# Should be `None` by default:
|
# Should be `None` by default:
|
||||||
self.assertIsNone(_util.GenericInsightMixin._type_arg) # type: ignore[misc]
|
self.assertIsNone(_util.GenericInsightMixin._type_arg_0) # type: ignore[misc]
|
||||||
|
self.assertIsNone(_util.GenericInsightMixin._type_arg_1) # type: ignore[misc]
|
||||||
|
self.assertIsNone(_util.GenericInsightMixin._type_arg_2) # type: ignore[misc]
|
||||||
|
self.assertIsNone(_util.GenericInsightMixin._type_arg_3) # type: ignore[misc]
|
||||||
|
self.assertIsNone(_util.GenericInsightMixin._type_arg_4) # type: ignore[misc]
|
||||||
|
|
||||||
# If the mixin type argument was not specified (still generic),
|
# If the mixin type argument was not specified (still generic),
|
||||||
# ensure that the attribute remains `None` on the subclass:
|
# ensure that the attribute remains `None` on the subclass:
|
||||||
@ -24,30 +28,55 @@ class GenericInsightMixinTestCase(TestCase):
|
|||||||
class Bar(Generic[t]):
|
class Bar(Generic[t]):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class TestSchema1(Bar[str], _util.GenericInsightMixin[t]):
|
class TestCls(Bar[str], _util.GenericInsightMixin[t, None, int, str, bool]):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.assertIsNone(TestSchema1._type_arg) # type: ignore[misc]
|
self.assertIsNone(TestCls._type_arg_0) # type: ignore[misc]
|
||||||
|
self.assertIsNone(TestCls._type_arg_1) # type: ignore[misc]
|
||||||
|
self.assertIs(int, TestCls._type_arg_2) # type: ignore[misc]
|
||||||
|
self.assertIs(str, TestCls._type_arg_3) # type: ignore[misc]
|
||||||
|
self.assertIs(bool, TestCls._type_arg_4) # type: ignore[misc]
|
||||||
mock_super.assert_called_once()
|
mock_super.assert_called_once()
|
||||||
mock_super_meth.assert_called_once_with()
|
mock_super_meth.assert_called_once_with()
|
||||||
|
|
||||||
mock_super.reset_mock()
|
mock_super.reset_mock()
|
||||||
mock_super_meth.reset_mock()
|
mock_super_meth.reset_mock()
|
||||||
|
|
||||||
# If the mixin type argument was specified,
|
# If the mixin type arguments were omitted,
|
||||||
# ensure it was assigned to the attribute on the child class:
|
# ensure the attributes remained `None`:
|
||||||
|
|
||||||
class TestSchema2(Bar[str], _util.GenericInsightMixin[Foo]):
|
class UnspecifiedCls(_util.GenericInsightMixin): # type: ignore[type-arg]
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.assertIs(Foo, TestSchema2._type_arg) # type: ignore[misc]
|
self.assertIsNone(UnspecifiedCls._type_arg_0) # type: ignore[misc]
|
||||||
|
self.assertIsNone(UnspecifiedCls._type_arg_1) # type: ignore[misc]
|
||||||
|
self.assertIsNone(UnspecifiedCls._type_arg_2) # type: ignore[misc]
|
||||||
|
self.assertIsNone(UnspecifiedCls._type_arg_3) # type: ignore[misc]
|
||||||
|
self.assertIsNone(UnspecifiedCls._type_arg_4) # type: ignore[misc]
|
||||||
mock_super.assert_called_once()
|
mock_super.assert_called_once()
|
||||||
mock_super_meth.assert_called_once_with()
|
mock_super_meth.assert_called_once_with()
|
||||||
|
|
||||||
def test__get_type_arg(self) -> None:
|
def test__get_type_arg(self) -> None:
|
||||||
with self.assertRaises(AttributeError):
|
with self.assertRaises(AttributeError):
|
||||||
_util.GenericInsightMixin._get_type_arg()
|
_util.GenericInsightMixin._get_type_arg(0)
|
||||||
|
|
||||||
_type = object()
|
_type_0 = object()
|
||||||
with patch.object(_util.GenericInsightMixin, "_type_arg", new=_type):
|
_type_1 = object()
|
||||||
self.assertIs(_type, _util.GenericInsightMixin._get_type_arg())
|
_type_2 = object()
|
||||||
|
_type_3 = object()
|
||||||
|
_type_4 = object()
|
||||||
|
with patch.multiple(
|
||||||
|
_util.GenericInsightMixin,
|
||||||
|
_type_arg_0=_type_0,
|
||||||
|
_type_arg_1=_type_1,
|
||||||
|
_type_arg_2=_type_2,
|
||||||
|
_type_arg_3=_type_3,
|
||||||
|
_type_arg_4=_type_4,
|
||||||
|
):
|
||||||
|
self.assertIs(_type_0, _util.GenericInsightMixin._get_type_arg(0))
|
||||||
|
self.assertIs(_type_1, _util.GenericInsightMixin._get_type_arg(1))
|
||||||
|
self.assertIs(_type_2, _util.GenericInsightMixin._get_type_arg(2))
|
||||||
|
self.assertIs(_type_3, _util.GenericInsightMixin._get_type_arg(3))
|
||||||
|
self.assertIs(_type_4, _util.GenericInsightMixin._get_type_arg(4))
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
_util.GenericInsightMixin._get_type_arg(5) # type: ignore[call-overload]
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from typing import Any
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
@ -5,6 +6,29 @@ from marshmallow_generic import _util, schema
|
|||||||
|
|
||||||
|
|
||||||
class GenericSchemaTestCase(TestCase):
|
class GenericSchemaTestCase(TestCase):
|
||||||
|
@patch("marshmallow.schema.Schema.__init__")
|
||||||
|
def test___init__(self, mock_super_init: MagicMock) -> None:
|
||||||
|
class Foo:
|
||||||
|
pass
|
||||||
|
|
||||||
|
kwargs: dict[str, Any] = {
|
||||||
|
"only": object(),
|
||||||
|
"exclude": object(),
|
||||||
|
"context": object(),
|
||||||
|
"load_only": object(),
|
||||||
|
"dump_only": object(),
|
||||||
|
"partial": object(),
|
||||||
|
"unknown": object(),
|
||||||
|
"many": None,
|
||||||
|
}
|
||||||
|
schema.GenericSchema[Foo](**kwargs)
|
||||||
|
mock_super_init.assert_called_once_with(**kwargs | {"many": False})
|
||||||
|
mock_super_init.reset_mock()
|
||||||
|
kwargs["many"] = True
|
||||||
|
with self.assertWarns(UserWarning):
|
||||||
|
schema.GenericSchema[Foo](**kwargs)
|
||||||
|
mock_super_init.assert_called_once_with(**kwargs)
|
||||||
|
|
||||||
@patch.object(_util.GenericInsightMixin, "_get_type_arg")
|
@patch.object(_util.GenericInsightMixin, "_get_type_arg")
|
||||||
def test_instantiate(self, mock__get_type_arg: MagicMock) -> None:
|
def test_instantiate(self, mock__get_type_arg: MagicMock) -> None:
|
||||||
mock__get_type_arg.return_value = mock_cls = MagicMock()
|
mock__get_type_arg.return_value = mock_cls = MagicMock()
|
||||||
@ -17,9 +41,29 @@ class GenericSchemaTestCase(TestCase):
|
|||||||
# Explicit annotation to possibly catch mypy errors:
|
# Explicit annotation to possibly catch mypy errors:
|
||||||
output: Foo = schema_obj.instantiate(mock_data)
|
output: Foo = schema_obj.instantiate(mock_data)
|
||||||
self.assertIs(mock_cls.return_value, output)
|
self.assertIs(mock_cls.return_value, output)
|
||||||
mock__get_type_arg.assert_called_once_with()
|
mock__get_type_arg.assert_called_once_with(0)
|
||||||
mock_cls.assert_called_once_with(**mock_data)
|
mock_cls.assert_called_once_with(**mock_data)
|
||||||
|
|
||||||
|
def test_dump_and_dumps(self) -> None:
|
||||||
|
"""Mainly for static type checking purposes."""
|
||||||
|
|
||||||
|
class Foo:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TestSchema(schema.GenericSchema[Foo]):
|
||||||
|
pass
|
||||||
|
|
||||||
|
foo = Foo()
|
||||||
|
single: dict[str, Any] = TestSchema().dump(foo)
|
||||||
|
self.assertDictEqual({}, single)
|
||||||
|
json_string: str = TestSchema().dumps(foo)
|
||||||
|
self.assertEqual("{}", json_string)
|
||||||
|
|
||||||
|
multiple: list[dict[str, Any]] = TestSchema().dump([foo], many=True)
|
||||||
|
self.assertListEqual([{}], multiple)
|
||||||
|
json_string = TestSchema().dumps([foo], many=True)
|
||||||
|
self.assertEqual("[{}]", json_string)
|
||||||
|
|
||||||
def test_load_and_loads(self) -> None:
|
def test_load_and_loads(self) -> None:
|
||||||
"""Mainly for static type checking purposes."""
|
"""Mainly for static type checking purposes."""
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user