marshmallow 3.25.1__py3-none-any.whl → 3.26.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 +6 -6
- marshmallow/class_registry.py +8 -7
- marshmallow/decorators.py +15 -10
- marshmallow/error_store.py +3 -3
- marshmallow/fields.py +50 -47
- marshmallow/orderedset.py +1 -1
- marshmallow/schema.py +178 -89
- marshmallow/types.py +24 -0
- marshmallow/utils.py +11 -13
- marshmallow/validate.py +32 -18
- {marshmallow-3.25.1.dist-info → marshmallow-3.26.0.dist-info}/METADATA +1 -1
- marshmallow-3.26.0.dist-info/RECORD +18 -0
- marshmallow-3.25.1.dist-info/RECORD +0 -18
- {marshmallow-3.25.1.dist-info → marshmallow-3.26.0.dist-info}/LICENSE +0 -0
- {marshmallow-3.25.1.dist-info → marshmallow-3.26.0.dist-info}/WHEEL +0 -0
marshmallow/__init__.py
CHANGED
|
@@ -68,14 +68,14 @@ __all__ = [
|
|
|
68
68
|
"RAISE",
|
|
69
69
|
"Schema",
|
|
70
70
|
"SchemaOpts",
|
|
71
|
+
"ValidationError",
|
|
71
72
|
"fields",
|
|
72
|
-
"
|
|
73
|
-
"validates_schema",
|
|
74
|
-
"pre_dump",
|
|
73
|
+
"missing",
|
|
75
74
|
"post_dump",
|
|
76
|
-
"pre_load",
|
|
77
75
|
"post_load",
|
|
78
76
|
"pprint",
|
|
79
|
-
"
|
|
80
|
-
"
|
|
77
|
+
"pre_dump",
|
|
78
|
+
"pre_load",
|
|
79
|
+
"validates",
|
|
80
|
+
"validates_schema",
|
|
81
81
|
]
|
marshmallow/class_registry.py
CHANGED
|
@@ -7,6 +7,7 @@ class:`fields.Nested <marshmallow.fields.Nested>`.
|
|
|
7
7
|
This module is treated as private API.
|
|
8
8
|
Users should not need to use this module directly.
|
|
9
9
|
"""
|
|
10
|
+
# ruff: noqa: ERA001
|
|
10
11
|
|
|
11
12
|
from __future__ import annotations
|
|
12
13
|
|
|
@@ -49,7 +50,7 @@ def register(classname: str, cls: SchemaType) -> None:
|
|
|
49
50
|
module = cls.__module__
|
|
50
51
|
# Full module path to the class
|
|
51
52
|
# e.g. user.schemas.UserSchema
|
|
52
|
-
fullpath = "."
|
|
53
|
+
fullpath = f"{module}.{classname}"
|
|
53
54
|
# If the class is already registered; need to check if the entries are
|
|
54
55
|
# in the same module as cls to avoid having multiple instances of the same
|
|
55
56
|
# class in the registry
|
|
@@ -66,18 +67,19 @@ def register(classname: str, cls: SchemaType) -> None:
|
|
|
66
67
|
else:
|
|
67
68
|
# If fullpath does exist, replace existing entry
|
|
68
69
|
_registry[fullpath] = [cls]
|
|
69
|
-
return None
|
|
70
70
|
|
|
71
71
|
|
|
72
72
|
@typing.overload
|
|
73
|
-
def get_class(classname: str, all: typing.Literal[False] = ...) -> SchemaType: ...
|
|
73
|
+
def get_class(classname: str, *, all: typing.Literal[False] = ...) -> SchemaType: ...
|
|
74
74
|
|
|
75
75
|
|
|
76
76
|
@typing.overload
|
|
77
|
-
def get_class(
|
|
77
|
+
def get_class(
|
|
78
|
+
classname: str, *, all: typing.Literal[True] = ...
|
|
79
|
+
) -> list[SchemaType]: ...
|
|
78
80
|
|
|
79
81
|
|
|
80
|
-
def get_class(classname: str, all: bool = False) -> list[SchemaType] | SchemaType:
|
|
82
|
+
def get_class(classname: str, *, all: bool = False) -> list[SchemaType] | SchemaType: # noqa: A002
|
|
81
83
|
"""Retrieve a class from the registry.
|
|
82
84
|
|
|
83
85
|
:raises: `marshmallow.exceptions.RegistryError` if the class cannot be found
|
|
@@ -98,5 +100,4 @@ def get_class(classname: str, all: bool = False) -> list[SchemaType] | SchemaTyp
|
|
|
98
100
|
"were found. Please use the full, "
|
|
99
101
|
"module-qualified path."
|
|
100
102
|
)
|
|
101
|
-
|
|
102
|
-
return _registry[classname][0]
|
|
103
|
+
return _registry[classname][0]
|
marshmallow/decorators.py
CHANGED
|
@@ -93,9 +93,9 @@ def validates(field_name: str) -> Callable[..., Any]:
|
|
|
93
93
|
|
|
94
94
|
def validates_schema(
|
|
95
95
|
fn: Callable[..., Any] | None = None,
|
|
96
|
-
pass_many: bool = False,
|
|
97
|
-
pass_original: bool = False,
|
|
98
|
-
skip_on_field_errors: bool = True,
|
|
96
|
+
pass_many: bool = False, # noqa: FBT001, FBT002
|
|
97
|
+
pass_original: bool = False, # noqa: FBT001, FBT002
|
|
98
|
+
skip_on_field_errors: bool = True, # noqa: FBT001, FBT002
|
|
99
99
|
) -> Callable[..., Any]:
|
|
100
100
|
"""Register a schema-level validator.
|
|
101
101
|
|
|
@@ -126,7 +126,8 @@ def validates_schema(
|
|
|
126
126
|
|
|
127
127
|
|
|
128
128
|
def pre_dump(
|
|
129
|
-
fn: Callable[..., Any] | None = None,
|
|
129
|
+
fn: Callable[..., Any] | None = None,
|
|
130
|
+
pass_many: bool = False, # noqa: FBT001, FBT002
|
|
130
131
|
) -> Callable[..., Any]:
|
|
131
132
|
"""Register a method to invoke before serializing an object. The method
|
|
132
133
|
receives the object to be serialized and returns the processed object.
|
|
@@ -143,8 +144,8 @@ def pre_dump(
|
|
|
143
144
|
|
|
144
145
|
def post_dump(
|
|
145
146
|
fn: Callable[..., Any] | None = None,
|
|
146
|
-
pass_many: bool = False,
|
|
147
|
-
pass_original: bool = False,
|
|
147
|
+
pass_many: bool = False, # noqa: FBT001, FBT002
|
|
148
|
+
pass_original: bool = False, # noqa: FBT001, FBT002
|
|
148
149
|
) -> Callable[..., Any]:
|
|
149
150
|
"""Register a method to invoke after serializing an object. The method
|
|
150
151
|
receives the serialized object and returns the processed object.
|
|
@@ -163,7 +164,8 @@ def post_dump(
|
|
|
163
164
|
|
|
164
165
|
|
|
165
166
|
def pre_load(
|
|
166
|
-
fn: Callable[..., Any] | None = None,
|
|
167
|
+
fn: Callable[..., Any] | None = None,
|
|
168
|
+
pass_many: bool = False, # noqa: FBT001, FBT002
|
|
167
169
|
) -> Callable[..., Any]:
|
|
168
170
|
"""Register a method to invoke before deserializing an object. The method
|
|
169
171
|
receives the data to be deserialized and returns the processed data.
|
|
@@ -181,8 +183,8 @@ def pre_load(
|
|
|
181
183
|
|
|
182
184
|
def post_load(
|
|
183
185
|
fn: Callable[..., Any] | None = None,
|
|
184
|
-
pass_many: bool = False,
|
|
185
|
-
pass_original: bool = False,
|
|
186
|
+
pass_many: bool = False, # noqa: FBT001, FBT002
|
|
187
|
+
pass_original: bool = False, # noqa: FBT001, FBT002
|
|
186
188
|
) -> Callable[..., Any]:
|
|
187
189
|
"""Register a method to invoke after deserializing an object. The method
|
|
188
190
|
receives the deserialized data and returns the processed data.
|
|
@@ -202,7 +204,10 @@ def post_load(
|
|
|
202
204
|
|
|
203
205
|
|
|
204
206
|
def set_hook(
|
|
205
|
-
fn: Callable[..., Any] | None,
|
|
207
|
+
fn: Callable[..., Any] | None,
|
|
208
|
+
tag: str,
|
|
209
|
+
many: bool = False, # noqa: FBT001, FBT002
|
|
210
|
+
**kwargs: Any,
|
|
206
211
|
) -> Callable[..., Any]:
|
|
207
212
|
"""Mark decorated function as a hook to be picked up later.
|
|
208
213
|
You should not need to use this method directly.
|
marshmallow/error_store.py
CHANGED
|
@@ -25,7 +25,7 @@ class ErrorStore:
|
|
|
25
25
|
self.errors = merge_errors(self.errors, messages)
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
def merge_errors(errors1, errors2):
|
|
28
|
+
def merge_errors(errors1, errors2): # noqa: PLR0911
|
|
29
29
|
"""Deeply merge two error messages.
|
|
30
30
|
|
|
31
31
|
The format of ``errors1`` and ``errors2`` matches the ``message``
|
|
@@ -40,7 +40,7 @@ def merge_errors(errors1, errors2):
|
|
|
40
40
|
return errors1 + errors2
|
|
41
41
|
if isinstance(errors2, dict):
|
|
42
42
|
return dict(errors2, **{SCHEMA: merge_errors(errors1, errors2.get(SCHEMA))})
|
|
43
|
-
return errors1
|
|
43
|
+
return [*errors1, errors2]
|
|
44
44
|
if isinstance(errors1, dict):
|
|
45
45
|
if isinstance(errors2, list):
|
|
46
46
|
return dict(errors1, **{SCHEMA: merge_errors(errors1.get(SCHEMA), errors2)})
|
|
@@ -54,7 +54,7 @@ def merge_errors(errors1, errors2):
|
|
|
54
54
|
return errors
|
|
55
55
|
return dict(errors1, **{SCHEMA: merge_errors(errors1.get(SCHEMA), errors2)})
|
|
56
56
|
if isinstance(errors2, list):
|
|
57
|
-
return [errors1
|
|
57
|
+
return [errors1, *errors2]
|
|
58
58
|
if isinstance(errors2, dict):
|
|
59
59
|
return dict(errors2, **{SCHEMA: merge_errors(errors1, errors2.get(SCHEMA))})
|
|
60
60
|
return [errors1, errors2]
|
marshmallow/fields.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# ruff: noqa: F841, SLF001
|
|
1
2
|
from __future__ import annotations
|
|
2
3
|
|
|
3
4
|
import collections
|
|
@@ -11,7 +12,6 @@ import typing
|
|
|
11
12
|
import uuid
|
|
12
13
|
import warnings
|
|
13
14
|
from collections.abc import Mapping as _Mapping
|
|
14
|
-
from enum import Enum as EnumType
|
|
15
15
|
|
|
16
16
|
from marshmallow import class_registry, types, utils, validate
|
|
17
17
|
from marshmallow.base import FieldABC
|
|
@@ -35,47 +35,49 @@ from marshmallow.warnings import (
|
|
|
35
35
|
)
|
|
36
36
|
|
|
37
37
|
if typing.TYPE_CHECKING:
|
|
38
|
+
from enum import Enum as EnumType
|
|
39
|
+
|
|
38
40
|
from marshmallow.schema import Schema, SchemaMeta
|
|
39
41
|
|
|
40
42
|
|
|
41
43
|
__all__ = [
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"Nested",
|
|
45
|
-
"Mapping",
|
|
46
|
-
"Dict",
|
|
47
|
-
"List",
|
|
48
|
-
"Tuple",
|
|
49
|
-
"String",
|
|
44
|
+
"IP",
|
|
45
|
+
"URL",
|
|
50
46
|
"UUID",
|
|
51
|
-
"Number",
|
|
52
|
-
"Integer",
|
|
53
|
-
"Decimal",
|
|
54
|
-
"Boolean",
|
|
55
|
-
"Float",
|
|
56
|
-
"DateTime",
|
|
57
|
-
"NaiveDateTime",
|
|
58
47
|
"AwareDateTime",
|
|
59
|
-
"
|
|
48
|
+
"Bool",
|
|
49
|
+
"Boolean",
|
|
50
|
+
"Constant",
|
|
60
51
|
"Date",
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
52
|
+
"DateTime",
|
|
53
|
+
"Decimal",
|
|
54
|
+
"Dict",
|
|
64
55
|
"Email",
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
56
|
+
"Enum",
|
|
57
|
+
"Field",
|
|
58
|
+
"Float",
|
|
59
|
+
"Function",
|
|
68
60
|
"IPInterface",
|
|
61
|
+
"IPv4",
|
|
69
62
|
"IPv4Interface",
|
|
63
|
+
"IPv6",
|
|
70
64
|
"IPv6Interface",
|
|
71
|
-
"Enum",
|
|
72
|
-
"Method",
|
|
73
|
-
"Function",
|
|
74
|
-
"Str",
|
|
75
|
-
"Bool",
|
|
76
65
|
"Int",
|
|
77
|
-
"
|
|
66
|
+
"Integer",
|
|
67
|
+
"List",
|
|
68
|
+
"Mapping",
|
|
69
|
+
"Method",
|
|
70
|
+
"NaiveDateTime",
|
|
71
|
+
"Nested",
|
|
72
|
+
"Number",
|
|
78
73
|
"Pluck",
|
|
74
|
+
"Raw",
|
|
75
|
+
"Str",
|
|
76
|
+
"String",
|
|
77
|
+
"Time",
|
|
78
|
+
"TimeDelta",
|
|
79
|
+
"Tuple",
|
|
80
|
+
"Url",
|
|
79
81
|
]
|
|
80
82
|
|
|
81
83
|
|
|
@@ -498,7 +500,9 @@ class Nested(Field):
|
|
|
498
500
|
name = fields.Str()
|
|
499
501
|
# Use lambda functions when you need two-way nesting or self-nesting
|
|
500
502
|
parent = fields.Nested(lambda: ParentSchema(only=("id",)), dump_only=True)
|
|
501
|
-
siblings = fields.List(
|
|
503
|
+
siblings = fields.List(
|
|
504
|
+
fields.Nested(lambda: ChildSchema(only=("id", "name")))
|
|
505
|
+
)
|
|
502
506
|
|
|
503
507
|
|
|
504
508
|
class ParentSchema(Schema):
|
|
@@ -843,7 +847,7 @@ class Tuple(Field):
|
|
|
843
847
|
super().__init__(**kwargs)
|
|
844
848
|
if not utils.is_collection(tuple_fields):
|
|
845
849
|
raise ValueError(
|
|
846
|
-
"tuple_fields must be an iterable of Field classes or
|
|
850
|
+
"tuple_fields must be an iterable of Field classes or instances."
|
|
847
851
|
)
|
|
848
852
|
|
|
849
853
|
try:
|
|
@@ -863,9 +867,9 @@ class Tuple(Field):
|
|
|
863
867
|
super()._bind_to_schema(field_name, schema)
|
|
864
868
|
new_tuple_fields = []
|
|
865
869
|
for field in self.tuple_fields:
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
new_tuple_fields.append(
|
|
870
|
+
new_field = copy.deepcopy(field)
|
|
871
|
+
new_field._bind_to_schema(field_name, self)
|
|
872
|
+
new_tuple_fields.append(new_field)
|
|
869
873
|
|
|
870
874
|
self.tuple_fields = new_tuple_fields
|
|
871
875
|
|
|
@@ -1294,7 +1298,7 @@ class DateTime(Field):
|
|
|
1294
1298
|
"format": '"{input}" cannot be formatted as a {obj_type}.',
|
|
1295
1299
|
}
|
|
1296
1300
|
|
|
1297
|
-
def __init__(self, format: str | None = None, **kwargs) -> None:
|
|
1301
|
+
def __init__(self, format: str | None = None, **kwargs) -> None: # noqa: A002
|
|
1298
1302
|
super().__init__(**kwargs)
|
|
1299
1303
|
# Allow this to be None. It may be set later in the ``_serialize``
|
|
1300
1304
|
# or ``_deserialize`` methods. This allows a Schema to dynamically set the
|
|
@@ -1352,7 +1356,7 @@ class NaiveDateTime(DateTime):
|
|
|
1352
1356
|
|
|
1353
1357
|
def __init__(
|
|
1354
1358
|
self,
|
|
1355
|
-
format: str | None = None,
|
|
1359
|
+
format: str | None = None, # noqa: A002
|
|
1356
1360
|
*,
|
|
1357
1361
|
timezone: dt.timezone | None = None,
|
|
1358
1362
|
**kwargs,
|
|
@@ -1389,7 +1393,7 @@ class AwareDateTime(DateTime):
|
|
|
1389
1393
|
|
|
1390
1394
|
def __init__(
|
|
1391
1395
|
self,
|
|
1392
|
-
format: str | None = None,
|
|
1396
|
+
format: str | None = None, # noqa: A002
|
|
1393
1397
|
*,
|
|
1394
1398
|
default_timezone: dt.tzinfo | None = None,
|
|
1395
1399
|
**kwargs,
|
|
@@ -1548,7 +1552,7 @@ class TimeDelta(Field):
|
|
|
1548
1552
|
delta = utils.timedelta_to_microseconds(value)
|
|
1549
1553
|
unit = utils.timedelta_to_microseconds(base_unit)
|
|
1550
1554
|
return delta // unit
|
|
1551
|
-
assert self.serialization_type is float
|
|
1555
|
+
assert self.serialization_type is float # noqa: S101
|
|
1552
1556
|
return value.total_seconds() / base_unit.total_seconds()
|
|
1553
1557
|
|
|
1554
1558
|
def _deserialize(self, value, attr, data, **kwargs):
|
|
@@ -1643,16 +1647,15 @@ class Mapping(Field):
|
|
|
1643
1647
|
if not self.value_field and not self.key_field:
|
|
1644
1648
|
return self.mapping_type(value)
|
|
1645
1649
|
|
|
1646
|
-
#
|
|
1650
|
+
# Serialize keys
|
|
1647
1651
|
if self.key_field is None:
|
|
1648
|
-
keys = {k: k for k in value
|
|
1652
|
+
keys = {k: k for k in value}
|
|
1649
1653
|
else:
|
|
1650
1654
|
keys = {
|
|
1651
|
-
k: self.key_field._serialize(k, None, None, **kwargs)
|
|
1652
|
-
for k in value.keys()
|
|
1655
|
+
k: self.key_field._serialize(k, None, None, **kwargs) for k in value
|
|
1653
1656
|
}
|
|
1654
1657
|
|
|
1655
|
-
#
|
|
1658
|
+
# Serialize values
|
|
1656
1659
|
result = self.mapping_type()
|
|
1657
1660
|
if self.value_field is None:
|
|
1658
1661
|
for k, v in value.items():
|
|
@@ -1672,18 +1675,18 @@ class Mapping(Field):
|
|
|
1672
1675
|
|
|
1673
1676
|
errors = collections.defaultdict(dict)
|
|
1674
1677
|
|
|
1675
|
-
#
|
|
1678
|
+
# Deserialize keys
|
|
1676
1679
|
if self.key_field is None:
|
|
1677
|
-
keys = {k: k for k in value
|
|
1680
|
+
keys = {k: k for k in value}
|
|
1678
1681
|
else:
|
|
1679
1682
|
keys = {}
|
|
1680
|
-
for key in value
|
|
1683
|
+
for key in value:
|
|
1681
1684
|
try:
|
|
1682
1685
|
keys[key] = self.key_field.deserialize(key, **kwargs)
|
|
1683
1686
|
except ValidationError as error:
|
|
1684
1687
|
errors[key]["key"] = error.messages
|
|
1685
1688
|
|
|
1686
|
-
#
|
|
1689
|
+
# Deserialize values
|
|
1687
1690
|
result = self.mapping_type()
|
|
1688
1691
|
if self.value_field is None:
|
|
1689
1692
|
for k, v in value.items():
|
marshmallow/orderedset.py
CHANGED
marshmallow/schema.py
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
"""The `Schema <marshmallow.Schema>` class, including its metaclass and options (class Meta)."""
|
|
1
|
+
"""The `Schema <marshmallow.Schema>` class, including its metaclass and options (`class Meta <marshmallow.Schema.Meta>`)."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import copy
|
|
6
6
|
import datetime as dt
|
|
7
7
|
import decimal
|
|
8
|
+
import functools
|
|
8
9
|
import inspect
|
|
9
10
|
import json
|
|
11
|
+
import operator
|
|
10
12
|
import typing
|
|
11
13
|
import uuid
|
|
12
14
|
import warnings
|
|
13
15
|
from abc import ABCMeta
|
|
14
16
|
from collections import OrderedDict, defaultdict
|
|
15
17
|
from collections.abc import Mapping
|
|
18
|
+
from itertools import zip_longest
|
|
16
19
|
|
|
17
20
|
from marshmallow import base, class_registry, types
|
|
18
21
|
from marshmallow import fields as ma_fields
|
|
@@ -25,7 +28,7 @@ from marshmallow.decorators import (
|
|
|
25
28
|
VALIDATES_SCHEMA,
|
|
26
29
|
)
|
|
27
30
|
from marshmallow.error_store import ErrorStore
|
|
28
|
-
from marshmallow.exceptions import StringNotCollectionError, ValidationError
|
|
31
|
+
from marshmallow.exceptions import SCHEMA, StringNotCollectionError, ValidationError
|
|
29
32
|
from marshmallow.orderedset import OrderedSet
|
|
30
33
|
from marshmallow.utils import (
|
|
31
34
|
EXCLUDE,
|
|
@@ -40,8 +43,11 @@ from marshmallow.utils import (
|
|
|
40
43
|
)
|
|
41
44
|
from marshmallow.warnings import RemovedInMarshmallow4Warning
|
|
42
45
|
|
|
46
|
+
if typing.TYPE_CHECKING:
|
|
47
|
+
from marshmallow.fields import Field
|
|
43
48
|
|
|
44
|
-
|
|
49
|
+
|
|
50
|
+
def _get_fields(attrs) -> list[tuple[str, Field]]:
|
|
45
51
|
"""Get fields from a class
|
|
46
52
|
|
|
47
53
|
:param attrs: Mapping of class attributes
|
|
@@ -63,8 +69,11 @@ def _get_fields_by_mro(klass: SchemaMeta):
|
|
|
63
69
|
:param klass: Class whose fields to retrieve
|
|
64
70
|
"""
|
|
65
71
|
mro = inspect.getmro(klass)
|
|
72
|
+
# Combine fields from all parents
|
|
73
|
+
# functools.reduce(operator.iadd, list_of_lists) is faster than sum(list_of_lists, [])
|
|
66
74
|
# Loop over mro in reverse to maintain correct order of fields
|
|
67
|
-
return
|
|
75
|
+
return functools.reduce(
|
|
76
|
+
operator.iadd,
|
|
68
77
|
(
|
|
69
78
|
_get_fields(
|
|
70
79
|
getattr(base, "_declared_fields", base.__dict__),
|
|
@@ -79,10 +88,20 @@ class SchemaMeta(ABCMeta):
|
|
|
79
88
|
"""Metaclass for the Schema class. Binds the declared fields to
|
|
80
89
|
a ``_declared_fields`` attribute, which is a dictionary mapping attribute
|
|
81
90
|
names to field objects. Also sets the ``opts`` class attribute, which is
|
|
82
|
-
the Schema class's
|
|
91
|
+
the Schema class's `class Meta <marshmallow.Schema.Meta>` options.
|
|
83
92
|
"""
|
|
84
93
|
|
|
85
|
-
|
|
94
|
+
Meta: type
|
|
95
|
+
opts: typing.Any
|
|
96
|
+
OPTIONS_CLASS: type
|
|
97
|
+
_declared_fields: dict[str, Field]
|
|
98
|
+
|
|
99
|
+
def __new__(
|
|
100
|
+
mcs, # noqa: N804
|
|
101
|
+
name: str,
|
|
102
|
+
bases: tuple[type, ...],
|
|
103
|
+
attrs: dict[str, typing.Any],
|
|
104
|
+
) -> SchemaMeta:
|
|
86
105
|
meta = attrs.get("Meta")
|
|
87
106
|
ordered = getattr(meta, "ordered", False)
|
|
88
107
|
if not ordered:
|
|
@@ -112,7 +131,7 @@ class SchemaMeta(ABCMeta):
|
|
|
112
131
|
cls_fields += list(klass.opts.include.items())
|
|
113
132
|
|
|
114
133
|
# Assign _declared_fields on class
|
|
115
|
-
klass._declared_fields = mcs.get_declared_fields(
|
|
134
|
+
klass._declared_fields = mcs.get_declared_fields( # noqa: SLF001
|
|
116
135
|
klass=klass,
|
|
117
136
|
cls_fields=cls_fields,
|
|
118
137
|
inherited_fields=inherited_fields,
|
|
@@ -122,19 +141,19 @@ class SchemaMeta(ABCMeta):
|
|
|
122
141
|
|
|
123
142
|
@classmethod
|
|
124
143
|
def get_declared_fields(
|
|
125
|
-
mcs,
|
|
144
|
+
mcs, # noqa: N804
|
|
126
145
|
klass: SchemaMeta,
|
|
127
|
-
cls_fields: list[tuple[str,
|
|
128
|
-
inherited_fields: list[tuple[str,
|
|
146
|
+
cls_fields: list[tuple[str, Field]],
|
|
147
|
+
inherited_fields: list[tuple[str, Field]],
|
|
129
148
|
dict_cls: type[dict] = dict,
|
|
130
|
-
) -> dict[str,
|
|
149
|
+
) -> dict[str, Field]:
|
|
131
150
|
"""Returns a dictionary of field_name => `Field` pairs declared on the class.
|
|
132
151
|
This is exposed mainly so that plugins can add additional fields, e.g. fields
|
|
133
|
-
computed from class Meta options.
|
|
152
|
+
computed from `class Meta <marshmallow.Schema.Meta>` options.
|
|
134
153
|
|
|
135
154
|
:param klass: The class object.
|
|
136
155
|
:param cls_fields: The fields declared on the class, including those added
|
|
137
|
-
by the ``include`` class Meta option.
|
|
156
|
+
by the ``include`` `class Meta <marshmallow.Schema.Meta>` option.
|
|
138
157
|
:param inherited_fields: Inherited fields.
|
|
139
158
|
:param dict_cls: dict-like class to use for dict output Default to ``dict``.
|
|
140
159
|
"""
|
|
@@ -191,9 +210,9 @@ class SchemaMeta(ABCMeta):
|
|
|
191
210
|
|
|
192
211
|
|
|
193
212
|
class SchemaOpts:
|
|
194
|
-
"""
|
|
213
|
+
"""Defines defaults for `marshmallow.Schema.Meta`."""
|
|
195
214
|
|
|
196
|
-
def __init__(self, meta, ordered: bool = False):
|
|
215
|
+
def __init__(self, meta: type, ordered: bool = False): # noqa: FBT001, FBT002
|
|
197
216
|
self.fields = getattr(meta, "fields", ())
|
|
198
217
|
if not isinstance(self.fields, (list, tuple)):
|
|
199
218
|
raise ValueError("`fields` option must be a list or tuple.")
|
|
@@ -202,8 +221,7 @@ class SchemaOpts:
|
|
|
202
221
|
raise ValueError("`additional` option must be a list or tuple.")
|
|
203
222
|
if self.fields and self.additional:
|
|
204
223
|
raise ValueError(
|
|
205
|
-
"Cannot set both `fields` and `additional` options"
|
|
206
|
-
" for the same Schema."
|
|
224
|
+
"Cannot set both `fields` and `additional` options for the same Schema."
|
|
207
225
|
)
|
|
208
226
|
self.exclude = getattr(meta, "exclude", ())
|
|
209
227
|
if not isinstance(self.exclude, (list, tuple)):
|
|
@@ -221,6 +239,14 @@ class SchemaOpts:
|
|
|
221
239
|
else:
|
|
222
240
|
render_module = json
|
|
223
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
|
+
)
|
|
224
250
|
self.ordered = getattr(meta, "ordered", ordered)
|
|
225
251
|
self.index_errors = getattr(meta, "index_errors", True)
|
|
226
252
|
self.include = getattr(meta, "include", {})
|
|
@@ -232,7 +258,7 @@ class SchemaOpts:
|
|
|
232
258
|
|
|
233
259
|
|
|
234
260
|
class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
235
|
-
"""Base schema class with which to define
|
|
261
|
+
"""Base schema class with which to define schemas.
|
|
236
262
|
|
|
237
263
|
Example usage:
|
|
238
264
|
|
|
@@ -284,7 +310,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
284
310
|
`prefix` parameter removed.
|
|
285
311
|
"""
|
|
286
312
|
|
|
287
|
-
TYPE_MAPPING: dict[type, type[
|
|
313
|
+
TYPE_MAPPING: dict[type, type[Field]] = {
|
|
288
314
|
str: ma_fields.String,
|
|
289
315
|
bytes: ma_fields.String,
|
|
290
316
|
dt.datetime: ma_fields.DateTime,
|
|
@@ -314,7 +340,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
314
340
|
|
|
315
341
|
# These get set by SchemaMeta
|
|
316
342
|
opts: typing.Any
|
|
317
|
-
_declared_fields: dict[str,
|
|
343
|
+
_declared_fields: dict[str, Field] = {}
|
|
318
344
|
_hooks: dict[str, list[tuple[str, bool, dict]]] = {}
|
|
319
345
|
|
|
320
346
|
class Meta:
|
|
@@ -322,39 +348,87 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
322
348
|
|
|
323
349
|
Example usage: ::
|
|
324
350
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
351
|
+
from marshmallow import Schema
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
class MySchema(Schema):
|
|
355
|
+
class Meta:
|
|
356
|
+
fields = ("id", "email", "date_created")
|
|
357
|
+
exclude = ("password", "secret_attribute")
|
|
358
|
+
|
|
359
|
+
.. admonition:: A note on type checking
|
|
360
|
+
|
|
361
|
+
Type checkers will only check the attributes of the `Meta <marshmallow.Schema.Meta>`
|
|
362
|
+
class if you explicitly subclass `marshmallow.Schema.Meta`.
|
|
363
|
+
|
|
364
|
+
.. code-block:: python
|
|
365
|
+
|
|
366
|
+
from marshmallow import Schema
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
class MySchema(Schema):
|
|
370
|
+
# Not checked by type checkers
|
|
371
|
+
class Meta:
|
|
372
|
+
additional = True
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
class MySchema2(Schema):
|
|
376
|
+
# Type checkers will check attributes
|
|
377
|
+
class Meta(Schema.Opts):
|
|
378
|
+
additional = True # Incompatible types in assignment
|
|
379
|
+
|
|
380
|
+
.. versionremoved:: 3.0.0b7 Remove ``strict``.
|
|
381
|
+
.. versionadded:: 3.0.0b12 Add `unknown`.
|
|
382
|
+
.. versionchanged:: 3.0.0b17 Rename ``dateformat`` to `datetimeformat`.
|
|
383
|
+
.. versionadded:: 3.9.0 Add `timeformat`.
|
|
384
|
+
.. versionchanged:: 3.26.0 Deprecate `ordered`. Field order is preserved by default.
|
|
385
|
+
"""
|
|
386
|
+
|
|
387
|
+
fields: typing.ClassVar[tuple[Field] | list[Field]]
|
|
388
|
+
"""Fields to include in the (de)serialized result"""
|
|
389
|
+
additional: typing.ClassVar[tuple[Field] | list[Field]]
|
|
390
|
+
"""Fields to include in addition to the explicitly declared fields.
|
|
391
|
+
`additional <marshmallow.Schema.Meta.additional>` and `fields <marshmallow.Schema.Meta.fields>`
|
|
392
|
+
are mutually-exclusive options.
|
|
393
|
+
"""
|
|
394
|
+
include: typing.ClassVar[dict[str, Field]]
|
|
395
|
+
"""Dictionary of additional fields to include in the schema. It is
|
|
396
|
+
usually better to define fields as class variables, but you may need to
|
|
397
|
+
use this option, e.g., if your fields are Python keywords.
|
|
398
|
+
"""
|
|
399
|
+
exclude: typing.ClassVar[tuple[Field] | list[Field]]
|
|
400
|
+
"""Fields to exclude in the serialized result.
|
|
401
|
+
Nested fields can be represented with dot delimiters.
|
|
402
|
+
"""
|
|
403
|
+
many: typing.ClassVar[bool]
|
|
404
|
+
"""Whether data should be (de)serialized as a collection by default."""
|
|
405
|
+
dateformat: typing.ClassVar[str]
|
|
406
|
+
"""Default format for `Date <marshmallow.fields.Date>` fields."""
|
|
407
|
+
datetimeformat: typing.ClassVar[str]
|
|
408
|
+
"""Default format for `DateTime <marshmallow.fields.DateTime>` fields."""
|
|
409
|
+
timeformat: typing.ClassVar[str]
|
|
410
|
+
"""Default format for `Time <marshmallow.fields.Time>` fields."""
|
|
411
|
+
render_module: typing.ClassVar[types.RenderModule]
|
|
412
|
+
""" Module to use for `loads <marshmallow.Schema.loads>` and `dumps <marshmallow.Schema.dumps>`.
|
|
413
|
+
Defaults to `json` from the standard library.
|
|
414
|
+
"""
|
|
415
|
+
ordered: typing.ClassVar[bool]
|
|
416
|
+
"""If `True`, `Schema.dump <marshmallow.Schema.dump>` is a `collections.OrderedDict`."""
|
|
417
|
+
index_errors: typing.ClassVar[bool]
|
|
418
|
+
"""If `True`, errors dictionaries will include the index of invalid items in a collection."""
|
|
419
|
+
load_only: typing.ClassVar[tuple[Field] | list[Field]]
|
|
420
|
+
"""Fields to exclude from serialized results"""
|
|
421
|
+
dump_only: typing.ClassVar[tuple[Field] | list[Field]]
|
|
422
|
+
"""Fields to exclude from serialized results"""
|
|
423
|
+
unknown: typing.ClassVar[str]
|
|
424
|
+
"""Whether to exclude, include, or raise an error for unknown fields in the data.
|
|
425
|
+
Use `EXCLUDE`, `INCLUDE` or `RAISE`.
|
|
426
|
+
"""
|
|
427
|
+
register: typing.ClassVar[bool]
|
|
428
|
+
"""Whether to register the `Schema <marshmallow.Schema>` with marshmallow's internal
|
|
429
|
+
class registry. Must be `True` if you intend to refer to this `Schema <marshmallow.Schema>`
|
|
430
|
+
by class name in `Nested` fields. Only set this to `False` when memory
|
|
431
|
+
usage is critical. Defaults to `True`.
|
|
358
432
|
"""
|
|
359
433
|
|
|
360
434
|
def __init__(
|
|
@@ -400,9 +474,9 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
400
474
|
self.context = context or {}
|
|
401
475
|
self._normalize_nested_options()
|
|
402
476
|
#: Dictionary mapping field_names -> :class:`Field` objects
|
|
403
|
-
self.fields: dict[str,
|
|
404
|
-
self.load_fields: dict[str,
|
|
405
|
-
self.dump_fields: dict[str,
|
|
477
|
+
self.fields: dict[str, Field] = {}
|
|
478
|
+
self.load_fields: dict[str, Field] = {}
|
|
479
|
+
self.dump_fields: dict[str, Field] = {}
|
|
406
480
|
self._init_fields()
|
|
407
481
|
messages = {}
|
|
408
482
|
messages.update(self._default_error_messages)
|
|
@@ -416,15 +490,15 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
416
490
|
|
|
417
491
|
@property
|
|
418
492
|
def dict_class(self) -> type[dict]:
|
|
493
|
+
"""`dict` type to return when serializing."""
|
|
419
494
|
if self.ordered:
|
|
420
495
|
return OrderedDict
|
|
421
|
-
|
|
422
|
-
return dict
|
|
496
|
+
return dict
|
|
423
497
|
|
|
424
498
|
@classmethod
|
|
425
499
|
def from_dict(
|
|
426
500
|
cls,
|
|
427
|
-
fields: dict[str,
|
|
501
|
+
fields: dict[str, Field],
|
|
428
502
|
*,
|
|
429
503
|
name: str = "GeneratedSchema",
|
|
430
504
|
) -> type[Schema]:
|
|
@@ -450,8 +524,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
450
524
|
Meta = type(
|
|
451
525
|
"GeneratedMeta", (getattr(cls, "Meta", object),), {"register": False}
|
|
452
526
|
)
|
|
453
|
-
|
|
454
|
-
return schema_cls
|
|
527
|
+
return type(name, (cls,), {**fields.copy(), "Meta": Meta})
|
|
455
528
|
|
|
456
529
|
##### Override-able methods #####
|
|
457
530
|
|
|
@@ -468,7 +541,6 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
468
541
|
.. versionchanged:: 3.0.0rc9
|
|
469
542
|
Receives `many` and `partial` (on deserialization) as keyword arguments.
|
|
470
543
|
"""
|
|
471
|
-
pass
|
|
472
544
|
|
|
473
545
|
def get_attribute(self, obj: typing.Any, attr: str, default: typing.Any):
|
|
474
546
|
"""Defines how to pull values from an object to serialize.
|
|
@@ -720,16 +792,17 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
720
792
|
|
|
721
793
|
def loads(
|
|
722
794
|
self,
|
|
723
|
-
json_data: str,
|
|
795
|
+
json_data: str | bytes | bytearray,
|
|
724
796
|
*,
|
|
725
797
|
many: bool | None = None,
|
|
726
798
|
partial: bool | types.StrSequenceOrSet | None = None,
|
|
727
799
|
unknown: str | None = None,
|
|
728
800
|
**kwargs,
|
|
729
801
|
):
|
|
730
|
-
"""Same as :meth:`load`, except it
|
|
802
|
+
"""Same as :meth:`load`, except it uses `marshmallow.Schema.Meta.render_module` to deserialize
|
|
803
|
+
the passed string before passing data to :meth:`load`.
|
|
731
804
|
|
|
732
|
-
:param json_data: A
|
|
805
|
+
:param json_data: A string of the data to deserialize.
|
|
733
806
|
:param many: Whether to deserialize `obj` as a collection. If `None`, the
|
|
734
807
|
value for `self.many` is used.
|
|
735
808
|
:param partial: Whether to ignore missing fields and not require
|
|
@@ -752,15 +825,15 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
752
825
|
|
|
753
826
|
def _run_validator(
|
|
754
827
|
self,
|
|
755
|
-
validator_func,
|
|
828
|
+
validator_func: types.SchemaValidator,
|
|
756
829
|
output,
|
|
757
830
|
*,
|
|
758
831
|
original_data,
|
|
759
|
-
error_store,
|
|
760
|
-
many,
|
|
761
|
-
partial,
|
|
762
|
-
pass_original,
|
|
763
|
-
index=None,
|
|
832
|
+
error_store: ErrorStore,
|
|
833
|
+
many: bool,
|
|
834
|
+
partial: bool | types.StrSequenceOrSet | None,
|
|
835
|
+
pass_original: bool,
|
|
836
|
+
index: int | None = None,
|
|
764
837
|
):
|
|
765
838
|
try:
|
|
766
839
|
if pass_original: # Pass original, raw data (before unmarshalling)
|
|
@@ -768,7 +841,26 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
768
841
|
else:
|
|
769
842
|
validator_func(output, partial=partial, many=many)
|
|
770
843
|
except ValidationError as err:
|
|
771
|
-
|
|
844
|
+
field_name = err.field_name
|
|
845
|
+
data_key: str
|
|
846
|
+
if field_name == SCHEMA:
|
|
847
|
+
data_key = SCHEMA
|
|
848
|
+
else:
|
|
849
|
+
field_obj: Field | None = None
|
|
850
|
+
try:
|
|
851
|
+
field_obj = self.fields[field_name]
|
|
852
|
+
except KeyError:
|
|
853
|
+
if field_name in self.declared_fields:
|
|
854
|
+
field_obj = self.declared_fields[field_name]
|
|
855
|
+
if field_obj:
|
|
856
|
+
data_key = (
|
|
857
|
+
field_obj.data_key
|
|
858
|
+
if field_obj.data_key is not None
|
|
859
|
+
else field_name
|
|
860
|
+
)
|
|
861
|
+
else:
|
|
862
|
+
data_key = field_name
|
|
863
|
+
error_store.store_error(err.messages, data_key, index=index)
|
|
772
864
|
|
|
773
865
|
def validate(
|
|
774
866
|
self,
|
|
@@ -1016,26 +1108,26 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
1016
1108
|
self.dump_fields = dump_fields
|
|
1017
1109
|
self.load_fields = load_fields
|
|
1018
1110
|
|
|
1019
|
-
def on_bind_field(self, field_name: str, field_obj:
|
|
1111
|
+
def on_bind_field(self, field_name: str, field_obj: Field) -> None:
|
|
1020
1112
|
"""Hook to modify a field when it is bound to the `Schema <marshmallow.Schema>`.
|
|
1021
1113
|
|
|
1022
1114
|
No-op by default.
|
|
1023
1115
|
"""
|
|
1024
|
-
return
|
|
1116
|
+
return
|
|
1025
1117
|
|
|
1026
|
-
def _bind_field(self, field_name: str, field_obj:
|
|
1118
|
+
def _bind_field(self, field_name: str, field_obj: Field) -> None:
|
|
1027
1119
|
"""Bind field to the schema, setting any necessary attributes on the
|
|
1028
1120
|
field (e.g. parent and name).
|
|
1029
1121
|
|
|
1030
1122
|
Also set field load_only and dump_only values if field_name was
|
|
1031
|
-
specified in
|
|
1123
|
+
specified in `class Meta <marshmallow.Schema.Meta>`.
|
|
1032
1124
|
"""
|
|
1033
1125
|
if field_name in self.load_only:
|
|
1034
1126
|
field_obj.load_only = True
|
|
1035
1127
|
if field_name in self.dump_only:
|
|
1036
1128
|
field_obj.dump_only = True
|
|
1037
1129
|
try:
|
|
1038
|
-
field_obj._bind_to_schema(field_name, self)
|
|
1130
|
+
field_obj._bind_to_schema(field_name, self) # noqa: SLF001
|
|
1039
1131
|
except TypeError as error:
|
|
1040
1132
|
# Field declared as a class, not an instance. Ignore type checking because
|
|
1041
1133
|
# we handle unsupported arg types, i.e. this is dead code from
|
|
@@ -1044,10 +1136,10 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
1044
1136
|
msg = (
|
|
1045
1137
|
f'Field for "{field_name}" must be declared as a '
|
|
1046
1138
|
"Field instance, not a class. "
|
|
1047
|
-
f'Did you mean "fields.{field_obj.__name__}()"?' # type: ignore
|
|
1139
|
+
f'Did you mean "fields.{field_obj.__name__}()"?' # type: ignore[attr-defined]
|
|
1048
1140
|
)
|
|
1049
1141
|
raise TypeError(msg) from error
|
|
1050
|
-
raise
|
|
1142
|
+
raise
|
|
1051
1143
|
self.on_bind_field(field_name, field_obj)
|
|
1052
1144
|
|
|
1053
1145
|
def _invoke_dump_processors(
|
|
@@ -1059,10 +1151,9 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
1059
1151
|
data = self._invoke_processors(
|
|
1060
1152
|
tag, pass_many=False, data=data, many=many, original_data=original_data
|
|
1061
1153
|
)
|
|
1062
|
-
|
|
1154
|
+
return self._invoke_processors(
|
|
1063
1155
|
tag, pass_many=True, data=data, many=many, original_data=original_data
|
|
1064
1156
|
)
|
|
1065
|
-
return data
|
|
1066
1157
|
|
|
1067
1158
|
def _invoke_load_processors(
|
|
1068
1159
|
self,
|
|
@@ -1083,7 +1174,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
1083
1174
|
original_data=original_data,
|
|
1084
1175
|
partial=partial,
|
|
1085
1176
|
)
|
|
1086
|
-
|
|
1177
|
+
return self._invoke_processors(
|
|
1087
1178
|
tag,
|
|
1088
1179
|
pass_many=False,
|
|
1089
1180
|
data=data,
|
|
@@ -1091,7 +1182,6 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
1091
1182
|
original_data=original_data,
|
|
1092
1183
|
partial=partial,
|
|
1093
1184
|
)
|
|
1094
|
-
return data
|
|
1095
1185
|
|
|
1096
1186
|
def _invoke_field_validators(self, *, error_store: ErrorStore, data, many: bool):
|
|
1097
1187
|
for attr_name, _, validator_kwargs in self._hooks[VALIDATES]:
|
|
@@ -1123,7 +1213,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
1123
1213
|
index=(idx if self.opts.index_errors else None),
|
|
1124
1214
|
)
|
|
1125
1215
|
if validated_value is missing:
|
|
1126
|
-
|
|
1216
|
+
item.pop(field_name, None)
|
|
1127
1217
|
else:
|
|
1128
1218
|
try:
|
|
1129
1219
|
value = data[field_obj.attribute or field_name]
|
|
@@ -1202,15 +1292,14 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
1202
1292
|
if pass_original:
|
|
1203
1293
|
data = [
|
|
1204
1294
|
processor(item, original, many=many, **kwargs)
|
|
1205
|
-
for item, original in
|
|
1295
|
+
for item, original in zip_longest(data, original_data)
|
|
1206
1296
|
]
|
|
1207
1297
|
else:
|
|
1208
1298
|
data = [processor(item, many=many, **kwargs) for item in data]
|
|
1299
|
+
elif pass_original:
|
|
1300
|
+
data = processor(data, original_data, many=many, **kwargs)
|
|
1209
1301
|
else:
|
|
1210
|
-
|
|
1211
|
-
data = processor(data, original_data, many=many, **kwargs)
|
|
1212
|
-
else:
|
|
1213
|
-
data = processor(data, many=many, **kwargs)
|
|
1302
|
+
data = processor(data, many=many, **kwargs)
|
|
1214
1303
|
return data
|
|
1215
1304
|
|
|
1216
1305
|
|
marshmallow/types.py
CHANGED
|
@@ -14,3 +14,27 @@ StrSequenceOrSet = typing.Union[typing.Sequence[str], typing.AbstractSet[str]]
|
|
|
14
14
|
|
|
15
15
|
#: Type for validator functions
|
|
16
16
|
Validator = typing.Callable[[typing.Any], typing.Any]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class SchemaValidator(typing.Protocol):
|
|
20
|
+
def __call__(
|
|
21
|
+
self,
|
|
22
|
+
output: typing.Any,
|
|
23
|
+
original_data: typing.Any = ...,
|
|
24
|
+
*,
|
|
25
|
+
partial: bool | StrSequenceOrSet | None = None,
|
|
26
|
+
many: bool = False,
|
|
27
|
+
) -> None: ...
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class RenderModule(typing.Protocol):
|
|
31
|
+
def dumps(
|
|
32
|
+
self, obj: typing.Any, *args: typing.Any, **kwargs: typing.Any
|
|
33
|
+
) -> str: ...
|
|
34
|
+
|
|
35
|
+
def loads(
|
|
36
|
+
self,
|
|
37
|
+
json_data: str | bytes | bytearray,
|
|
38
|
+
*args: typing.Any,
|
|
39
|
+
**kwargs: typing.Any,
|
|
40
|
+
) -> typing.Any: ...
|
marshmallow/utils.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Utility methods for marshmallow."""
|
|
2
2
|
|
|
3
|
+
# ruff: noqa: T201, T203
|
|
3
4
|
from __future__ import annotations
|
|
4
5
|
|
|
5
6
|
import collections
|
|
@@ -137,7 +138,7 @@ _iso8601_time_re = re.compile(
|
|
|
137
138
|
)
|
|
138
139
|
|
|
139
140
|
|
|
140
|
-
def get_fixed_timezone(offset:
|
|
141
|
+
def get_fixed_timezone(offset: float | dt.timedelta) -> dt.timezone:
|
|
141
142
|
"""Return a tzinfo instance with a fixed offset from UTC."""
|
|
142
143
|
if isinstance(offset, dt.timedelta):
|
|
143
144
|
offset = offset.total_seconds() // 60
|
|
@@ -169,7 +170,7 @@ def from_iso_datetime(value):
|
|
|
169
170
|
tzinfo = get_fixed_timezone(offset)
|
|
170
171
|
kw = {k: int(v) for k, v in kw.items() if v is not None}
|
|
171
172
|
kw["tzinfo"] = tzinfo
|
|
172
|
-
return dt.datetime(**kw)
|
|
173
|
+
return dt.datetime(**kw) # noqa: DTZ001
|
|
173
174
|
|
|
174
175
|
|
|
175
176
|
def from_iso_time(value):
|
|
@@ -279,17 +280,15 @@ def get_value(obj, key: int | str, default=missing):
|
|
|
279
280
|
"""
|
|
280
281
|
if not isinstance(key, int) and "." in key:
|
|
281
282
|
return _get_value_for_keys(obj, key.split("."), default)
|
|
282
|
-
|
|
283
|
-
return _get_value_for_key(obj, key, default)
|
|
283
|
+
return _get_value_for_key(obj, key, default)
|
|
284
284
|
|
|
285
285
|
|
|
286
286
|
def _get_value_for_keys(obj, keys, default):
|
|
287
287
|
if len(keys) == 1:
|
|
288
288
|
return _get_value_for_key(obj, keys[0], default)
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
)
|
|
289
|
+
return _get_value_for_keys(
|
|
290
|
+
_get_value_for_key(obj, keys[0], default), keys[1:], default
|
|
291
|
+
)
|
|
293
292
|
|
|
294
293
|
|
|
295
294
|
def _get_value_for_key(obj, key, default):
|
|
@@ -318,7 +317,7 @@ def set_value(dct: dict[str, typing.Any], key: str, value: typing.Any):
|
|
|
318
317
|
target = dct.setdefault(head, {})
|
|
319
318
|
if not isinstance(target, dict):
|
|
320
319
|
raise ValueError(
|
|
321
|
-
f"Cannot set {key} in {head}
|
|
320
|
+
f"Cannot set {key} in {head} due to existing value: {target}"
|
|
322
321
|
)
|
|
323
322
|
set_value(target, rest, value)
|
|
324
323
|
else:
|
|
@@ -360,10 +359,9 @@ def resolve_field_instance(cls_or_instance: type[Field] | Field) -> Field:
|
|
|
360
359
|
if not issubclass(cls_or_instance, FieldABC):
|
|
361
360
|
raise FieldInstanceResolutionError
|
|
362
361
|
return cls_or_instance()
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
return cls_or_instance
|
|
362
|
+
if not isinstance(cls_or_instance, FieldABC):
|
|
363
|
+
raise FieldInstanceResolutionError
|
|
364
|
+
return cls_or_instance
|
|
367
365
|
|
|
368
366
|
|
|
369
367
|
def timedelta_to_microseconds(value: dt.timedelta) -> int:
|
marshmallow/validate.py
CHANGED
|
@@ -9,10 +9,12 @@ from abc import ABC, abstractmethod
|
|
|
9
9
|
from itertools import zip_longest
|
|
10
10
|
from operator import attrgetter
|
|
11
11
|
|
|
12
|
-
from marshmallow import types
|
|
13
12
|
from marshmallow.exceptions import ValidationError
|
|
14
13
|
from marshmallow.warnings import ChangedInMarshmallow4Warning
|
|
15
14
|
|
|
15
|
+
if typing.TYPE_CHECKING:
|
|
16
|
+
from marshmallow import types
|
|
17
|
+
|
|
16
18
|
_T = typing.TypeVar("_T")
|
|
17
19
|
|
|
18
20
|
|
|
@@ -73,8 +75,8 @@ class And(Validator):
|
|
|
73
75
|
return f"validators={self.validators!r}"
|
|
74
76
|
|
|
75
77
|
def __call__(self, value: typing.Any) -> typing.Any:
|
|
76
|
-
errors = []
|
|
77
|
-
kwargs = {}
|
|
78
|
+
errors: list[str | dict] = []
|
|
79
|
+
kwargs: dict[str, typing.Any] = {}
|
|
78
80
|
for validator in self.validators:
|
|
79
81
|
try:
|
|
80
82
|
r = validator(value)
|
|
@@ -85,14 +87,13 @@ class And(Validator):
|
|
|
85
87
|
ChangedInMarshmallow4Warning,
|
|
86
88
|
stacklevel=2,
|
|
87
89
|
)
|
|
88
|
-
raise ValidationError(self.error)
|
|
90
|
+
raise ValidationError(self.error) # noqa: TRY301
|
|
89
91
|
except ValidationError as err:
|
|
90
92
|
kwargs.update(err.kwargs)
|
|
91
93
|
if isinstance(err.messages, dict):
|
|
92
94
|
errors.append(err.messages)
|
|
93
95
|
else:
|
|
94
|
-
|
|
95
|
-
errors.extend(typing.cast(list, err.messages))
|
|
96
|
+
errors.extend(err.messages)
|
|
96
97
|
if errors:
|
|
97
98
|
raise ValidationError(errors, **kwargs)
|
|
98
99
|
return value
|
|
@@ -115,7 +116,7 @@ class URL(Validator):
|
|
|
115
116
|
self._memoized = {}
|
|
116
117
|
|
|
117
118
|
def _regex_generator(
|
|
118
|
-
self, relative: bool, absolute: bool, require_tld: bool
|
|
119
|
+
self, *, relative: bool, absolute: bool, require_tld: bool
|
|
119
120
|
) -> typing.Pattern:
|
|
120
121
|
hostname_variants = [
|
|
121
122
|
# a normal domain name, expressed in [A-Z0-9] chars with hyphens allowed only in the middle
|
|
@@ -169,12 +170,12 @@ class URL(Validator):
|
|
|
169
170
|
return re.compile("".join(parts), re.IGNORECASE)
|
|
170
171
|
|
|
171
172
|
def __call__(
|
|
172
|
-
self, relative: bool, absolute: bool, require_tld: bool
|
|
173
|
+
self, *, relative: bool, absolute: bool, require_tld: bool
|
|
173
174
|
) -> typing.Pattern:
|
|
174
175
|
key = (relative, absolute, require_tld)
|
|
175
176
|
if key not in self._memoized:
|
|
176
177
|
self._memoized[key] = self._regex_generator(
|
|
177
|
-
relative, absolute, require_tld
|
|
178
|
+
relative=relative, absolute=absolute, require_tld=require_tld
|
|
178
179
|
)
|
|
179
180
|
|
|
180
181
|
return self._memoized[key]
|
|
@@ -215,14 +216,24 @@ class URL(Validator):
|
|
|
215
216
|
raise ValidationError(message)
|
|
216
217
|
|
|
217
218
|
# Check first if the scheme is valid
|
|
219
|
+
scheme = None
|
|
218
220
|
if "://" in value:
|
|
219
221
|
scheme = value.split("://")[0].lower()
|
|
220
222
|
if scheme not in self.schemes:
|
|
221
223
|
raise ValidationError(message)
|
|
222
224
|
|
|
223
|
-
regex = self._regex(
|
|
225
|
+
regex = self._regex(
|
|
226
|
+
relative=self.relative, absolute=self.absolute, require_tld=self.require_tld
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
# Hostname is optional for file URLS. If absent it means `localhost`.
|
|
230
|
+
# Fill it in for the validation if needed
|
|
231
|
+
if scheme == "file" and value.startswith("file:///"):
|
|
232
|
+
matched = regex.search(value.replace("file:///", "file://localhost/", 1))
|
|
233
|
+
else:
|
|
234
|
+
matched = regex.search(value)
|
|
224
235
|
|
|
225
|
-
if not
|
|
236
|
+
if not matched:
|
|
226
237
|
raise ValidationError(message)
|
|
227
238
|
|
|
228
239
|
return value
|
|
@@ -318,8 +329,8 @@ class Range(Validator):
|
|
|
318
329
|
|
|
319
330
|
def __init__(
|
|
320
331
|
self,
|
|
321
|
-
min=None,
|
|
322
|
-
max=None,
|
|
332
|
+
min=None, # noqa: A002
|
|
333
|
+
max=None, # noqa: A002
|
|
323
334
|
*,
|
|
324
335
|
min_inclusive: bool = True,
|
|
325
336
|
max_inclusive: bool = True,
|
|
@@ -365,6 +376,9 @@ class Range(Validator):
|
|
|
365
376
|
return value
|
|
366
377
|
|
|
367
378
|
|
|
379
|
+
_SizedT = typing.TypeVar("_SizedT", bound=typing.Sized)
|
|
380
|
+
|
|
381
|
+
|
|
368
382
|
class Length(Validator):
|
|
369
383
|
"""Validator which succeeds if the value passed to it has a
|
|
370
384
|
length between a minimum and maximum. Uses len(), so it
|
|
@@ -387,8 +401,8 @@ class Length(Validator):
|
|
|
387
401
|
|
|
388
402
|
def __init__(
|
|
389
403
|
self,
|
|
390
|
-
min: int | None = None,
|
|
391
|
-
max: int | None = None,
|
|
404
|
+
min: int | None = None, # noqa: A002
|
|
405
|
+
max: int | None = None, # noqa: A002
|
|
392
406
|
*,
|
|
393
407
|
equal: int | None = None,
|
|
394
408
|
error: str | None = None,
|
|
@@ -407,12 +421,12 @@ class Length(Validator):
|
|
|
407
421
|
def _repr_args(self) -> str:
|
|
408
422
|
return f"min={self.min!r}, max={self.max!r}, equal={self.equal!r}"
|
|
409
423
|
|
|
410
|
-
def _format_error(self, value:
|
|
424
|
+
def _format_error(self, value: _SizedT, message: str) -> str:
|
|
411
425
|
return (self.error or message).format(
|
|
412
426
|
input=value, min=self.min, max=self.max, equal=self.equal
|
|
413
427
|
)
|
|
414
428
|
|
|
415
|
-
def __call__(self, value:
|
|
429
|
+
def __call__(self, value: _SizedT) -> _SizedT:
|
|
416
430
|
length = len(value)
|
|
417
431
|
|
|
418
432
|
if self.equal is not None:
|
|
@@ -531,7 +545,7 @@ class Predicate(Validator):
|
|
|
531
545
|
def _format_error(self, value: typing.Any) -> str:
|
|
532
546
|
return self.error.format(input=value, method=self.method)
|
|
533
547
|
|
|
534
|
-
def __call__(self, value:
|
|
548
|
+
def __call__(self, value: _T) -> _T:
|
|
535
549
|
method = getattr(value, self.method)
|
|
536
550
|
|
|
537
551
|
if not method(**self.kwargs):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: marshmallow
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.26.0
|
|
4
4
|
Summary: A lightweight library for converting complex datatypes to and from native Python datatypes.
|
|
5
5
|
Author-email: Steven Loria <sloria1@gmail.com>
|
|
6
6
|
Maintainer-email: Steven Loria <sloria1@gmail.com>, Jérôme Lafréchoux <jerome@jolimont.fr>, Jared Deckard <jared@shademaps.com>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
marshmallow/__init__.py,sha256=TU1arjtOLs87YDfW4Z35YVbaY2CUXK-VgylcuL8LaQg,2387
|
|
2
|
+
marshmallow/base.py,sha256=39W78-rnuzzx5T95YWBEECzjtqxdUA8XzYJNHd39VLg,1362
|
|
3
|
+
marshmallow/class_registry.py,sha256=HTC9srCEaRsiy5L_vUKQso7IQfeZeRXxZfz4_2NitoM,3029
|
|
4
|
+
marshmallow/decorators.py,sha256=pMjGPaXBZCRfAdQS3Bz5ieTZGA3BOv61FdTPsLwCtMQ,8749
|
|
5
|
+
marshmallow/error_store.py,sha256=iCPSdw8nJGiS4fjWuIAY1aSI_Hhckcdo3l_g-7pjaMw,2240
|
|
6
|
+
marshmallow/exceptions.py,sha256=DuARdOcirCdJxmlp16V97hQKAXOokvdW12jXtYOlGyk,2326
|
|
7
|
+
marshmallow/fields.py,sha256=DjjL5m5YZbwMVHE97MQDNfoFf_e75qMR3eIs17UuVMc,74763
|
|
8
|
+
marshmallow/orderedset.py,sha256=-Lq83AWIIFs2bxptDwkHtfQ63ebX3WD3R6N3B5rRnVI,2936
|
|
9
|
+
marshmallow/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
marshmallow/schema.py,sha256=bSBynndwixq1zcOOPdoZkX7DmpVSpHBaNEh1zqiD888,52128
|
|
11
|
+
marshmallow/types.py,sha256=VY0_D-Xou7nKjcvWB1iccm8cZtxI3rkis1nhNelNn5Q,979
|
|
12
|
+
marshmallow/utils.py,sha256=tLzu9FDL3Ph51qKsoqWIyPSwg8dZ8rzjeXXGLUndHFE,11943
|
|
13
|
+
marshmallow/validate.py,sha256=Fx3F8F20dBGg-Wrv84Chx5SYedX9E0l592hR4MxS0kQ,24652
|
|
14
|
+
marshmallow/warnings.py,sha256=YHC0kQQBbTKCiA1FuwnbnXqrph7oKuU9BjTV4cxwnzE,192
|
|
15
|
+
marshmallow-3.26.0.dist-info/LICENSE,sha256=kGtdkFHkJhRMsXOtkRZnuOvQWpxYTCwmwTWzKj7RIAE,1064
|
|
16
|
+
marshmallow-3.26.0.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
|
|
17
|
+
marshmallow-3.26.0.dist-info/METADATA,sha256=Scbh6z8NMiWGVQ3EYrwo6H9-PNczOrepN_tFFZu_A9k,7310
|
|
18
|
+
marshmallow-3.26.0.dist-info/RECORD,,
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
marshmallow/__init__.py,sha256=C-zbaQJ9dlJLJxotIqTa5OOaD6ojGNRqW8moGrMsGr8,2387
|
|
2
|
-
marshmallow/base.py,sha256=39W78-rnuzzx5T95YWBEECzjtqxdUA8XzYJNHd39VLg,1362
|
|
3
|
-
marshmallow/class_registry.py,sha256=JaA2tJo53ZNzTVoJM_MuN_WKaadEskIcxjOrLKy76iQ,3015
|
|
4
|
-
marshmallow/decorators.py,sha256=3U7gLAmiotqoVC8O2j_7kwUS2ceoJUdkAcuk8SuH9oQ,8486
|
|
5
|
-
marshmallow/error_store.py,sha256=A7AxgLMw9ffSmaxRH4x3wcBWibx-DuGH4LwSDpVn50I,2223
|
|
6
|
-
marshmallow/exceptions.py,sha256=DuARdOcirCdJxmlp16V97hQKAXOokvdW12jXtYOlGyk,2326
|
|
7
|
-
marshmallow/fields.py,sha256=WgtoWGZR3WLkqEf056NTUlHurLVlfCho3TQQ5zSlMqc,74688
|
|
8
|
-
marshmallow/orderedset.py,sha256=C2aAG6w1faIL1phinbAltbe3AUAnF5MN6n7fzESNDhI,2922
|
|
9
|
-
marshmallow/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
marshmallow/schema.py,sha256=w81gPdm9L4_NaloLb8Z04U5shzUbuDOtDPeJL8uMHH0,48653
|
|
11
|
-
marshmallow/types.py,sha256=csTzHJh-tbNs9t9eDRG6HjJMNawSVNw27mmor6w4ztM,423
|
|
12
|
-
marshmallow/utils.py,sha256=Xvpd7h3IdB8tRKfa-u95DbOXjLgDzWRNJIwjsTNI9n0,11970
|
|
13
|
-
marshmallow/validate.py,sha256=D9CbPe2vywalBcTa3iuwCqIK6mNhDn5pQaJ_kn_6_y4,24116
|
|
14
|
-
marshmallow/warnings.py,sha256=YHC0kQQBbTKCiA1FuwnbnXqrph7oKuU9BjTV4cxwnzE,192
|
|
15
|
-
marshmallow-3.25.1.dist-info/LICENSE,sha256=kGtdkFHkJhRMsXOtkRZnuOvQWpxYTCwmwTWzKj7RIAE,1064
|
|
16
|
-
marshmallow-3.25.1.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
|
|
17
|
-
marshmallow-3.25.1.dist-info/METADATA,sha256=aBxXuzqfK0y4iUwJb9zVGHq9vcndAyUzCRZRVVTLmf8,7310
|
|
18
|
-
marshmallow-3.25.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|