marshmallow 3.26.0__py3-none-any.whl → 4.0.0__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 -51
- marshmallow/constants.py +22 -0
- marshmallow/decorators.py +56 -40
- marshmallow/exceptions.py +3 -3
- marshmallow/experimental/__init__.py +5 -0
- marshmallow/experimental/context.py +73 -0
- marshmallow/fields.py +379 -452
- marshmallow/schema.py +140 -199
- marshmallow/types.py +14 -6
- marshmallow/utils.py +17 -226
- marshmallow/validate.py +2 -16
- {marshmallow-3.26.0.dist-info → marshmallow-4.0.0.dist-info}/METADATA +8 -6
- marshmallow-4.0.0.dist-info/RECORD +19 -0
- {marshmallow-3.26.0.dist-info → marshmallow-4.0.0.dist-info}/WHEEL +1 -1
- marshmallow/base.py +0 -61
- marshmallow/warnings.py +0 -10
- marshmallow-3.26.0.dist-info/RECORD +0 -18
- {marshmallow-3.26.0.dist-info → marshmallow-4.0.0.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,12 +348,13 @@ 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
|
-
fields: typing.ClassVar[tuple[
|
|
355
|
+
fields: typing.ClassVar[tuple[str, ...] | list[str]]
|
|
388
356
|
"""Fields to include in the (de)serialized result"""
|
|
389
|
-
additional: typing.ClassVar[tuple[
|
|
357
|
+
additional: typing.ClassVar[tuple[str, ...] | list[str]]
|
|
390
358
|
"""Fields to include in addition to the explicitly declared fields.
|
|
391
359
|
`additional <marshmallow.Schema.Meta.additional>` and `fields <marshmallow.Schema.Meta.fields>`
|
|
392
360
|
are mutually-exclusive options.
|
|
@@ -396,7 +364,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
396
364
|
usually better to define fields as class variables, but you may need to
|
|
397
365
|
use this option, e.g., if your fields are Python keywords.
|
|
398
366
|
"""
|
|
399
|
-
exclude: typing.ClassVar[tuple[
|
|
367
|
+
exclude: typing.ClassVar[tuple[str, ...] | list[str]]
|
|
400
368
|
"""Fields to exclude in the serialized result.
|
|
401
369
|
Nested fields can be represented with dot delimiters.
|
|
402
370
|
"""
|
|
@@ -408,19 +376,20 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
408
376
|
"""Default format for `DateTime <marshmallow.fields.DateTime>` fields."""
|
|
409
377
|
timeformat: typing.ClassVar[str]
|
|
410
378
|
"""Default format for `Time <marshmallow.fields.Time>` fields."""
|
|
411
|
-
|
|
379
|
+
|
|
380
|
+
# FIXME: Use a more constrained type here.
|
|
381
|
+
# ClassVar[RenderModule] doesn't work.
|
|
382
|
+
render_module: typing.Any
|
|
412
383
|
""" Module to use for `loads <marshmallow.Schema.loads>` and `dumps <marshmallow.Schema.dumps>`.
|
|
413
384
|
Defaults to `json` from the standard library.
|
|
414
385
|
"""
|
|
415
|
-
ordered: typing.ClassVar[bool]
|
|
416
|
-
"""If `True`, `Schema.dump <marshmallow.Schema.dump>` is a `collections.OrderedDict`."""
|
|
417
386
|
index_errors: typing.ClassVar[bool]
|
|
418
387
|
"""If `True`, errors dictionaries will include the index of invalid items in a collection."""
|
|
419
|
-
load_only: typing.ClassVar[tuple[
|
|
388
|
+
load_only: typing.ClassVar[tuple[str, ...] | list[str]]
|
|
420
389
|
"""Fields to exclude from serialized results"""
|
|
421
|
-
dump_only: typing.ClassVar[tuple[
|
|
390
|
+
dump_only: typing.ClassVar[tuple[str, ...] | list[str]]
|
|
422
391
|
"""Fields to exclude from serialized results"""
|
|
423
|
-
unknown: typing.ClassVar[
|
|
392
|
+
unknown: typing.ClassVar[types.UnknownOption]
|
|
424
393
|
"""Whether to exclude, include, or raise an error for unknown fields in the data.
|
|
425
394
|
Use `EXCLUDE`, `INCLUDE` or `RAISE`.
|
|
426
395
|
"""
|
|
@@ -437,11 +406,10 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
437
406
|
only: types.StrSequenceOrSet | None = None,
|
|
438
407
|
exclude: types.StrSequenceOrSet = (),
|
|
439
408
|
many: bool | None = None,
|
|
440
|
-
context: dict | None = None,
|
|
441
409
|
load_only: types.StrSequenceOrSet = (),
|
|
442
410
|
dump_only: types.StrSequenceOrSet = (),
|
|
443
411
|
partial: bool | types.StrSequenceOrSet | None = None,
|
|
444
|
-
unknown:
|
|
412
|
+
unknown: types.UnknownOption | None = None,
|
|
445
413
|
):
|
|
446
414
|
# Raise error if only or exclude is passed as string, not list of strings
|
|
447
415
|
if only is not None and not is_collection(only):
|
|
@@ -455,23 +423,12 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
455
423
|
self.exclude: set[typing.Any] | typing.MutableSet[typing.Any] = set(
|
|
456
424
|
self.opts.exclude
|
|
457
425
|
) | set(exclude)
|
|
458
|
-
self.ordered = self.opts.ordered
|
|
459
426
|
self.load_only = set(load_only) or set(self.opts.load_only)
|
|
460
427
|
self.dump_only = set(dump_only) or set(self.opts.dump_only)
|
|
461
428
|
self.partial = partial
|
|
462
|
-
self.unknown = (
|
|
463
|
-
self.opts.unknown
|
|
464
|
-
if unknown is None
|
|
465
|
-
else validate_unknown_parameter_value(unknown)
|
|
429
|
+
self.unknown: types.UnknownOption = (
|
|
430
|
+
self.opts.unknown if unknown is None else unknown
|
|
466
431
|
)
|
|
467
|
-
if context:
|
|
468
|
-
warnings.warn(
|
|
469
|
-
"The `context` parameter is deprecated and will be removed in marshmallow 4.0. "
|
|
470
|
-
"Use `contextvars.ContextVar` to pass context instead.",
|
|
471
|
-
RemovedInMarshmallow4Warning,
|
|
472
|
-
stacklevel=2,
|
|
473
|
-
)
|
|
474
|
-
self.context = context or {}
|
|
475
432
|
self._normalize_nested_options()
|
|
476
433
|
#: Dictionary mapping field_names -> :class:`Field` objects
|
|
477
434
|
self.fields: dict[str, Field] = {}
|
|
@@ -488,13 +445,6 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
488
445
|
def __repr__(self) -> str:
|
|
489
446
|
return f"<{self.__class__.__name__}(many={self.many})>"
|
|
490
447
|
|
|
491
|
-
@property
|
|
492
|
-
def dict_class(self) -> type[dict]:
|
|
493
|
-
"""`dict` type to return when serializing."""
|
|
494
|
-
if self.ordered:
|
|
495
|
-
return OrderedDict
|
|
496
|
-
return dict
|
|
497
|
-
|
|
498
448
|
@classmethod
|
|
499
449
|
def from_dict(
|
|
500
450
|
cls,
|
|
@@ -599,7 +549,6 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
599
549
|
for `self.many` is used.
|
|
600
550
|
:return: Serialized data
|
|
601
551
|
|
|
602
|
-
.. versionadded:: 1.0.0
|
|
603
552
|
.. versionchanged:: 3.0.0b7
|
|
604
553
|
This method returns the serialized data rather than a ``(data, errors)`` duple.
|
|
605
554
|
A :exc:`ValidationError <marshmallow.exceptions.ValidationError>` is raised
|
|
@@ -632,7 +581,6 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
632
581
|
for `self.many` is used.
|
|
633
582
|
:return: A ``json`` string
|
|
634
583
|
|
|
635
|
-
.. versionadded:: 1.0.0
|
|
636
584
|
.. versionchanged:: 3.0.0b7
|
|
637
585
|
This method returns the serialized data rather than a ``(data, errors)`` duple.
|
|
638
586
|
A :exc:`ValidationError <marshmallow.exceptions.ValidationError>` is raised
|
|
@@ -643,15 +591,12 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
643
591
|
|
|
644
592
|
def _deserialize(
|
|
645
593
|
self,
|
|
646
|
-
data:
|
|
647
|
-
typing.Mapping[str, typing.Any]
|
|
648
|
-
| typing.Iterable[typing.Mapping[str, typing.Any]]
|
|
649
|
-
),
|
|
594
|
+
data: Mapping[str, typing.Any] | Sequence[Mapping[str, typing.Any]],
|
|
650
595
|
*,
|
|
651
596
|
error_store: ErrorStore,
|
|
652
597
|
many: bool = False,
|
|
653
598
|
partial=None,
|
|
654
|
-
unknown=RAISE,
|
|
599
|
+
unknown: types.UnknownOption = RAISE,
|
|
655
600
|
index=None,
|
|
656
601
|
) -> typing.Any | list[typing.Any]:
|
|
657
602
|
"""Deserialize ``data``.
|
|
@@ -673,13 +618,13 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
673
618
|
index_errors = self.opts.index_errors
|
|
674
619
|
index = index if index_errors else None
|
|
675
620
|
if many:
|
|
676
|
-
if not
|
|
621
|
+
if not is_sequence_but_not_string(data):
|
|
677
622
|
error_store.store_error([self.error_messages["type"]], index=index)
|
|
678
623
|
ret_l = []
|
|
679
624
|
else:
|
|
680
625
|
ret_l = [
|
|
681
626
|
self._deserialize(
|
|
682
|
-
|
|
627
|
+
d,
|
|
683
628
|
error_store=error_store,
|
|
684
629
|
many=False,
|
|
685
630
|
partial=partial,
|
|
@@ -757,14 +702,11 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
757
702
|
|
|
758
703
|
def load(
|
|
759
704
|
self,
|
|
760
|
-
data:
|
|
761
|
-
typing.Mapping[str, typing.Any]
|
|
762
|
-
| typing.Iterable[typing.Mapping[str, typing.Any]]
|
|
763
|
-
),
|
|
705
|
+
data: Mapping[str, typing.Any] | Sequence[Mapping[str, typing.Any]],
|
|
764
706
|
*,
|
|
765
707
|
many: bool | None = None,
|
|
766
708
|
partial: bool | types.StrSequenceOrSet | None = None,
|
|
767
|
-
unknown:
|
|
709
|
+
unknown: types.UnknownOption | None = None,
|
|
768
710
|
):
|
|
769
711
|
"""Deserialize a data structure to an object defined by this Schema's fields.
|
|
770
712
|
|
|
@@ -780,7 +722,6 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
780
722
|
If `None`, the value for `self.unknown` is used.
|
|
781
723
|
:return: Deserialized data
|
|
782
724
|
|
|
783
|
-
.. versionadded:: 1.0.0
|
|
784
725
|
.. versionchanged:: 3.0.0b7
|
|
785
726
|
This method returns the deserialized data rather than a ``(data, errors)`` duple.
|
|
786
727
|
A :exc:`ValidationError <marshmallow.exceptions.ValidationError>` is raised
|
|
@@ -792,17 +733,18 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
792
733
|
|
|
793
734
|
def loads(
|
|
794
735
|
self,
|
|
795
|
-
|
|
736
|
+
s: str | bytes | bytearray,
|
|
737
|
+
/,
|
|
796
738
|
*,
|
|
797
739
|
many: bool | None = None,
|
|
798
740
|
partial: bool | types.StrSequenceOrSet | None = None,
|
|
799
|
-
unknown:
|
|
741
|
+
unknown: types.UnknownOption | None = None,
|
|
800
742
|
**kwargs,
|
|
801
743
|
):
|
|
802
744
|
"""Same as :meth:`load`, except it uses `marshmallow.Schema.Meta.render_module` to deserialize
|
|
803
745
|
the passed string before passing data to :meth:`load`.
|
|
804
746
|
|
|
805
|
-
:param
|
|
747
|
+
:param s: A string of the data to deserialize.
|
|
806
748
|
:param many: Whether to deserialize `obj` as a collection. If `None`, the
|
|
807
749
|
value for `self.many` is used.
|
|
808
750
|
:param partial: Whether to ignore missing fields and not require
|
|
@@ -814,13 +756,14 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
814
756
|
If `None`, the value for `self.unknown` is used.
|
|
815
757
|
:return: Deserialized data
|
|
816
758
|
|
|
817
|
-
.. versionadded:: 1.0.0
|
|
818
759
|
.. versionchanged:: 3.0.0b7
|
|
819
760
|
This method returns the deserialized data rather than a ``(data, errors)`` duple.
|
|
820
761
|
A :exc:`ValidationError <marshmallow.exceptions.ValidationError>` is raised
|
|
821
762
|
if invalid data are passed.
|
|
763
|
+
.. versionchanged:: 4.0.0
|
|
764
|
+
Rename ``json_module`` parameter to ``s``.
|
|
822
765
|
"""
|
|
823
|
-
data = self.opts.render_module.loads(
|
|
766
|
+
data = self.opts.render_module.loads(s, **kwargs)
|
|
824
767
|
return self.load(data, many=many, partial=partial, unknown=unknown)
|
|
825
768
|
|
|
826
769
|
def _run_validator(
|
|
@@ -832,14 +775,17 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
832
775
|
error_store: ErrorStore,
|
|
833
776
|
many: bool,
|
|
834
777
|
partial: bool | types.StrSequenceOrSet | None,
|
|
778
|
+
unknown: types.UnknownOption | None,
|
|
835
779
|
pass_original: bool,
|
|
836
780
|
index: int | None = None,
|
|
837
781
|
):
|
|
838
782
|
try:
|
|
839
783
|
if pass_original: # Pass original, raw data (before unmarshalling)
|
|
840
|
-
validator_func(
|
|
784
|
+
validator_func(
|
|
785
|
+
output, original_data, partial=partial, many=many, unknown=unknown
|
|
786
|
+
)
|
|
841
787
|
else:
|
|
842
|
-
validator_func(output, partial=partial, many=many)
|
|
788
|
+
validator_func(output, partial=partial, many=many, unknown=unknown)
|
|
843
789
|
except ValidationError as err:
|
|
844
790
|
field_name = err.field_name
|
|
845
791
|
data_key: str
|
|
@@ -864,10 +810,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
864
810
|
|
|
865
811
|
def validate(
|
|
866
812
|
self,
|
|
867
|
-
data:
|
|
868
|
-
typing.Mapping[str, typing.Any]
|
|
869
|
-
| typing.Iterable[typing.Mapping[str, typing.Any]]
|
|
870
|
-
),
|
|
813
|
+
data: Mapping[str, typing.Any] | Sequence[Mapping[str, typing.Any]],
|
|
871
814
|
*,
|
|
872
815
|
many: bool | None = None,
|
|
873
816
|
partial: bool | types.StrSequenceOrSet | None = None,
|
|
@@ -883,27 +826,22 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
883
826
|
its value is an iterable, only missing fields listed in that iterable
|
|
884
827
|
will be ignored. Use dot delimiters to specify nested fields.
|
|
885
828
|
:return: A dictionary of validation errors.
|
|
886
|
-
|
|
887
|
-
.. versionadded:: 1.1.0
|
|
888
829
|
"""
|
|
889
830
|
try:
|
|
890
831
|
self._do_load(data, many=many, partial=partial, postprocess=False)
|
|
891
832
|
except ValidationError as exc:
|
|
892
|
-
return typing.cast(dict[str, list[str]], exc.messages)
|
|
833
|
+
return typing.cast("dict[str, list[str]]", exc.messages)
|
|
893
834
|
return {}
|
|
894
835
|
|
|
895
836
|
##### Private Helpers #####
|
|
896
837
|
|
|
897
838
|
def _do_load(
|
|
898
839
|
self,
|
|
899
|
-
data: (
|
|
900
|
-
typing.Mapping[str, typing.Any]
|
|
901
|
-
| typing.Iterable[typing.Mapping[str, typing.Any]]
|
|
902
|
-
),
|
|
840
|
+
data: (Mapping[str, typing.Any] | Sequence[Mapping[str, typing.Any]]),
|
|
903
841
|
*,
|
|
904
842
|
many: bool | None = None,
|
|
905
843
|
partial: bool | types.StrSequenceOrSet | None = None,
|
|
906
|
-
unknown:
|
|
844
|
+
unknown: types.UnknownOption | None = None,
|
|
907
845
|
postprocess: bool = True,
|
|
908
846
|
):
|
|
909
847
|
"""Deserialize `data`, returning the deserialized result.
|
|
@@ -925,18 +863,19 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
925
863
|
error_store = ErrorStore()
|
|
926
864
|
errors: dict[str, list[str]] = {}
|
|
927
865
|
many = self.many if many is None else bool(many)
|
|
928
|
-
unknown =
|
|
929
|
-
self.unknown
|
|
930
|
-
if unknown is None
|
|
931
|
-
else validate_unknown_parameter_value(unknown)
|
|
932
|
-
)
|
|
866
|
+
unknown = self.unknown if unknown is None else unknown
|
|
933
867
|
if partial is None:
|
|
934
868
|
partial = self.partial
|
|
935
869
|
# Run preprocessors
|
|
936
870
|
if self._hooks[PRE_LOAD]:
|
|
937
871
|
try:
|
|
938
872
|
processed_data = self._invoke_load_processors(
|
|
939
|
-
PRE_LOAD,
|
|
873
|
+
PRE_LOAD,
|
|
874
|
+
data,
|
|
875
|
+
many=many,
|
|
876
|
+
original_data=data,
|
|
877
|
+
partial=partial,
|
|
878
|
+
unknown=unknown,
|
|
940
879
|
)
|
|
941
880
|
except ValidationError as err:
|
|
942
881
|
errors = err.normalized_messages()
|
|
@@ -961,20 +900,22 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
961
900
|
field_errors = bool(error_store.errors)
|
|
962
901
|
self._invoke_schema_validators(
|
|
963
902
|
error_store=error_store,
|
|
964
|
-
|
|
903
|
+
pass_collection=True,
|
|
965
904
|
data=result,
|
|
966
905
|
original_data=data,
|
|
967
906
|
many=many,
|
|
968
907
|
partial=partial,
|
|
908
|
+
unknown=unknown,
|
|
969
909
|
field_errors=field_errors,
|
|
970
910
|
)
|
|
971
911
|
self._invoke_schema_validators(
|
|
972
912
|
error_store=error_store,
|
|
973
|
-
|
|
913
|
+
pass_collection=False,
|
|
974
914
|
data=result,
|
|
975
915
|
original_data=data,
|
|
976
916
|
many=many,
|
|
977
917
|
partial=partial,
|
|
918
|
+
unknown=unknown,
|
|
978
919
|
field_errors=field_errors,
|
|
979
920
|
)
|
|
980
921
|
errors = error_store.errors
|
|
@@ -987,6 +928,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
987
928
|
many=many,
|
|
988
929
|
original_data=data,
|
|
989
930
|
partial=partial,
|
|
931
|
+
unknown=unknown,
|
|
990
932
|
)
|
|
991
933
|
except ValidationError as err:
|
|
992
934
|
errors = err.normalized_messages()
|
|
@@ -1041,8 +983,6 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
1041
983
|
available_field_names = self.set_class(self.opts.fields)
|
|
1042
984
|
else:
|
|
1043
985
|
available_field_names = self.set_class(self.declared_fields.keys())
|
|
1044
|
-
if self.opts.additional:
|
|
1045
|
-
available_field_names |= self.set_class(self.opts.additional)
|
|
1046
986
|
|
|
1047
987
|
invalid_fields = self.set_class()
|
|
1048
988
|
|
|
@@ -1067,7 +1007,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
1067
1007
|
|
|
1068
1008
|
fields_dict = self.dict_class()
|
|
1069
1009
|
for field_name in field_names:
|
|
1070
|
-
field_obj = self.declared_fields
|
|
1010
|
+
field_obj = self.declared_fields[field_name]
|
|
1071
1011
|
self._bind_field(field_name, field_obj)
|
|
1072
1012
|
fields_dict[field_name] = field_obj
|
|
1073
1013
|
|
|
@@ -1126,129 +1066,128 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
1126
1066
|
field_obj.load_only = True
|
|
1127
1067
|
if field_name in self.dump_only:
|
|
1128
1068
|
field_obj.dump_only = True
|
|
1129
|
-
|
|
1130
|
-
field_obj._bind_to_schema(field_name, self) # noqa: SLF001
|
|
1131
|
-
except TypeError as error:
|
|
1132
|
-
# Field declared as a class, not an instance. Ignore type checking because
|
|
1133
|
-
# we handle unsupported arg types, i.e. this is dead code from
|
|
1134
|
-
# the type checker's perspective.
|
|
1135
|
-
if isinstance(field_obj, type) and issubclass(field_obj, base.FieldABC):
|
|
1136
|
-
msg = (
|
|
1137
|
-
f'Field for "{field_name}" must be declared as a '
|
|
1138
|
-
"Field instance, not a class. "
|
|
1139
|
-
f'Did you mean "fields.{field_obj.__name__}()"?' # type: ignore[attr-defined]
|
|
1140
|
-
)
|
|
1141
|
-
raise TypeError(msg) from error
|
|
1142
|
-
raise
|
|
1069
|
+
field_obj._bind_to_schema(field_name, self)
|
|
1143
1070
|
self.on_bind_field(field_name, field_obj)
|
|
1144
1071
|
|
|
1145
1072
|
def _invoke_dump_processors(
|
|
1146
1073
|
self, tag: str, data, *, many: bool, original_data=None
|
|
1147
1074
|
):
|
|
1148
|
-
# The
|
|
1149
|
-
# 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
|
|
1150
1077
|
# to get a list of items.
|
|
1151
1078
|
data = self._invoke_processors(
|
|
1152
|
-
tag,
|
|
1079
|
+
tag,
|
|
1080
|
+
pass_collection=False,
|
|
1081
|
+
data=data,
|
|
1082
|
+
many=many,
|
|
1083
|
+
original_data=original_data,
|
|
1153
1084
|
)
|
|
1154
1085
|
return self._invoke_processors(
|
|
1155
|
-
tag,
|
|
1086
|
+
tag, pass_collection=True, data=data, many=many, original_data=original_data
|
|
1156
1087
|
)
|
|
1157
1088
|
|
|
1158
1089
|
def _invoke_load_processors(
|
|
1159
1090
|
self,
|
|
1160
1091
|
tag: str,
|
|
1161
|
-
data,
|
|
1092
|
+
data: Mapping[str, typing.Any] | Sequence[Mapping[str, typing.Any]],
|
|
1162
1093
|
*,
|
|
1163
1094
|
many: bool,
|
|
1164
1095
|
original_data,
|
|
1165
1096
|
partial: bool | types.StrSequenceOrSet | None,
|
|
1097
|
+
unknown: types.UnknownOption | None,
|
|
1166
1098
|
):
|
|
1167
|
-
# 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
|
|
1168
1100
|
# processors first.
|
|
1169
1101
|
data = self._invoke_processors(
|
|
1170
1102
|
tag,
|
|
1171
|
-
|
|
1103
|
+
pass_collection=True,
|
|
1172
1104
|
data=data,
|
|
1173
1105
|
many=many,
|
|
1174
1106
|
original_data=original_data,
|
|
1175
1107
|
partial=partial,
|
|
1108
|
+
unknown=unknown,
|
|
1176
1109
|
)
|
|
1177
1110
|
return self._invoke_processors(
|
|
1178
1111
|
tag,
|
|
1179
|
-
|
|
1112
|
+
pass_collection=False,
|
|
1180
1113
|
data=data,
|
|
1181
1114
|
many=many,
|
|
1182
1115
|
original_data=original_data,
|
|
1183
1116
|
partial=partial,
|
|
1117
|
+
unknown=unknown,
|
|
1184
1118
|
)
|
|
1185
1119
|
|
|
1186
1120
|
def _invoke_field_validators(self, *, error_store: ErrorStore, data, many: bool):
|
|
1187
1121
|
for attr_name, _, validator_kwargs in self._hooks[VALIDATES]:
|
|
1188
1122
|
validator = getattr(self, attr_name)
|
|
1189
|
-
field_name = validator_kwargs["field_name"]
|
|
1190
1123
|
|
|
1191
|
-
|
|
1192
|
-
field_obj = self.fields[field_name]
|
|
1193
|
-
except KeyError as error:
|
|
1194
|
-
if field_name in self.declared_fields:
|
|
1195
|
-
continue
|
|
1196
|
-
raise ValueError(f'"{field_name}" field does not exist.') from error
|
|
1124
|
+
field_names = validator_kwargs["field_names"]
|
|
1197
1125
|
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
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:
|
|
1203
1156
|
try:
|
|
1204
|
-
value =
|
|
1157
|
+
value = data[field_obj.attribute or field_name]
|
|
1205
1158
|
except KeyError:
|
|
1206
1159
|
pass
|
|
1207
1160
|
else:
|
|
1208
1161
|
validated_value = self._call_and_store(
|
|
1209
|
-
getter_func=
|
|
1162
|
+
getter_func=do_validate,
|
|
1210
1163
|
data=value,
|
|
1211
1164
|
field_name=data_key,
|
|
1212
1165
|
error_store=error_store,
|
|
1213
|
-
index=(idx if self.opts.index_errors else None),
|
|
1214
1166
|
)
|
|
1215
1167
|
if validated_value is missing:
|
|
1216
|
-
|
|
1217
|
-
else:
|
|
1218
|
-
try:
|
|
1219
|
-
value = data[field_obj.attribute or field_name]
|
|
1220
|
-
except KeyError:
|
|
1221
|
-
pass
|
|
1222
|
-
else:
|
|
1223
|
-
validated_value = self._call_and_store(
|
|
1224
|
-
getter_func=validator,
|
|
1225
|
-
data=value,
|
|
1226
|
-
field_name=data_key,
|
|
1227
|
-
error_store=error_store,
|
|
1228
|
-
)
|
|
1229
|
-
if validated_value is missing:
|
|
1230
|
-
data.pop(field_name, None)
|
|
1168
|
+
data.pop(field_name, None)
|
|
1231
1169
|
|
|
1232
1170
|
def _invoke_schema_validators(
|
|
1233
1171
|
self,
|
|
1234
1172
|
*,
|
|
1235
1173
|
error_store: ErrorStore,
|
|
1236
|
-
|
|
1174
|
+
pass_collection: bool,
|
|
1237
1175
|
data,
|
|
1238
1176
|
original_data,
|
|
1239
1177
|
many: bool,
|
|
1240
1178
|
partial: bool | types.StrSequenceOrSet | None,
|
|
1241
1179
|
field_errors: bool = False,
|
|
1180
|
+
unknown: types.UnknownOption | None,
|
|
1242
1181
|
):
|
|
1243
1182
|
for attr_name, hook_many, validator_kwargs in self._hooks[VALIDATES_SCHEMA]:
|
|
1244
|
-
if hook_many !=
|
|
1183
|
+
if hook_many != pass_collection:
|
|
1245
1184
|
continue
|
|
1246
1185
|
validator = getattr(self, attr_name)
|
|
1247
1186
|
if field_errors and validator_kwargs["skip_on_field_errors"]:
|
|
1248
1187
|
continue
|
|
1249
1188
|
pass_original = validator_kwargs.get("pass_original", False)
|
|
1250
1189
|
|
|
1251
|
-
if many and not
|
|
1190
|
+
if many and not pass_collection:
|
|
1252
1191
|
for idx, (item, orig) in enumerate(zip(data, original_data)):
|
|
1253
1192
|
self._run_validator(
|
|
1254
1193
|
validator,
|
|
@@ -1257,6 +1196,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
1257
1196
|
error_store=error_store,
|
|
1258
1197
|
many=many,
|
|
1259
1198
|
partial=partial,
|
|
1199
|
+
unknown=unknown,
|
|
1260
1200
|
index=idx,
|
|
1261
1201
|
pass_original=pass_original,
|
|
1262
1202
|
)
|
|
@@ -1269,26 +1209,27 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
1269
1209
|
many=many,
|
|
1270
1210
|
pass_original=pass_original,
|
|
1271
1211
|
partial=partial,
|
|
1212
|
+
unknown=unknown,
|
|
1272
1213
|
)
|
|
1273
1214
|
|
|
1274
1215
|
def _invoke_processors(
|
|
1275
1216
|
self,
|
|
1276
1217
|
tag: str,
|
|
1277
1218
|
*,
|
|
1278
|
-
|
|
1279
|
-
data,
|
|
1219
|
+
pass_collection: bool,
|
|
1220
|
+
data: Mapping[str, typing.Any] | Sequence[Mapping[str, typing.Any]],
|
|
1280
1221
|
many: bool,
|
|
1281
1222
|
original_data=None,
|
|
1282
1223
|
**kwargs,
|
|
1283
1224
|
):
|
|
1284
1225
|
for attr_name, hook_many, processor_kwargs in self._hooks[tag]:
|
|
1285
|
-
if hook_many !=
|
|
1226
|
+
if hook_many != pass_collection:
|
|
1286
1227
|
continue
|
|
1287
1228
|
# This will be a bound method.
|
|
1288
1229
|
processor = getattr(self, attr_name)
|
|
1289
1230
|
pass_original = processor_kwargs.get("pass_original", False)
|
|
1290
1231
|
|
|
1291
|
-
if many and not
|
|
1232
|
+
if many and not pass_collection:
|
|
1292
1233
|
if pass_original:
|
|
1293
1234
|
data = [
|
|
1294
1235
|
processor(item, original, many=many, **kwargs)
|