marshmallow 3.26.1__py3-none-any.whl → 4.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- marshmallow/__init__.py +1 -52
- marshmallow/constants.py +22 -0
- marshmallow/decorators.py +56 -40
- marshmallow/exceptions.py +3 -4
- marshmallow/experimental/__init__.py +5 -0
- marshmallow/experimental/context.py +73 -0
- marshmallow/fields.py +385 -456
- marshmallow/orderedset.py +1 -1
- marshmallow/schema.py +135 -197
- marshmallow/types.py +14 -6
- marshmallow/utils.py +17 -227
- marshmallow/validate.py +2 -16
- {marshmallow-3.26.1.dist-info → marshmallow-4.0.1.dist-info}/METADATA +9 -7
- marshmallow-4.0.1.dist-info/RECORD +19 -0
- {marshmallow-3.26.1.dist-info → marshmallow-4.0.1.dist-info}/WHEEL +1 -1
- marshmallow/base.py +0 -61
- marshmallow/warnings.py +0 -10
- marshmallow-3.26.1.dist-info/RECORD +0 -18
- {marshmallow-3.26.1.dist-info → marshmallow-4.0.1.dist-info/licenses}/LICENSE +0 -0
marshmallow/schema.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""The `Schema <marshmallow.Schema>` class, including its metaclass and options (`class Meta <marshmallow.Schema.Meta>`)."""
|
|
2
2
|
|
|
3
|
+
# ruff: noqa: SLF001
|
|
3
4
|
from __future__ import annotations
|
|
4
5
|
|
|
5
6
|
import copy
|
|
@@ -11,14 +12,14 @@ import json
|
|
|
11
12
|
import operator
|
|
12
13
|
import typing
|
|
13
14
|
import uuid
|
|
14
|
-
import warnings
|
|
15
15
|
from abc import ABCMeta
|
|
16
|
-
from collections import
|
|
17
|
-
from collections.abc import Mapping
|
|
16
|
+
from collections import defaultdict
|
|
17
|
+
from collections.abc import Mapping, Sequence
|
|
18
18
|
from itertools import zip_longest
|
|
19
19
|
|
|
20
|
-
from marshmallow import
|
|
20
|
+
from marshmallow import class_registry, types
|
|
21
21
|
from marshmallow import fields as ma_fields
|
|
22
|
+
from marshmallow.constants import EXCLUDE, INCLUDE, RAISE, missing
|
|
22
23
|
from marshmallow.decorators import (
|
|
23
24
|
POST_DUMP,
|
|
24
25
|
POST_LOAD,
|
|
@@ -31,17 +32,11 @@ from marshmallow.error_store import ErrorStore
|
|
|
31
32
|
from marshmallow.exceptions import SCHEMA, StringNotCollectionError, ValidationError
|
|
32
33
|
from marshmallow.orderedset import OrderedSet
|
|
33
34
|
from marshmallow.utils import (
|
|
34
|
-
EXCLUDE,
|
|
35
|
-
INCLUDE,
|
|
36
|
-
RAISE,
|
|
37
35
|
get_value,
|
|
38
36
|
is_collection,
|
|
39
|
-
|
|
40
|
-
missing,
|
|
37
|
+
is_sequence_but_not_string,
|
|
41
38
|
set_value,
|
|
42
|
-
validate_unknown_parameter_value,
|
|
43
39
|
)
|
|
44
|
-
from marshmallow.warnings import RemovedInMarshmallow4Warning
|
|
45
40
|
|
|
46
41
|
if typing.TYPE_CHECKING:
|
|
47
42
|
from marshmallow.fields import Field
|
|
@@ -52,11 +47,17 @@ def _get_fields(attrs) -> list[tuple[str, Field]]:
|
|
|
52
47
|
|
|
53
48
|
:param attrs: Mapping of class attributes
|
|
54
49
|
"""
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
50
|
+
ret = []
|
|
51
|
+
for field_name, field_value in attrs.items():
|
|
52
|
+
if isinstance(field_value, type) and issubclass(field_value, ma_fields.Field):
|
|
53
|
+
raise TypeError(
|
|
54
|
+
f'Field for "{field_name}" must be declared as a '
|
|
55
|
+
"Field instance, not a class. "
|
|
56
|
+
f'Did you mean "fields.{field_value.__name__}()"?'
|
|
57
|
+
)
|
|
58
|
+
if isinstance(field_value, ma_fields.Field):
|
|
59
|
+
ret.append((field_name, field_value))
|
|
60
|
+
return ret
|
|
60
61
|
|
|
61
62
|
|
|
62
63
|
# This function allows Schemas to inherit from non-Schema classes and ensures
|
|
@@ -97,24 +98,12 @@ class SchemaMeta(ABCMeta):
|
|
|
97
98
|
_declared_fields: dict[str, Field]
|
|
98
99
|
|
|
99
100
|
def __new__(
|
|
100
|
-
mcs,
|
|
101
|
+
mcs,
|
|
101
102
|
name: str,
|
|
102
103
|
bases: tuple[type, ...],
|
|
103
104
|
attrs: dict[str, typing.Any],
|
|
104
105
|
) -> SchemaMeta:
|
|
105
106
|
meta = attrs.get("Meta")
|
|
106
|
-
ordered = getattr(meta, "ordered", False)
|
|
107
|
-
if not ordered:
|
|
108
|
-
# Inherit 'ordered' option
|
|
109
|
-
# Warning: We loop through bases instead of MRO because we don't
|
|
110
|
-
# yet have access to the class object
|
|
111
|
-
# (i.e. can't call super before we have fields)
|
|
112
|
-
for base_ in bases:
|
|
113
|
-
if hasattr(base_, "Meta") and hasattr(base_.Meta, "ordered"):
|
|
114
|
-
ordered = base_.Meta.ordered
|
|
115
|
-
break
|
|
116
|
-
else:
|
|
117
|
-
ordered = False
|
|
118
107
|
cls_fields = _get_fields(attrs)
|
|
119
108
|
# Remove fields from list of class attributes to avoid shadowing
|
|
120
109
|
# Schema attributes/methods in case of name conflict
|
|
@@ -126,12 +115,12 @@ class SchemaMeta(ABCMeta):
|
|
|
126
115
|
meta = klass.Meta
|
|
127
116
|
# Set klass.opts in __new__ rather than __init__ so that it is accessible in
|
|
128
117
|
# get_declared_fields
|
|
129
|
-
klass.opts = klass.OPTIONS_CLASS(meta
|
|
118
|
+
klass.opts = klass.OPTIONS_CLASS(meta)
|
|
130
119
|
# Add fields specified in the `include` class Meta option
|
|
131
120
|
cls_fields += list(klass.opts.include.items())
|
|
132
121
|
|
|
133
122
|
# Assign _declared_fields on class
|
|
134
|
-
klass._declared_fields = mcs.get_declared_fields(
|
|
123
|
+
klass._declared_fields = mcs.get_declared_fields(
|
|
135
124
|
klass=klass,
|
|
136
125
|
cls_fields=cls_fields,
|
|
137
126
|
inherited_fields=inherited_fields,
|
|
@@ -212,52 +201,27 @@ class SchemaMeta(ABCMeta):
|
|
|
212
201
|
class SchemaOpts:
|
|
213
202
|
"""Defines defaults for `marshmallow.Schema.Meta`."""
|
|
214
203
|
|
|
215
|
-
def __init__(self, meta: type
|
|
204
|
+
def __init__(self, meta: type):
|
|
216
205
|
self.fields = getattr(meta, "fields", ())
|
|
217
206
|
if not isinstance(self.fields, (list, tuple)):
|
|
218
207
|
raise ValueError("`fields` option must be a list or tuple.")
|
|
219
|
-
self.additional = getattr(meta, "additional", ())
|
|
220
|
-
if not isinstance(self.additional, (list, tuple)):
|
|
221
|
-
raise ValueError("`additional` option must be a list or tuple.")
|
|
222
|
-
if self.fields and self.additional:
|
|
223
|
-
raise ValueError(
|
|
224
|
-
"Cannot set both `fields` and `additional` options for the same Schema."
|
|
225
|
-
)
|
|
226
208
|
self.exclude = getattr(meta, "exclude", ())
|
|
227
209
|
if not isinstance(self.exclude, (list, tuple)):
|
|
228
210
|
raise ValueError("`exclude` must be a list or tuple.")
|
|
229
211
|
self.dateformat = getattr(meta, "dateformat", None)
|
|
230
212
|
self.datetimeformat = getattr(meta, "datetimeformat", None)
|
|
231
213
|
self.timeformat = getattr(meta, "timeformat", None)
|
|
232
|
-
|
|
233
|
-
warnings.warn(
|
|
234
|
-
"The json_module class Meta option is deprecated. Use render_module instead.",
|
|
235
|
-
RemovedInMarshmallow4Warning,
|
|
236
|
-
stacklevel=2,
|
|
237
|
-
)
|
|
238
|
-
render_module = getattr(meta, "json_module", json)
|
|
239
|
-
else:
|
|
240
|
-
render_module = json
|
|
241
|
-
self.render_module = getattr(meta, "render_module", render_module)
|
|
242
|
-
if hasattr(meta, "ordered"):
|
|
243
|
-
warnings.warn(
|
|
244
|
-
"The `ordered` `class Meta` option is deprecated. "
|
|
245
|
-
"Field order is already preserved by default. "
|
|
246
|
-
"Set `Schema.dict_class` to OrderedDict to maintain the previous behavior.",
|
|
247
|
-
RemovedInMarshmallow4Warning,
|
|
248
|
-
stacklevel=2,
|
|
249
|
-
)
|
|
250
|
-
self.ordered = getattr(meta, "ordered", ordered)
|
|
214
|
+
self.render_module = getattr(meta, "render_module", json)
|
|
251
215
|
self.index_errors = getattr(meta, "index_errors", True)
|
|
252
216
|
self.include = getattr(meta, "include", {})
|
|
253
217
|
self.load_only = getattr(meta, "load_only", ())
|
|
254
218
|
self.dump_only = getattr(meta, "dump_only", ())
|
|
255
|
-
self.unknown =
|
|
219
|
+
self.unknown = getattr(meta, "unknown", RAISE)
|
|
256
220
|
self.register = getattr(meta, "register", True)
|
|
257
221
|
self.many = getattr(meta, "many", False)
|
|
258
222
|
|
|
259
223
|
|
|
260
|
-
class Schema(
|
|
224
|
+
class Schema(metaclass=SchemaMeta):
|
|
261
225
|
"""Base schema class with which to define schemas.
|
|
262
226
|
|
|
263
227
|
Example usage:
|
|
@@ -295,8 +259,6 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
295
259
|
delimiters.
|
|
296
260
|
:param many: Should be set to `True` if ``obj`` is a collection
|
|
297
261
|
so that the object will be serialized to a list.
|
|
298
|
-
:param context: Optional context passed to :class:`fields.Method` and
|
|
299
|
-
:class:`fields.Function` fields.
|
|
300
262
|
:param load_only: Fields to skip during serialization (write-only fields)
|
|
301
263
|
:param dump_only: Fields to skip during deserialization (read-only fields)
|
|
302
264
|
:param partial: Whether to ignore missing fields and not require
|
|
@@ -307,7 +269,10 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
307
269
|
fields in the data. Use `EXCLUDE`, `INCLUDE` or `RAISE`.
|
|
308
270
|
|
|
309
271
|
.. versionchanged:: 3.0.0
|
|
310
|
-
|
|
272
|
+
Remove ``prefix`` parameter.
|
|
273
|
+
|
|
274
|
+
.. versionchanged:: 4.0.0
|
|
275
|
+
Remove ``context`` parameter.
|
|
311
276
|
"""
|
|
312
277
|
|
|
313
278
|
TYPE_MAPPING: dict[type, type[Field]] = {
|
|
@@ -337,6 +302,8 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
337
302
|
OPTIONS_CLASS: type = SchemaOpts
|
|
338
303
|
|
|
339
304
|
set_class = OrderedSet
|
|
305
|
+
dict_class: type[dict] = dict
|
|
306
|
+
"""`dict` type to return when serializing."""
|
|
340
307
|
|
|
341
308
|
# These get set by SchemaMeta
|
|
342
309
|
opts: typing.Any
|
|
@@ -381,7 +348,8 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
381
348
|
.. versionadded:: 3.0.0b12 Add `unknown`.
|
|
382
349
|
.. versionchanged:: 3.0.0b17 Rename ``dateformat`` to `datetimeformat`.
|
|
383
350
|
.. versionadded:: 3.9.0 Add `timeformat`.
|
|
384
|
-
.. versionchanged:: 3.26.0 Deprecate
|
|
351
|
+
.. versionchanged:: 3.26.0 Deprecate ``ordered``. Field order is preserved by default.
|
|
352
|
+
.. versionremoved:: 4.0.0 Remove ``ordered``.
|
|
385
353
|
"""
|
|
386
354
|
|
|
387
355
|
fields: typing.ClassVar[tuple[str, ...] | list[str]]
|
|
@@ -415,15 +383,13 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
415
383
|
""" Module to use for `loads <marshmallow.Schema.loads>` and `dumps <marshmallow.Schema.dumps>`.
|
|
416
384
|
Defaults to `json` from the standard library.
|
|
417
385
|
"""
|
|
418
|
-
ordered: typing.ClassVar[bool]
|
|
419
|
-
"""If `True`, `Schema.dump <marshmallow.Schema.dump>` is a `collections.OrderedDict`."""
|
|
420
386
|
index_errors: typing.ClassVar[bool]
|
|
421
387
|
"""If `True`, errors dictionaries will include the index of invalid items in a collection."""
|
|
422
388
|
load_only: typing.ClassVar[tuple[str, ...] | list[str]]
|
|
423
389
|
"""Fields to exclude from serialized results"""
|
|
424
390
|
dump_only: typing.ClassVar[tuple[str, ...] | list[str]]
|
|
425
391
|
"""Fields to exclude from serialized results"""
|
|
426
|
-
unknown: typing.ClassVar[
|
|
392
|
+
unknown: typing.ClassVar[types.UnknownOption]
|
|
427
393
|
"""Whether to exclude, include, or raise an error for unknown fields in the data.
|
|
428
394
|
Use `EXCLUDE`, `INCLUDE` or `RAISE`.
|
|
429
395
|
"""
|
|
@@ -440,11 +406,10 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
440
406
|
only: types.StrSequenceOrSet | None = None,
|
|
441
407
|
exclude: types.StrSequenceOrSet = (),
|
|
442
408
|
many: bool | None = None,
|
|
443
|
-
context: dict | None = None,
|
|
444
409
|
load_only: types.StrSequenceOrSet = (),
|
|
445
410
|
dump_only: types.StrSequenceOrSet = (),
|
|
446
411
|
partial: bool | types.StrSequenceOrSet | None = None,
|
|
447
|
-
unknown:
|
|
412
|
+
unknown: types.UnknownOption | None = None,
|
|
448
413
|
):
|
|
449
414
|
# Raise error if only or exclude is passed as string, not list of strings
|
|
450
415
|
if only is not None and not is_collection(only):
|
|
@@ -458,23 +423,12 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
458
423
|
self.exclude: set[typing.Any] | typing.MutableSet[typing.Any] = set(
|
|
459
424
|
self.opts.exclude
|
|
460
425
|
) | set(exclude)
|
|
461
|
-
self.ordered = self.opts.ordered
|
|
462
426
|
self.load_only = set(load_only) or set(self.opts.load_only)
|
|
463
427
|
self.dump_only = set(dump_only) or set(self.opts.dump_only)
|
|
464
428
|
self.partial = partial
|
|
465
|
-
self.unknown = (
|
|
466
|
-
self.opts.unknown
|
|
467
|
-
if unknown is None
|
|
468
|
-
else validate_unknown_parameter_value(unknown)
|
|
429
|
+
self.unknown: types.UnknownOption = (
|
|
430
|
+
self.opts.unknown if unknown is None else unknown
|
|
469
431
|
)
|
|
470
|
-
if context:
|
|
471
|
-
warnings.warn(
|
|
472
|
-
"The `context` parameter is deprecated and will be removed in marshmallow 4.0. "
|
|
473
|
-
"Use `contextvars.ContextVar` to pass context instead.",
|
|
474
|
-
RemovedInMarshmallow4Warning,
|
|
475
|
-
stacklevel=2,
|
|
476
|
-
)
|
|
477
|
-
self.context = context or {}
|
|
478
432
|
self._normalize_nested_options()
|
|
479
433
|
#: Dictionary mapping field_names -> :class:`Field` objects
|
|
480
434
|
self.fields: dict[str, Field] = {}
|
|
@@ -491,13 +445,6 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
491
445
|
def __repr__(self) -> str:
|
|
492
446
|
return f"<{self.__class__.__name__}(many={self.many})>"
|
|
493
447
|
|
|
494
|
-
@property
|
|
495
|
-
def dict_class(self) -> type[dict]:
|
|
496
|
-
"""`dict` type to return when serializing."""
|
|
497
|
-
if self.ordered:
|
|
498
|
-
return OrderedDict
|
|
499
|
-
return dict
|
|
500
|
-
|
|
501
448
|
@classmethod
|
|
502
449
|
def from_dict(
|
|
503
450
|
cls,
|
|
@@ -602,9 +549,8 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
602
549
|
for `self.many` is used.
|
|
603
550
|
:return: Serialized data
|
|
604
551
|
|
|
605
|
-
.. versionadded:: 1.0.0
|
|
606
552
|
.. versionchanged:: 3.0.0b7
|
|
607
|
-
This method returns the serialized data rather than a ``(data, errors)``
|
|
553
|
+
This method returns the serialized data rather than a ``(data, errors)`` tuple.
|
|
608
554
|
A :exc:`ValidationError <marshmallow.exceptions.ValidationError>` is raised
|
|
609
555
|
if ``obj`` is invalid.
|
|
610
556
|
.. versionchanged:: 3.0.0rc9
|
|
@@ -635,9 +581,8 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
635
581
|
for `self.many` is used.
|
|
636
582
|
:return: A ``json`` string
|
|
637
583
|
|
|
638
|
-
.. versionadded:: 1.0.0
|
|
639
584
|
.. versionchanged:: 3.0.0b7
|
|
640
|
-
This method returns the serialized data rather than a ``(data, errors)``
|
|
585
|
+
This method returns the serialized data rather than a ``(data, errors)`` tuple.
|
|
641
586
|
A :exc:`ValidationError <marshmallow.exceptions.ValidationError>` is raised
|
|
642
587
|
if ``obj`` is invalid.
|
|
643
588
|
"""
|
|
@@ -646,15 +591,12 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
646
591
|
|
|
647
592
|
def _deserialize(
|
|
648
593
|
self,
|
|
649
|
-
data:
|
|
650
|
-
typing.Mapping[str, typing.Any]
|
|
651
|
-
| typing.Iterable[typing.Mapping[str, typing.Any]]
|
|
652
|
-
),
|
|
594
|
+
data: Mapping[str, typing.Any] | Sequence[Mapping[str, typing.Any]],
|
|
653
595
|
*,
|
|
654
596
|
error_store: ErrorStore,
|
|
655
597
|
many: bool = False,
|
|
656
598
|
partial=None,
|
|
657
|
-
unknown=RAISE,
|
|
599
|
+
unknown: types.UnknownOption = RAISE,
|
|
658
600
|
index=None,
|
|
659
601
|
) -> typing.Any | list[typing.Any]:
|
|
660
602
|
"""Deserialize ``data``.
|
|
@@ -676,13 +618,13 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
676
618
|
index_errors = self.opts.index_errors
|
|
677
619
|
index = index if index_errors else None
|
|
678
620
|
if many:
|
|
679
|
-
if not
|
|
621
|
+
if not is_sequence_but_not_string(data):
|
|
680
622
|
error_store.store_error([self.error_messages["type"]], index=index)
|
|
681
623
|
ret_l = []
|
|
682
624
|
else:
|
|
683
625
|
ret_l = [
|
|
684
626
|
self._deserialize(
|
|
685
|
-
|
|
627
|
+
d,
|
|
686
628
|
error_store=error_store,
|
|
687
629
|
many=False,
|
|
688
630
|
partial=partial,
|
|
@@ -760,14 +702,11 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
760
702
|
|
|
761
703
|
def load(
|
|
762
704
|
self,
|
|
763
|
-
data:
|
|
764
|
-
typing.Mapping[str, typing.Any]
|
|
765
|
-
| typing.Iterable[typing.Mapping[str, typing.Any]]
|
|
766
|
-
),
|
|
705
|
+
data: Mapping[str, typing.Any] | Sequence[Mapping[str, typing.Any]],
|
|
767
706
|
*,
|
|
768
707
|
many: bool | None = None,
|
|
769
708
|
partial: bool | types.StrSequenceOrSet | None = None,
|
|
770
|
-
unknown:
|
|
709
|
+
unknown: types.UnknownOption | None = None,
|
|
771
710
|
):
|
|
772
711
|
"""Deserialize a data structure to an object defined by this Schema's fields.
|
|
773
712
|
|
|
@@ -783,9 +722,8 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
783
722
|
If `None`, the value for `self.unknown` is used.
|
|
784
723
|
:return: Deserialized data
|
|
785
724
|
|
|
786
|
-
.. versionadded:: 1.0.0
|
|
787
725
|
.. versionchanged:: 3.0.0b7
|
|
788
|
-
This method returns the deserialized data rather than a ``(data, errors)``
|
|
726
|
+
This method returns the deserialized data rather than a ``(data, errors)`` tuple.
|
|
789
727
|
A :exc:`ValidationError <marshmallow.exceptions.ValidationError>` is raised
|
|
790
728
|
if invalid data are passed.
|
|
791
729
|
"""
|
|
@@ -795,17 +733,18 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
795
733
|
|
|
796
734
|
def loads(
|
|
797
735
|
self,
|
|
798
|
-
|
|
736
|
+
s: str | bytes | bytearray,
|
|
737
|
+
/,
|
|
799
738
|
*,
|
|
800
739
|
many: bool | None = None,
|
|
801
740
|
partial: bool | types.StrSequenceOrSet | None = None,
|
|
802
|
-
unknown:
|
|
741
|
+
unknown: types.UnknownOption | None = None,
|
|
803
742
|
**kwargs,
|
|
804
743
|
):
|
|
805
744
|
"""Same as :meth:`load`, except it uses `marshmallow.Schema.Meta.render_module` to deserialize
|
|
806
745
|
the passed string before passing data to :meth:`load`.
|
|
807
746
|
|
|
808
|
-
:param
|
|
747
|
+
:param s: A string of the data to deserialize.
|
|
809
748
|
:param many: Whether to deserialize `obj` as a collection. If `None`, the
|
|
810
749
|
value for `self.many` is used.
|
|
811
750
|
:param partial: Whether to ignore missing fields and not require
|
|
@@ -817,13 +756,14 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
817
756
|
If `None`, the value for `self.unknown` is used.
|
|
818
757
|
:return: Deserialized data
|
|
819
758
|
|
|
820
|
-
.. versionadded:: 1.0.0
|
|
821
759
|
.. versionchanged:: 3.0.0b7
|
|
822
|
-
This method returns the deserialized data rather than a ``(data, errors)``
|
|
760
|
+
This method returns the deserialized data rather than a ``(data, errors)`` tuple.
|
|
823
761
|
A :exc:`ValidationError <marshmallow.exceptions.ValidationError>` is raised
|
|
824
762
|
if invalid data are passed.
|
|
763
|
+
.. versionchanged:: 4.0.0
|
|
764
|
+
Rename ``json_module`` parameter to ``s``.
|
|
825
765
|
"""
|
|
826
|
-
data = self.opts.render_module.loads(
|
|
766
|
+
data = self.opts.render_module.loads(s, **kwargs)
|
|
827
767
|
return self.load(data, many=many, partial=partial, unknown=unknown)
|
|
828
768
|
|
|
829
769
|
def _run_validator(
|
|
@@ -835,14 +775,17 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
835
775
|
error_store: ErrorStore,
|
|
836
776
|
many: bool,
|
|
837
777
|
partial: bool | types.StrSequenceOrSet | None,
|
|
778
|
+
unknown: types.UnknownOption | None,
|
|
838
779
|
pass_original: bool,
|
|
839
780
|
index: int | None = None,
|
|
840
781
|
):
|
|
841
782
|
try:
|
|
842
783
|
if pass_original: # Pass original, raw data (before unmarshalling)
|
|
843
|
-
validator_func(
|
|
784
|
+
validator_func(
|
|
785
|
+
output, original_data, partial=partial, many=many, unknown=unknown
|
|
786
|
+
)
|
|
844
787
|
else:
|
|
845
|
-
validator_func(output, partial=partial, many=many)
|
|
788
|
+
validator_func(output, partial=partial, many=many, unknown=unknown)
|
|
846
789
|
except ValidationError as err:
|
|
847
790
|
field_name = err.field_name
|
|
848
791
|
data_key: str
|
|
@@ -867,10 +810,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
867
810
|
|
|
868
811
|
def validate(
|
|
869
812
|
self,
|
|
870
|
-
data:
|
|
871
|
-
typing.Mapping[str, typing.Any]
|
|
872
|
-
| typing.Iterable[typing.Mapping[str, typing.Any]]
|
|
873
|
-
),
|
|
813
|
+
data: Mapping[str, typing.Any] | Sequence[Mapping[str, typing.Any]],
|
|
874
814
|
*,
|
|
875
815
|
many: bool | None = None,
|
|
876
816
|
partial: bool | types.StrSequenceOrSet | None = None,
|
|
@@ -886,27 +826,22 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
886
826
|
its value is an iterable, only missing fields listed in that iterable
|
|
887
827
|
will be ignored. Use dot delimiters to specify nested fields.
|
|
888
828
|
:return: A dictionary of validation errors.
|
|
889
|
-
|
|
890
|
-
.. versionadded:: 1.1.0
|
|
891
829
|
"""
|
|
892
830
|
try:
|
|
893
831
|
self._do_load(data, many=many, partial=partial, postprocess=False)
|
|
894
832
|
except ValidationError as exc:
|
|
895
|
-
return typing.cast(dict[str, list[str]], exc.messages)
|
|
833
|
+
return typing.cast("dict[str, list[str]]", exc.messages)
|
|
896
834
|
return {}
|
|
897
835
|
|
|
898
836
|
##### Private Helpers #####
|
|
899
837
|
|
|
900
838
|
def _do_load(
|
|
901
839
|
self,
|
|
902
|
-
data: (
|
|
903
|
-
typing.Mapping[str, typing.Any]
|
|
904
|
-
| typing.Iterable[typing.Mapping[str, typing.Any]]
|
|
905
|
-
),
|
|
840
|
+
data: (Mapping[str, typing.Any] | Sequence[Mapping[str, typing.Any]]),
|
|
906
841
|
*,
|
|
907
842
|
many: bool | None = None,
|
|
908
843
|
partial: bool | types.StrSequenceOrSet | None = None,
|
|
909
|
-
unknown:
|
|
844
|
+
unknown: types.UnknownOption | None = None,
|
|
910
845
|
postprocess: bool = True,
|
|
911
846
|
):
|
|
912
847
|
"""Deserialize `data`, returning the deserialized result.
|
|
@@ -928,18 +863,19 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
928
863
|
error_store = ErrorStore()
|
|
929
864
|
errors: dict[str, list[str]] = {}
|
|
930
865
|
many = self.many if many is None else bool(many)
|
|
931
|
-
unknown =
|
|
932
|
-
self.unknown
|
|
933
|
-
if unknown is None
|
|
934
|
-
else validate_unknown_parameter_value(unknown)
|
|
935
|
-
)
|
|
866
|
+
unknown = self.unknown if unknown is None else unknown
|
|
936
867
|
if partial is None:
|
|
937
868
|
partial = self.partial
|
|
938
869
|
# Run preprocessors
|
|
939
870
|
if self._hooks[PRE_LOAD]:
|
|
940
871
|
try:
|
|
941
872
|
processed_data = self._invoke_load_processors(
|
|
942
|
-
PRE_LOAD,
|
|
873
|
+
PRE_LOAD,
|
|
874
|
+
data,
|
|
875
|
+
many=many,
|
|
876
|
+
original_data=data,
|
|
877
|
+
partial=partial,
|
|
878
|
+
unknown=unknown,
|
|
943
879
|
)
|
|
944
880
|
except ValidationError as err:
|
|
945
881
|
errors = err.normalized_messages()
|
|
@@ -964,20 +900,22 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
964
900
|
field_errors = bool(error_store.errors)
|
|
965
901
|
self._invoke_schema_validators(
|
|
966
902
|
error_store=error_store,
|
|
967
|
-
|
|
903
|
+
pass_collection=True,
|
|
968
904
|
data=result,
|
|
969
905
|
original_data=data,
|
|
970
906
|
many=many,
|
|
971
907
|
partial=partial,
|
|
908
|
+
unknown=unknown,
|
|
972
909
|
field_errors=field_errors,
|
|
973
910
|
)
|
|
974
911
|
self._invoke_schema_validators(
|
|
975
912
|
error_store=error_store,
|
|
976
|
-
|
|
913
|
+
pass_collection=False,
|
|
977
914
|
data=result,
|
|
978
915
|
original_data=data,
|
|
979
916
|
many=many,
|
|
980
917
|
partial=partial,
|
|
918
|
+
unknown=unknown,
|
|
981
919
|
field_errors=field_errors,
|
|
982
920
|
)
|
|
983
921
|
errors = error_store.errors
|
|
@@ -990,6 +928,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
990
928
|
many=many,
|
|
991
929
|
original_data=data,
|
|
992
930
|
partial=partial,
|
|
931
|
+
unknown=unknown,
|
|
993
932
|
)
|
|
994
933
|
except ValidationError as err:
|
|
995
934
|
errors = err.normalized_messages()
|
|
@@ -1044,8 +983,6 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
1044
983
|
available_field_names = self.set_class(self.opts.fields)
|
|
1045
984
|
else:
|
|
1046
985
|
available_field_names = self.set_class(self.declared_fields.keys())
|
|
1047
|
-
if self.opts.additional:
|
|
1048
|
-
available_field_names |= self.set_class(self.opts.additional)
|
|
1049
986
|
|
|
1050
987
|
invalid_fields = self.set_class()
|
|
1051
988
|
|
|
@@ -1070,7 +1007,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
1070
1007
|
|
|
1071
1008
|
fields_dict = self.dict_class()
|
|
1072
1009
|
for field_name in field_names:
|
|
1073
|
-
field_obj = self.declared_fields
|
|
1010
|
+
field_obj = self.declared_fields[field_name]
|
|
1074
1011
|
self._bind_field(field_name, field_obj)
|
|
1075
1012
|
fields_dict[field_name] = field_obj
|
|
1076
1013
|
|
|
@@ -1129,129 +1066,128 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
1129
1066
|
field_obj.load_only = True
|
|
1130
1067
|
if field_name in self.dump_only:
|
|
1131
1068
|
field_obj.dump_only = True
|
|
1132
|
-
|
|
1133
|
-
field_obj._bind_to_schema(field_name, self) # noqa: SLF001
|
|
1134
|
-
except TypeError as error:
|
|
1135
|
-
# Field declared as a class, not an instance. Ignore type checking because
|
|
1136
|
-
# we handle unsupported arg types, i.e. this is dead code from
|
|
1137
|
-
# the type checker's perspective.
|
|
1138
|
-
if isinstance(field_obj, type) and issubclass(field_obj, base.FieldABC):
|
|
1139
|
-
msg = (
|
|
1140
|
-
f'Field for "{field_name}" must be declared as a '
|
|
1141
|
-
"Field instance, not a class. "
|
|
1142
|
-
f'Did you mean "fields.{field_obj.__name__}()"?' # type: ignore[attr-defined]
|
|
1143
|
-
)
|
|
1144
|
-
raise TypeError(msg) from error
|
|
1145
|
-
raise
|
|
1069
|
+
field_obj._bind_to_schema(field_name, self)
|
|
1146
1070
|
self.on_bind_field(field_name, field_obj)
|
|
1147
1071
|
|
|
1148
1072
|
def _invoke_dump_processors(
|
|
1149
1073
|
self, tag: str, data, *, many: bool, original_data=None
|
|
1150
1074
|
):
|
|
1151
|
-
# The
|
|
1152
|
-
# invoke those after invoking the non-
|
|
1075
|
+
# The pass_collection post-dump processors may do things like add an envelope, so
|
|
1076
|
+
# invoke those after invoking the non-pass_collection processors which will expect
|
|
1153
1077
|
# to get a list of items.
|
|
1154
1078
|
data = self._invoke_processors(
|
|
1155
|
-
tag,
|
|
1079
|
+
tag,
|
|
1080
|
+
pass_collection=False,
|
|
1081
|
+
data=data,
|
|
1082
|
+
many=many,
|
|
1083
|
+
original_data=original_data,
|
|
1156
1084
|
)
|
|
1157
1085
|
return self._invoke_processors(
|
|
1158
|
-
tag,
|
|
1086
|
+
tag, pass_collection=True, data=data, many=many, original_data=original_data
|
|
1159
1087
|
)
|
|
1160
1088
|
|
|
1161
1089
|
def _invoke_load_processors(
|
|
1162
1090
|
self,
|
|
1163
1091
|
tag: str,
|
|
1164
|
-
data,
|
|
1092
|
+
data: Mapping[str, typing.Any] | Sequence[Mapping[str, typing.Any]],
|
|
1165
1093
|
*,
|
|
1166
1094
|
many: bool,
|
|
1167
1095
|
original_data,
|
|
1168
1096
|
partial: bool | types.StrSequenceOrSet | None,
|
|
1097
|
+
unknown: types.UnknownOption | None,
|
|
1169
1098
|
):
|
|
1170
|
-
# This has to invert the order of the dump processors, so run the
|
|
1099
|
+
# This has to invert the order of the dump processors, so run the pass_collection
|
|
1171
1100
|
# processors first.
|
|
1172
1101
|
data = self._invoke_processors(
|
|
1173
1102
|
tag,
|
|
1174
|
-
|
|
1103
|
+
pass_collection=True,
|
|
1175
1104
|
data=data,
|
|
1176
1105
|
many=many,
|
|
1177
1106
|
original_data=original_data,
|
|
1178
1107
|
partial=partial,
|
|
1108
|
+
unknown=unknown,
|
|
1179
1109
|
)
|
|
1180
1110
|
return self._invoke_processors(
|
|
1181
1111
|
tag,
|
|
1182
|
-
|
|
1112
|
+
pass_collection=False,
|
|
1183
1113
|
data=data,
|
|
1184
1114
|
many=many,
|
|
1185
1115
|
original_data=original_data,
|
|
1186
1116
|
partial=partial,
|
|
1117
|
+
unknown=unknown,
|
|
1187
1118
|
)
|
|
1188
1119
|
|
|
1189
1120
|
def _invoke_field_validators(self, *, error_store: ErrorStore, data, many: bool):
|
|
1190
1121
|
for attr_name, _, validator_kwargs in self._hooks[VALIDATES]:
|
|
1191
1122
|
validator = getattr(self, attr_name)
|
|
1192
|
-
field_name = validator_kwargs["field_name"]
|
|
1193
1123
|
|
|
1194
|
-
|
|
1195
|
-
field_obj = self.fields[field_name]
|
|
1196
|
-
except KeyError as error:
|
|
1197
|
-
if field_name in self.declared_fields:
|
|
1198
|
-
continue
|
|
1199
|
-
raise ValueError(f'"{field_name}" field does not exist.') from error
|
|
1124
|
+
field_names = validator_kwargs["field_names"]
|
|
1200
1125
|
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1126
|
+
for field_name in field_names:
|
|
1127
|
+
try:
|
|
1128
|
+
field_obj = self.fields[field_name]
|
|
1129
|
+
except KeyError as error:
|
|
1130
|
+
if field_name in self.declared_fields:
|
|
1131
|
+
continue
|
|
1132
|
+
raise ValueError(f'"{field_name}" field does not exist.') from error
|
|
1133
|
+
|
|
1134
|
+
data_key = (
|
|
1135
|
+
field_obj.data_key if field_obj.data_key is not None else field_name
|
|
1136
|
+
)
|
|
1137
|
+
do_validate = functools.partial(validator, data_key=data_key)
|
|
1138
|
+
|
|
1139
|
+
if many:
|
|
1140
|
+
for idx, item in enumerate(data):
|
|
1141
|
+
try:
|
|
1142
|
+
value = item[field_obj.attribute or field_name]
|
|
1143
|
+
except KeyError:
|
|
1144
|
+
pass
|
|
1145
|
+
else:
|
|
1146
|
+
validated_value = self._call_and_store(
|
|
1147
|
+
getter_func=do_validate,
|
|
1148
|
+
data=value,
|
|
1149
|
+
field_name=data_key,
|
|
1150
|
+
error_store=error_store,
|
|
1151
|
+
index=(idx if self.opts.index_errors else None),
|
|
1152
|
+
)
|
|
1153
|
+
if validated_value is missing:
|
|
1154
|
+
item.pop(field_name, None)
|
|
1155
|
+
else:
|
|
1206
1156
|
try:
|
|
1207
|
-
value =
|
|
1157
|
+
value = data[field_obj.attribute or field_name]
|
|
1208
1158
|
except KeyError:
|
|
1209
1159
|
pass
|
|
1210
1160
|
else:
|
|
1211
1161
|
validated_value = self._call_and_store(
|
|
1212
|
-
getter_func=
|
|
1162
|
+
getter_func=do_validate,
|
|
1213
1163
|
data=value,
|
|
1214
1164
|
field_name=data_key,
|
|
1215
1165
|
error_store=error_store,
|
|
1216
|
-
index=(idx if self.opts.index_errors else None),
|
|
1217
1166
|
)
|
|
1218
1167
|
if validated_value is missing:
|
|
1219
|
-
|
|
1220
|
-
else:
|
|
1221
|
-
try:
|
|
1222
|
-
value = data[field_obj.attribute or field_name]
|
|
1223
|
-
except KeyError:
|
|
1224
|
-
pass
|
|
1225
|
-
else:
|
|
1226
|
-
validated_value = self._call_and_store(
|
|
1227
|
-
getter_func=validator,
|
|
1228
|
-
data=value,
|
|
1229
|
-
field_name=data_key,
|
|
1230
|
-
error_store=error_store,
|
|
1231
|
-
)
|
|
1232
|
-
if validated_value is missing:
|
|
1233
|
-
data.pop(field_name, None)
|
|
1168
|
+
data.pop(field_name, None)
|
|
1234
1169
|
|
|
1235
1170
|
def _invoke_schema_validators(
|
|
1236
1171
|
self,
|
|
1237
1172
|
*,
|
|
1238
1173
|
error_store: ErrorStore,
|
|
1239
|
-
|
|
1174
|
+
pass_collection: bool,
|
|
1240
1175
|
data,
|
|
1241
1176
|
original_data,
|
|
1242
1177
|
many: bool,
|
|
1243
1178
|
partial: bool | types.StrSequenceOrSet | None,
|
|
1244
1179
|
field_errors: bool = False,
|
|
1180
|
+
unknown: types.UnknownOption | None,
|
|
1245
1181
|
):
|
|
1246
1182
|
for attr_name, hook_many, validator_kwargs in self._hooks[VALIDATES_SCHEMA]:
|
|
1247
|
-
if hook_many !=
|
|
1183
|
+
if hook_many != pass_collection:
|
|
1248
1184
|
continue
|
|
1249
1185
|
validator = getattr(self, attr_name)
|
|
1250
1186
|
if field_errors and validator_kwargs["skip_on_field_errors"]:
|
|
1251
1187
|
continue
|
|
1252
1188
|
pass_original = validator_kwargs.get("pass_original", False)
|
|
1253
1189
|
|
|
1254
|
-
if many and not
|
|
1190
|
+
if many and not pass_collection:
|
|
1255
1191
|
for idx, (item, orig) in enumerate(zip(data, original_data)):
|
|
1256
1192
|
self._run_validator(
|
|
1257
1193
|
validator,
|
|
@@ -1260,6 +1196,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
1260
1196
|
error_store=error_store,
|
|
1261
1197
|
many=many,
|
|
1262
1198
|
partial=partial,
|
|
1199
|
+
unknown=unknown,
|
|
1263
1200
|
index=idx,
|
|
1264
1201
|
pass_original=pass_original,
|
|
1265
1202
|
)
|
|
@@ -1272,26 +1209,27 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
1272
1209
|
many=many,
|
|
1273
1210
|
pass_original=pass_original,
|
|
1274
1211
|
partial=partial,
|
|
1212
|
+
unknown=unknown,
|
|
1275
1213
|
)
|
|
1276
1214
|
|
|
1277
1215
|
def _invoke_processors(
|
|
1278
1216
|
self,
|
|
1279
1217
|
tag: str,
|
|
1280
1218
|
*,
|
|
1281
|
-
|
|
1282
|
-
data,
|
|
1219
|
+
pass_collection: bool,
|
|
1220
|
+
data: Mapping[str, typing.Any] | Sequence[Mapping[str, typing.Any]],
|
|
1283
1221
|
many: bool,
|
|
1284
1222
|
original_data=None,
|
|
1285
1223
|
**kwargs,
|
|
1286
1224
|
):
|
|
1287
1225
|
for attr_name, hook_many, processor_kwargs in self._hooks[tag]:
|
|
1288
|
-
if hook_many !=
|
|
1226
|
+
if hook_many != pass_collection:
|
|
1289
1227
|
continue
|
|
1290
1228
|
# This will be a bound method.
|
|
1291
1229
|
processor = getattr(self, attr_name)
|
|
1292
1230
|
pass_original = processor_kwargs.get("pass_original", False)
|
|
1293
1231
|
|
|
1294
|
-
if many and not
|
|
1232
|
+
if many and not pass_collection:
|
|
1295
1233
|
if pass_original:
|
|
1296
1234
|
data = [
|
|
1297
1235
|
processor(item, original, many=many, **kwargs)
|