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/fields.py
CHANGED
|
@@ -1,42 +1,43 @@
|
|
|
1
|
-
# ruff: noqa:
|
|
1
|
+
# ruff: noqa: SLF001
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
+
import abc
|
|
4
5
|
import collections
|
|
5
6
|
import copy
|
|
6
7
|
import datetime as dt
|
|
7
8
|
import decimal
|
|
9
|
+
import email.utils
|
|
8
10
|
import ipaddress
|
|
9
11
|
import math
|
|
10
12
|
import numbers
|
|
11
13
|
import typing
|
|
12
14
|
import uuid
|
|
13
|
-
import warnings
|
|
14
15
|
from collections.abc import Mapping as _Mapping
|
|
16
|
+
from enum import Enum as EnumType
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
from typing import Unpack
|
|
20
|
+
except ImportError: # Remove when dropping Python 3.10
|
|
21
|
+
from typing_extensions import Unpack
|
|
22
|
+
|
|
23
|
+
# Remove when dropping Python 3.10
|
|
24
|
+
try:
|
|
25
|
+
from backports.datetime_fromisoformat import MonkeyPatch
|
|
26
|
+
except ImportError:
|
|
27
|
+
pass
|
|
28
|
+
else:
|
|
29
|
+
MonkeyPatch.patch_fromisoformat()
|
|
15
30
|
|
|
16
31
|
from marshmallow import class_registry, types, utils, validate
|
|
17
|
-
from marshmallow.
|
|
32
|
+
from marshmallow.constants import missing as missing_
|
|
18
33
|
from marshmallow.exceptions import (
|
|
19
|
-
FieldInstanceResolutionError,
|
|
20
34
|
StringNotCollectionError,
|
|
21
35
|
ValidationError,
|
|
22
|
-
|
|
23
|
-
from marshmallow.utils import (
|
|
24
|
-
is_aware,
|
|
25
|
-
is_collection,
|
|
26
|
-
resolve_field_instance,
|
|
27
|
-
)
|
|
28
|
-
from marshmallow.utils import (
|
|
29
|
-
missing as missing_,
|
|
36
|
+
_FieldInstanceResolutionError,
|
|
30
37
|
)
|
|
31
38
|
from marshmallow.validate import And, Length
|
|
32
|
-
from marshmallow.warnings import (
|
|
33
|
-
ChangedInMarshmallow4Warning,
|
|
34
|
-
RemovedInMarshmallow4Warning,
|
|
35
|
-
)
|
|
36
39
|
|
|
37
40
|
if typing.TYPE_CHECKING:
|
|
38
|
-
from enum import Enum as EnumType
|
|
39
|
-
|
|
40
41
|
from marshmallow.schema import Schema, SchemaMeta
|
|
41
42
|
|
|
42
43
|
|
|
@@ -80,9 +81,40 @@ __all__ = [
|
|
|
80
81
|
"Url",
|
|
81
82
|
]
|
|
82
83
|
|
|
84
|
+
_InternalT = typing.TypeVar("_InternalT")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class _BaseFieldKwargs(typing.TypedDict, total=False):
|
|
88
|
+
load_default: typing.Any
|
|
89
|
+
dump_default: typing.Any
|
|
90
|
+
data_key: str | None
|
|
91
|
+
attribute: str | None
|
|
92
|
+
validate: types.Validator | typing.Iterable[types.Validator] | None
|
|
93
|
+
required: bool
|
|
94
|
+
allow_none: bool | None
|
|
95
|
+
load_only: bool
|
|
96
|
+
dump_only: bool
|
|
97
|
+
error_messages: dict[str, str] | None
|
|
98
|
+
metadata: typing.Mapping[str, typing.Any] | None
|
|
83
99
|
|
|
84
|
-
|
|
85
|
-
|
|
100
|
+
|
|
101
|
+
def _resolve_field_instance(cls_or_instance: Field | type[Field]) -> Field:
|
|
102
|
+
"""Return a Field instance from a Field class or instance.
|
|
103
|
+
|
|
104
|
+
:param cls_or_instance: Field class or instance.
|
|
105
|
+
"""
|
|
106
|
+
if isinstance(cls_or_instance, type):
|
|
107
|
+
if not issubclass(cls_or_instance, Field):
|
|
108
|
+
raise _FieldInstanceResolutionError
|
|
109
|
+
return cls_or_instance()
|
|
110
|
+
if not isinstance(cls_or_instance, Field):
|
|
111
|
+
raise _FieldInstanceResolutionError
|
|
112
|
+
return cls_or_instance
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class Field(typing.Generic[_InternalT]):
|
|
116
|
+
"""Base field from which all other fields inherit.
|
|
117
|
+
This class should not be used directly within Schemas.
|
|
86
118
|
|
|
87
119
|
:param dump_default: If set, this value will be used during serialization if the
|
|
88
120
|
input value is missing. If not set, the field will be excluded from the
|
|
@@ -120,13 +152,13 @@ class Field(FieldABC):
|
|
|
120
152
|
.. versionchanged:: 3.0.0b8
|
|
121
153
|
Add ``data_key`` parameter for the specifying the key in the input and
|
|
122
154
|
output data. This parameter replaced both ``load_from`` and ``dump_to``.
|
|
123
|
-
|
|
124
155
|
.. versionchanged:: 3.13.0
|
|
125
156
|
Replace ``missing`` and ``default`` parameters with ``load_default`` and ``dump_default``.
|
|
126
|
-
|
|
127
157
|
.. versionchanged:: 3.24.0
|
|
128
158
|
`Field <marshmallow.fields.Field>` should no longer be used as a field within a `Schema <marshmallow.Schema>`.
|
|
129
159
|
Use `Raw <marshmallow.fields.Raw>` or another `Field <marshmallow.fields.Field>` subclass instead.
|
|
160
|
+
.. versionchanged:: 4.0.0
|
|
161
|
+
Remove ``context`` property.
|
|
130
162
|
"""
|
|
131
163
|
|
|
132
164
|
# Some fields, such as Method fields and Function fields, are not expected
|
|
@@ -147,9 +179,7 @@ class Field(FieldABC):
|
|
|
147
179
|
self,
|
|
148
180
|
*,
|
|
149
181
|
load_default: typing.Any = missing_,
|
|
150
|
-
missing: typing.Any = missing_,
|
|
151
182
|
dump_default: typing.Any = missing_,
|
|
152
|
-
default: typing.Any = missing_,
|
|
153
183
|
data_key: str | None = None,
|
|
154
184
|
attribute: str | None = None,
|
|
155
185
|
validate: types.Validator | typing.Iterable[types.Validator] | None = None,
|
|
@@ -159,34 +189,7 @@ class Field(FieldABC):
|
|
|
159
189
|
dump_only: bool = False,
|
|
160
190
|
error_messages: dict[str, str] | None = None,
|
|
161
191
|
metadata: typing.Mapping[str, typing.Any] | None = None,
|
|
162
|
-
**additional_metadata,
|
|
163
192
|
) -> None:
|
|
164
|
-
if self.__class__ is Field:
|
|
165
|
-
warnings.warn(
|
|
166
|
-
"`Field` should not be instantiated. Use `fields.Raw` or "
|
|
167
|
-
"another field subclass instead.",
|
|
168
|
-
ChangedInMarshmallow4Warning,
|
|
169
|
-
stacklevel=2,
|
|
170
|
-
)
|
|
171
|
-
# handle deprecated `default` and `missing` parameters
|
|
172
|
-
if default is not missing_:
|
|
173
|
-
warnings.warn(
|
|
174
|
-
"The 'default' argument to fields is deprecated. "
|
|
175
|
-
"Use 'dump_default' instead.",
|
|
176
|
-
RemovedInMarshmallow4Warning,
|
|
177
|
-
stacklevel=2,
|
|
178
|
-
)
|
|
179
|
-
if dump_default is missing_:
|
|
180
|
-
dump_default = default
|
|
181
|
-
if missing is not missing_:
|
|
182
|
-
warnings.warn(
|
|
183
|
-
"The 'missing' argument to fields is deprecated. "
|
|
184
|
-
"Use 'load_default' instead.",
|
|
185
|
-
RemovedInMarshmallow4Warning,
|
|
186
|
-
stacklevel=2,
|
|
187
|
-
)
|
|
188
|
-
if load_default is missing_:
|
|
189
|
-
load_default = missing
|
|
190
193
|
self.dump_default = dump_default
|
|
191
194
|
self.load_default = load_default
|
|
192
195
|
|
|
@@ -215,16 +218,7 @@ class Field(FieldABC):
|
|
|
215
218
|
self.required = required
|
|
216
219
|
|
|
217
220
|
metadata = metadata or {}
|
|
218
|
-
self.metadata =
|
|
219
|
-
if additional_metadata:
|
|
220
|
-
warnings.warn(
|
|
221
|
-
"Passing field metadata as keyword arguments is deprecated. Use the "
|
|
222
|
-
"explicit `metadata=...` argument instead. "
|
|
223
|
-
f"Additional metadata: {additional_metadata}",
|
|
224
|
-
RemovedInMarshmallow4Warning,
|
|
225
|
-
stacklevel=2,
|
|
226
|
-
)
|
|
227
|
-
|
|
221
|
+
self.metadata = metadata
|
|
228
222
|
# Collect default error message from self and parent classes
|
|
229
223
|
messages: dict[str, str] = {}
|
|
230
224
|
for cls in reversed(self.__class__.__mro__):
|
|
@@ -257,7 +251,7 @@ class Field(FieldABC):
|
|
|
257
251
|
typing.Callable[[typing.Any, str, typing.Any], typing.Any] | None
|
|
258
252
|
) = None,
|
|
259
253
|
default: typing.Any = missing_,
|
|
260
|
-
):
|
|
254
|
+
) -> _InternalT:
|
|
261
255
|
"""Return the value for a given key from an object.
|
|
262
256
|
|
|
263
257
|
:param obj: The object to get the value from.
|
|
@@ -269,7 +263,7 @@ class Field(FieldABC):
|
|
|
269
263
|
check_key = attr if self.attribute is None else self.attribute
|
|
270
264
|
return accessor_func(obj, check_key, default)
|
|
271
265
|
|
|
272
|
-
def _validate(self, value: typing.Any):
|
|
266
|
+
def _validate(self, value: typing.Any) -> None:
|
|
273
267
|
"""Perform validation on ``value``. Raise a :exc:`ValidationError` if validation
|
|
274
268
|
does not succeed.
|
|
275
269
|
"""
|
|
@@ -277,7 +271,7 @@ class Field(FieldABC):
|
|
|
277
271
|
|
|
278
272
|
@property
|
|
279
273
|
def _validate_all(self) -> typing.Callable[[typing.Any], None]:
|
|
280
|
-
return And(*self.validators
|
|
274
|
+
return And(*self.validators)
|
|
281
275
|
|
|
282
276
|
def make_error(self, key: str, **kwargs) -> ValidationError:
|
|
283
277
|
"""Helper method to make a `ValidationError` with an error message
|
|
@@ -296,20 +290,6 @@ class Field(FieldABC):
|
|
|
296
290
|
msg = msg.format(**kwargs)
|
|
297
291
|
return ValidationError(msg)
|
|
298
292
|
|
|
299
|
-
def fail(self, key: str, **kwargs):
|
|
300
|
-
"""Helper method that raises a `ValidationError` with an error message
|
|
301
|
-
from ``self.error_messages``.
|
|
302
|
-
|
|
303
|
-
.. deprecated:: 3.0.0
|
|
304
|
-
Use `make_error <marshmallow.fields.Field.make_error>` instead.
|
|
305
|
-
"""
|
|
306
|
-
warnings.warn(
|
|
307
|
-
f'`Field.fail` is deprecated. Use `raise self.make_error("{key}", ...)` instead.',
|
|
308
|
-
RemovedInMarshmallow4Warning,
|
|
309
|
-
stacklevel=2,
|
|
310
|
-
)
|
|
311
|
-
raise self.make_error(key=key, **kwargs)
|
|
312
|
-
|
|
313
293
|
def _validate_missing(self, value: typing.Any) -> None:
|
|
314
294
|
"""Validate missing values. Raise a :exc:`ValidationError` if
|
|
315
295
|
`value` should be considered missing.
|
|
@@ -347,13 +327,33 @@ class Field(FieldABC):
|
|
|
347
327
|
value = None
|
|
348
328
|
return self._serialize(value, attr, obj, **kwargs)
|
|
349
329
|
|
|
330
|
+
# If value is None, None may be returned
|
|
331
|
+
@typing.overload
|
|
332
|
+
def deserialize(
|
|
333
|
+
self,
|
|
334
|
+
value: None,
|
|
335
|
+
attr: str | None = None,
|
|
336
|
+
data: typing.Mapping[str, typing.Any] | None = None,
|
|
337
|
+
**kwargs,
|
|
338
|
+
) -> None | _InternalT: ...
|
|
339
|
+
|
|
340
|
+
# If value is not None, internal type is returned
|
|
341
|
+
@typing.overload
|
|
350
342
|
def deserialize(
|
|
351
343
|
self,
|
|
352
344
|
value: typing.Any,
|
|
353
345
|
attr: str | None = None,
|
|
354
346
|
data: typing.Mapping[str, typing.Any] | None = None,
|
|
355
347
|
**kwargs,
|
|
356
|
-
):
|
|
348
|
+
) -> _InternalT: ...
|
|
349
|
+
|
|
350
|
+
def deserialize(
|
|
351
|
+
self,
|
|
352
|
+
value: typing.Any,
|
|
353
|
+
attr: str | None = None,
|
|
354
|
+
data: typing.Mapping[str, typing.Any] | None = None,
|
|
355
|
+
**kwargs,
|
|
356
|
+
) -> _InternalT | None:
|
|
357
357
|
"""Deserialize ``value``.
|
|
358
358
|
|
|
359
359
|
:param value: The value to deserialize.
|
|
@@ -377,21 +377,21 @@ class Field(FieldABC):
|
|
|
377
377
|
|
|
378
378
|
# Methods for concrete classes to override.
|
|
379
379
|
|
|
380
|
-
def _bind_to_schema(self, field_name: str,
|
|
380
|
+
def _bind_to_schema(self, field_name: str, parent: Schema | Field) -> None:
|
|
381
381
|
"""Update field with values from its parent schema. Called by
|
|
382
|
-
|
|
382
|
+
`Schema._bind_field <marshmallow.Schema._bind_field>`.
|
|
383
383
|
|
|
384
384
|
:param field_name: Field name set in schema.
|
|
385
|
-
:param
|
|
385
|
+
:param parent: Parent object.
|
|
386
386
|
"""
|
|
387
|
-
self.parent = self.parent or
|
|
387
|
+
self.parent = self.parent or parent
|
|
388
388
|
self.name = self.name or field_name
|
|
389
389
|
self.root = self.root or (
|
|
390
|
-
self.parent.root if isinstance(self.parent,
|
|
390
|
+
self.parent.root if isinstance(self.parent, Field) else self.parent
|
|
391
391
|
)
|
|
392
392
|
|
|
393
393
|
def _serialize(
|
|
394
|
-
self, value:
|
|
394
|
+
self, value: _InternalT | None, attr: str | None, obj: typing.Any, **kwargs
|
|
395
395
|
) -> typing.Any:
|
|
396
396
|
"""Serializes ``value`` to a basic Python datatype. Noop by default.
|
|
397
397
|
Concrete :class:`Field` classes should implement this method.
|
|
@@ -418,7 +418,7 @@ class Field(FieldABC):
|
|
|
418
418
|
attr: str | None,
|
|
419
419
|
data: typing.Mapping[str, typing.Any] | None,
|
|
420
420
|
**kwargs,
|
|
421
|
-
) ->
|
|
421
|
+
) -> _InternalT:
|
|
422
422
|
"""Deserialize value. Concrete :class:`Field` classes should implement this method.
|
|
423
423
|
|
|
424
424
|
:param value: The value to be deserialized.
|
|
@@ -433,59 +433,8 @@ class Field(FieldABC):
|
|
|
433
433
|
"""
|
|
434
434
|
return value
|
|
435
435
|
|
|
436
|
-
# Properties
|
|
437
|
-
|
|
438
|
-
@property
|
|
439
|
-
def context(self) -> dict | None:
|
|
440
|
-
"""The context dictionary for the parent `Schema <marshmallow.Schema>`."""
|
|
441
|
-
if self.parent:
|
|
442
|
-
return self.parent.context
|
|
443
|
-
return None
|
|
444
|
-
|
|
445
|
-
# the default and missing properties are provided for compatibility and
|
|
446
|
-
# emit warnings when they are accessed and set
|
|
447
|
-
@property
|
|
448
|
-
def default(self):
|
|
449
|
-
warnings.warn(
|
|
450
|
-
"The 'default' attribute of fields is deprecated. "
|
|
451
|
-
"Use 'dump_default' instead.",
|
|
452
|
-
RemovedInMarshmallow4Warning,
|
|
453
|
-
stacklevel=2,
|
|
454
|
-
)
|
|
455
|
-
return self.dump_default
|
|
456
|
-
|
|
457
|
-
@default.setter
|
|
458
|
-
def default(self, value):
|
|
459
|
-
warnings.warn(
|
|
460
|
-
"The 'default' attribute of fields is deprecated. "
|
|
461
|
-
"Use 'dump_default' instead.",
|
|
462
|
-
RemovedInMarshmallow4Warning,
|
|
463
|
-
stacklevel=2,
|
|
464
|
-
)
|
|
465
|
-
self.dump_default = value
|
|
466
436
|
|
|
467
|
-
|
|
468
|
-
def missing(self):
|
|
469
|
-
warnings.warn(
|
|
470
|
-
"The 'missing' attribute of fields is deprecated. "
|
|
471
|
-
"Use 'load_default' instead.",
|
|
472
|
-
RemovedInMarshmallow4Warning,
|
|
473
|
-
stacklevel=2,
|
|
474
|
-
)
|
|
475
|
-
return self.load_default
|
|
476
|
-
|
|
477
|
-
@missing.setter
|
|
478
|
-
def missing(self, value):
|
|
479
|
-
warnings.warn(
|
|
480
|
-
"The 'missing' attribute of fields is deprecated. "
|
|
481
|
-
"Use 'load_default' instead.",
|
|
482
|
-
RemovedInMarshmallow4Warning,
|
|
483
|
-
stacklevel=2,
|
|
484
|
-
)
|
|
485
|
-
self.load_default = value
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
class Raw(Field):
|
|
437
|
+
class Raw(Field[typing.Any]):
|
|
489
438
|
"""Field that applies no formatting."""
|
|
490
439
|
|
|
491
440
|
|
|
@@ -551,61 +500,45 @@ class Nested(Field):
|
|
|
551
500
|
| typing.Callable[[], Schema | SchemaMeta | dict[str, Field]]
|
|
552
501
|
),
|
|
553
502
|
*,
|
|
554
|
-
dump_default: typing.Any = missing_,
|
|
555
|
-
default: typing.Any = missing_,
|
|
556
503
|
only: types.StrSequenceOrSet | None = None,
|
|
557
504
|
exclude: types.StrSequenceOrSet = (),
|
|
558
505
|
many: bool = False,
|
|
559
|
-
unknown:
|
|
560
|
-
**kwargs,
|
|
506
|
+
unknown: types.UnknownOption | None = None,
|
|
507
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
561
508
|
):
|
|
562
509
|
# Raise error if only or exclude is passed as string, not list of strings
|
|
563
|
-
if only is not None and not
|
|
510
|
+
if only is not None and not utils.is_sequence_but_not_string(only):
|
|
564
511
|
raise StringNotCollectionError('"only" should be a collection of strings.')
|
|
565
|
-
if not
|
|
512
|
+
if not utils.is_sequence_but_not_string(exclude):
|
|
566
513
|
raise StringNotCollectionError(
|
|
567
514
|
'"exclude" should be a collection of strings.'
|
|
568
515
|
)
|
|
569
|
-
if nested == "self":
|
|
570
|
-
warnings.warn(
|
|
571
|
-
"Passing 'self' to `Nested` is deprecated. "
|
|
572
|
-
"Use `Nested(lambda: MySchema(...))` instead.",
|
|
573
|
-
RemovedInMarshmallow4Warning,
|
|
574
|
-
stacklevel=2,
|
|
575
|
-
)
|
|
576
516
|
self.nested = nested
|
|
577
517
|
self.only = only
|
|
578
518
|
self.exclude = exclude
|
|
579
519
|
self.many = many
|
|
580
520
|
self.unknown = unknown
|
|
581
521
|
self._schema: Schema | None = None # Cached Schema instance
|
|
582
|
-
super().__init__(
|
|
522
|
+
super().__init__(**kwargs)
|
|
583
523
|
|
|
584
524
|
@property
|
|
585
525
|
def schema(self) -> Schema:
|
|
586
|
-
"""The nested
|
|
587
|
-
|
|
588
|
-
.. versionchanged:: 1.0.0
|
|
589
|
-
Renamed from ``serializer`` to ``schema``.
|
|
590
|
-
"""
|
|
526
|
+
"""The nested Schema object."""
|
|
591
527
|
if not self._schema:
|
|
592
|
-
# Inherit context from parent.
|
|
593
|
-
context = getattr(self.parent, "context", {})
|
|
594
528
|
if callable(self.nested) and not isinstance(self.nested, type):
|
|
595
529
|
nested = self.nested()
|
|
596
530
|
else:
|
|
597
531
|
nested = typing.cast("Schema", self.nested)
|
|
598
532
|
# defer the import of `marshmallow.schema` to avoid circular imports
|
|
599
|
-
from marshmallow.schema import Schema
|
|
533
|
+
from marshmallow.schema import Schema # noqa: PLC0415
|
|
600
534
|
|
|
601
535
|
if isinstance(nested, dict):
|
|
602
536
|
nested = Schema.from_dict(nested)
|
|
603
537
|
|
|
604
538
|
if isinstance(nested, Schema):
|
|
605
539
|
self._schema = copy.copy(nested)
|
|
606
|
-
self._schema.context.update(context)
|
|
607
540
|
# Respect only and exclude passed from parent and re-initialize fields
|
|
608
|
-
set_class = typing.cast(type[set], self._schema.set_class)
|
|
541
|
+
set_class = typing.cast("type[set]", self._schema.set_class)
|
|
609
542
|
if self.only is not None:
|
|
610
543
|
if self._schema.only is not None:
|
|
611
544
|
original = self._schema.only
|
|
@@ -624,15 +557,12 @@ class Nested(Field):
|
|
|
624
557
|
"`Nested` fields must be passed a "
|
|
625
558
|
f"`Schema`, not {nested.__class__}."
|
|
626
559
|
)
|
|
627
|
-
elif nested == "self":
|
|
628
|
-
schema_class = typing.cast(Schema, self.root).__class__
|
|
629
560
|
else:
|
|
630
|
-
schema_class = class_registry.get_class(nested, all=False)
|
|
561
|
+
schema_class = class_registry.get_class(nested, all=False) # type: ignore[unreachable]
|
|
631
562
|
self._schema = schema_class(
|
|
632
563
|
many=self.many,
|
|
633
564
|
only=self.only,
|
|
634
565
|
exclude=self.exclude,
|
|
635
|
-
context=context,
|
|
636
566
|
load_only=self._nested_normalized_option("load_only"),
|
|
637
567
|
dump_only=self._nested_normalized_option("dump_only"),
|
|
638
568
|
)
|
|
@@ -661,7 +591,9 @@ class Nested(Field):
|
|
|
661
591
|
raise self.make_error("type", input=value, type=value.__class__.__name__)
|
|
662
592
|
|
|
663
593
|
def _load(
|
|
664
|
-
self,
|
|
594
|
+
self,
|
|
595
|
+
value: typing.Any,
|
|
596
|
+
partial: bool | types.StrSequenceOrSet | None = None, # noqa: FBT001
|
|
665
597
|
):
|
|
666
598
|
try:
|
|
667
599
|
valid_data = self.schema.load(value, unknown=self.unknown, partial=partial)
|
|
@@ -676,9 +608,9 @@ class Nested(Field):
|
|
|
676
608
|
value: typing.Any,
|
|
677
609
|
attr: str | None,
|
|
678
610
|
data: typing.Mapping[str, typing.Any] | None,
|
|
679
|
-
partial: bool | types.StrSequenceOrSet | None = None,
|
|
611
|
+
partial: bool | types.StrSequenceOrSet | None = None, # noqa: FBT001
|
|
680
612
|
**kwargs,
|
|
681
|
-
)
|
|
613
|
+
):
|
|
682
614
|
"""Same as :meth:`Field._deserialize` with additional ``partial`` argument.
|
|
683
615
|
|
|
684
616
|
:param partial: For nested schemas, the ``partial``
|
|
@@ -712,9 +644,8 @@ class Pluck(Nested):
|
|
|
712
644
|
loaded = AlbumSchema().load(in_data) # => {'artist': {'id': 42}}
|
|
713
645
|
dumped = AlbumSchema().dump(loaded) # => {'artist': 42}
|
|
714
646
|
|
|
715
|
-
:param nested: The Schema class or class name (string)
|
|
716
|
-
|
|
717
|
-
:param field_name: The key to pluck a value from.
|
|
647
|
+
:param nested: The Schema class or class name (string) to nest
|
|
648
|
+
:param str field_name: The key to pluck a value from.
|
|
718
649
|
:param kwargs: The same keyword arguments that :class:`Nested` receives.
|
|
719
650
|
"""
|
|
720
651
|
|
|
@@ -724,8 +655,8 @@ class Pluck(Nested):
|
|
|
724
655
|
field_name: str,
|
|
725
656
|
*,
|
|
726
657
|
many: bool = False,
|
|
727
|
-
unknown:
|
|
728
|
-
**kwargs,
|
|
658
|
+
unknown: types.UnknownOption | None = None,
|
|
659
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
729
660
|
):
|
|
730
661
|
super().__init__(
|
|
731
662
|
nested, only=(field_name,), many=many, unknown=unknown, **kwargs
|
|
@@ -754,7 +685,7 @@ class Pluck(Nested):
|
|
|
754
685
|
return self._load(value, partial=partial)
|
|
755
686
|
|
|
756
687
|
|
|
757
|
-
class List(Field):
|
|
688
|
+
class List(Field[list[typing.Optional[_InternalT]]]):
|
|
758
689
|
"""A list field, composed with another `Field` class or
|
|
759
690
|
instance.
|
|
760
691
|
|
|
@@ -772,33 +703,37 @@ class List(Field):
|
|
|
772
703
|
#: Default error messages.
|
|
773
704
|
default_error_messages = {"invalid": "Not a valid list."}
|
|
774
705
|
|
|
775
|
-
def __init__(
|
|
706
|
+
def __init__(
|
|
707
|
+
self,
|
|
708
|
+
cls_or_instance: Field[_InternalT] | type[Field[_InternalT]],
|
|
709
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
710
|
+
):
|
|
776
711
|
super().__init__(**kwargs)
|
|
777
712
|
try:
|
|
778
|
-
self.inner =
|
|
779
|
-
except
|
|
713
|
+
self.inner: Field[_InternalT] = _resolve_field_instance(cls_or_instance)
|
|
714
|
+
except _FieldInstanceResolutionError as error:
|
|
780
715
|
raise ValueError(
|
|
781
716
|
"The list elements must be a subclass or instance of "
|
|
782
|
-
"marshmallow.
|
|
717
|
+
"marshmallow.fields.Field."
|
|
783
718
|
) from error
|
|
784
719
|
if isinstance(self.inner, Nested):
|
|
785
720
|
self.only = self.inner.only
|
|
786
721
|
self.exclude = self.inner.exclude
|
|
787
722
|
|
|
788
|
-
def _bind_to_schema(self, field_name: str,
|
|
789
|
-
super()._bind_to_schema(field_name,
|
|
723
|
+
def _bind_to_schema(self, field_name: str, parent: Schema | Field) -> None:
|
|
724
|
+
super()._bind_to_schema(field_name, parent)
|
|
790
725
|
self.inner = copy.deepcopy(self.inner)
|
|
791
726
|
self.inner._bind_to_schema(field_name, self)
|
|
792
727
|
if isinstance(self.inner, Nested):
|
|
793
728
|
self.inner.only = self.only
|
|
794
729
|
self.inner.exclude = self.exclude
|
|
795
730
|
|
|
796
|
-
def _serialize(self, value, attr, obj, **kwargs) -> list[
|
|
731
|
+
def _serialize(self, value, attr, obj, **kwargs) -> list[_InternalT] | None:
|
|
797
732
|
if value is None:
|
|
798
733
|
return None
|
|
799
734
|
return [self.inner._serialize(each, attr, obj, **kwargs) for each in value]
|
|
800
735
|
|
|
801
|
-
def _deserialize(self, value, attr, data, **kwargs) -> list[
|
|
736
|
+
def _deserialize(self, value, attr, data, **kwargs) -> list[_InternalT | None]:
|
|
802
737
|
if not utils.is_collection(value):
|
|
803
738
|
raise self.make_error("invalid")
|
|
804
739
|
|
|
@@ -809,14 +744,14 @@ class List(Field):
|
|
|
809
744
|
result.append(self.inner.deserialize(each, **kwargs))
|
|
810
745
|
except ValidationError as error:
|
|
811
746
|
if error.valid_data is not None:
|
|
812
|
-
result.append(error.valid_data)
|
|
747
|
+
result.append(typing.cast("_InternalT", error.valid_data))
|
|
813
748
|
errors.update({idx: error.messages})
|
|
814
749
|
if errors:
|
|
815
750
|
raise ValidationError(errors, valid_data=result)
|
|
816
751
|
return result
|
|
817
752
|
|
|
818
753
|
|
|
819
|
-
class Tuple(Field):
|
|
754
|
+
class Tuple(Field[tuple]):
|
|
820
755
|
"""A tuple field, composed of a fixed number of other `Field` classes or
|
|
821
756
|
instances
|
|
822
757
|
|
|
@@ -842,7 +777,7 @@ class Tuple(Field):
|
|
|
842
777
|
def __init__(
|
|
843
778
|
self,
|
|
844
779
|
tuple_fields: typing.Iterable[Field] | typing.Iterable[type[Field]],
|
|
845
|
-
**kwargs,
|
|
780
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
846
781
|
):
|
|
847
782
|
super().__init__(**kwargs)
|
|
848
783
|
if not utils.is_collection(tuple_fields):
|
|
@@ -852,19 +787,19 @@ class Tuple(Field):
|
|
|
852
787
|
|
|
853
788
|
try:
|
|
854
789
|
self.tuple_fields = [
|
|
855
|
-
|
|
790
|
+
_resolve_field_instance(cls_or_instance)
|
|
856
791
|
for cls_or_instance in tuple_fields
|
|
857
792
|
]
|
|
858
|
-
except
|
|
793
|
+
except _FieldInstanceResolutionError as error:
|
|
859
794
|
raise ValueError(
|
|
860
795
|
'Elements of "tuple_fields" must be subclasses or '
|
|
861
|
-
"instances of marshmallow.
|
|
796
|
+
"instances of marshmallow.fields.Field."
|
|
862
797
|
) from error
|
|
863
798
|
|
|
864
799
|
self.validate_length = Length(equal=len(self.tuple_fields))
|
|
865
800
|
|
|
866
|
-
def _bind_to_schema(self, field_name: str,
|
|
867
|
-
super()._bind_to_schema(field_name,
|
|
801
|
+
def _bind_to_schema(self, field_name: str, parent: Schema | Field) -> None:
|
|
802
|
+
super()._bind_to_schema(field_name, parent)
|
|
868
803
|
new_tuple_fields = []
|
|
869
804
|
for field in self.tuple_fields:
|
|
870
805
|
new_field = copy.deepcopy(field)
|
|
@@ -873,7 +808,9 @@ class Tuple(Field):
|
|
|
873
808
|
|
|
874
809
|
self.tuple_fields = new_tuple_fields
|
|
875
810
|
|
|
876
|
-
def _serialize(
|
|
811
|
+
def _serialize(
|
|
812
|
+
self, value: tuple | None, attr: str | None, obj: typing.Any, **kwargs
|
|
813
|
+
) -> tuple | None:
|
|
877
814
|
if value is None:
|
|
878
815
|
return None
|
|
879
816
|
|
|
@@ -882,8 +819,14 @@ class Tuple(Field):
|
|
|
882
819
|
for field, each in zip(self.tuple_fields, value)
|
|
883
820
|
)
|
|
884
821
|
|
|
885
|
-
def _deserialize(
|
|
886
|
-
|
|
822
|
+
def _deserialize(
|
|
823
|
+
self,
|
|
824
|
+
value: typing.Any,
|
|
825
|
+
attr: str | None,
|
|
826
|
+
data: typing.Mapping[str, typing.Any] | None,
|
|
827
|
+
**kwargs,
|
|
828
|
+
) -> tuple:
|
|
829
|
+
if not utils.is_sequence_but_not_string(value):
|
|
887
830
|
raise self.make_error("invalid")
|
|
888
831
|
|
|
889
832
|
self.validate_length(value)
|
|
@@ -904,7 +847,7 @@ class Tuple(Field):
|
|
|
904
847
|
return tuple(result)
|
|
905
848
|
|
|
906
849
|
|
|
907
|
-
class String(Field):
|
|
850
|
+
class String(Field[str]):
|
|
908
851
|
"""A string field.
|
|
909
852
|
|
|
910
853
|
:param kwargs: The same keyword arguments that :class:`Field` receives.
|
|
@@ -921,7 +864,7 @@ class String(Field):
|
|
|
921
864
|
return None
|
|
922
865
|
return utils.ensure_text_type(value)
|
|
923
866
|
|
|
924
|
-
def _deserialize(self, value, attr, data, **kwargs) ->
|
|
867
|
+
def _deserialize(self, value, attr, data, **kwargs) -> str:
|
|
925
868
|
if not isinstance(value, (str, bytes)):
|
|
926
869
|
raise self.make_error("invalid")
|
|
927
870
|
try:
|
|
@@ -930,16 +873,14 @@ class String(Field):
|
|
|
930
873
|
raise self.make_error("invalid_utf8") from error
|
|
931
874
|
|
|
932
875
|
|
|
933
|
-
class UUID(
|
|
876
|
+
class UUID(Field[uuid.UUID]):
|
|
934
877
|
"""A UUID field."""
|
|
935
878
|
|
|
936
879
|
#: Default error messages.
|
|
937
880
|
default_error_messages = {"invalid_uuid": "Not a valid UUID."}
|
|
938
881
|
|
|
939
|
-
def _validated(self, value) -> uuid.UUID
|
|
882
|
+
def _validated(self, value) -> uuid.UUID:
|
|
940
883
|
"""Format the value or raise a :exc:`ValidationError` if an error occurs."""
|
|
941
|
-
if value is None:
|
|
942
|
-
return None
|
|
943
884
|
if isinstance(value, uuid.UUID):
|
|
944
885
|
return value
|
|
945
886
|
try:
|
|
@@ -949,15 +890,20 @@ class UUID(String):
|
|
|
949
890
|
except (ValueError, AttributeError, TypeError) as error:
|
|
950
891
|
raise self.make_error("invalid_uuid") from error
|
|
951
892
|
|
|
952
|
-
def
|
|
893
|
+
def _serialize(self, value, attr, obj, **kwargs) -> str | None:
|
|
894
|
+
if value is None:
|
|
895
|
+
return None
|
|
896
|
+
return str(value)
|
|
897
|
+
|
|
898
|
+
def _deserialize(self, value, attr, data, **kwargs) -> uuid.UUID:
|
|
953
899
|
return self._validated(value)
|
|
954
900
|
|
|
955
901
|
|
|
956
|
-
|
|
902
|
+
_NumT = typing.TypeVar("_NumT")
|
|
957
903
|
|
|
958
904
|
|
|
959
|
-
class Number(Field
|
|
960
|
-
"""Base class for number fields.
|
|
905
|
+
class Number(Field[_NumT]):
|
|
906
|
+
"""Base class for number fields. This class should not be used within schemas.
|
|
961
907
|
|
|
962
908
|
:param as_string: If `True`, format the serialized value as a string.
|
|
963
909
|
:param kwargs: The same keyword arguments that :class:`Field` receives.
|
|
@@ -967,7 +913,7 @@ class Number(Field, typing.Generic[_NumType]):
|
|
|
967
913
|
Use `Integer <marshmallow.fields.Integer>`, `Float <marshmallow.fields.Float>`, or `Decimal <marshmallow.fields.Decimal>` instead.
|
|
968
914
|
"""
|
|
969
915
|
|
|
970
|
-
num_type: type
|
|
916
|
+
num_type: type[_NumT]
|
|
971
917
|
|
|
972
918
|
#: Default error messages.
|
|
973
919
|
default_error_messages = {
|
|
@@ -975,21 +921,15 @@ class Number(Field, typing.Generic[_NumType]):
|
|
|
975
921
|
"too_large": "Number too large.",
|
|
976
922
|
}
|
|
977
923
|
|
|
978
|
-
def __init__(self, *, as_string: bool = False, **kwargs):
|
|
979
|
-
if self.__class__ is Number:
|
|
980
|
-
warnings.warn(
|
|
981
|
-
"`Number` field should not be instantiated. Use `Integer`, `Float`, or `Decimal` instead.",
|
|
982
|
-
ChangedInMarshmallow4Warning,
|
|
983
|
-
stacklevel=2,
|
|
984
|
-
)
|
|
924
|
+
def __init__(self, *, as_string: bool = False, **kwargs: Unpack[_BaseFieldKwargs]):
|
|
985
925
|
self.as_string = as_string
|
|
986
926
|
super().__init__(**kwargs)
|
|
987
927
|
|
|
988
|
-
def _format_num(self, value) ->
|
|
928
|
+
def _format_num(self, value) -> _NumT:
|
|
989
929
|
"""Return the number value for value, given this field's `num_type`."""
|
|
990
|
-
return self.num_type(value)
|
|
930
|
+
return self.num_type(value) # type: ignore[call-arg]
|
|
991
931
|
|
|
992
|
-
def _validated(self, value: typing.Any) ->
|
|
932
|
+
def _validated(self, value: typing.Any) -> _NumT:
|
|
993
933
|
"""Format the value or raise a :exc:`ValidationError` if an error occurs."""
|
|
994
934
|
# (value is True or value is False) is ~5x faster than isinstance(value, bool)
|
|
995
935
|
if value is True or value is False:
|
|
@@ -1001,17 +941,17 @@ class Number(Field, typing.Generic[_NumType]):
|
|
|
1001
941
|
except OverflowError as error:
|
|
1002
942
|
raise self.make_error("too_large", input=value) from error
|
|
1003
943
|
|
|
1004
|
-
def _to_string(self, value:
|
|
944
|
+
def _to_string(self, value: _NumT) -> str:
|
|
1005
945
|
return str(value)
|
|
1006
946
|
|
|
1007
|
-
def _serialize(self, value, attr, obj, **kwargs) -> str |
|
|
947
|
+
def _serialize(self, value, attr, obj, **kwargs) -> str | _NumT | None:
|
|
1008
948
|
"""Return a string if `self.as_string=True`, otherwise return this field's `num_type`."""
|
|
1009
949
|
if value is None:
|
|
1010
950
|
return None
|
|
1011
|
-
ret:
|
|
951
|
+
ret: _NumT = self._format_num(value)
|
|
1012
952
|
return self._to_string(ret) if self.as_string else ret
|
|
1013
953
|
|
|
1014
|
-
def _deserialize(self, value, attr, data, **kwargs) ->
|
|
954
|
+
def _deserialize(self, value, attr, data, **kwargs) -> _NumT:
|
|
1015
955
|
return self._validated(value)
|
|
1016
956
|
|
|
1017
957
|
|
|
@@ -1028,9 +968,15 @@ class Integer(Number[int]):
|
|
|
1028
968
|
#: Default error messages.
|
|
1029
969
|
default_error_messages = {"invalid": "Not a valid integer."}
|
|
1030
970
|
|
|
1031
|
-
def __init__(
|
|
971
|
+
def __init__(
|
|
972
|
+
self,
|
|
973
|
+
*,
|
|
974
|
+
strict: bool = False,
|
|
975
|
+
as_string: bool = False,
|
|
976
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
977
|
+
):
|
|
1032
978
|
self.strict = strict
|
|
1033
|
-
super().__init__(**kwargs)
|
|
979
|
+
super().__init__(as_string=as_string, **kwargs)
|
|
1034
980
|
|
|
1035
981
|
# override Number
|
|
1036
982
|
def _validated(self, value: typing.Any) -> int:
|
|
@@ -1055,7 +1001,13 @@ class Float(Number[float]):
|
|
|
1055
1001
|
"special": "Special numeric values (nan or infinity) are not permitted."
|
|
1056
1002
|
}
|
|
1057
1003
|
|
|
1058
|
-
def __init__(
|
|
1004
|
+
def __init__(
|
|
1005
|
+
self,
|
|
1006
|
+
*,
|
|
1007
|
+
allow_nan: bool = False,
|
|
1008
|
+
as_string: bool = False,
|
|
1009
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
1010
|
+
):
|
|
1059
1011
|
self.allow_nan = allow_nan
|
|
1060
1012
|
super().__init__(as_string=as_string, **kwargs)
|
|
1061
1013
|
|
|
@@ -1100,8 +1052,6 @@ class Decimal(Number[decimal.Decimal]):
|
|
|
1100
1052
|
:param as_string: If `True`, serialize to a string instead of a Python
|
|
1101
1053
|
`decimal.Decimal` type.
|
|
1102
1054
|
:param kwargs: The same keyword arguments that :class:`Number` receives.
|
|
1103
|
-
|
|
1104
|
-
.. versionadded:: 1.2.0
|
|
1105
1055
|
"""
|
|
1106
1056
|
|
|
1107
1057
|
num_type = decimal.Decimal
|
|
@@ -1118,7 +1068,7 @@ class Decimal(Number[decimal.Decimal]):
|
|
|
1118
1068
|
*,
|
|
1119
1069
|
allow_nan: bool = False,
|
|
1120
1070
|
as_string: bool = False,
|
|
1121
|
-
**kwargs,
|
|
1071
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
1122
1072
|
):
|
|
1123
1073
|
self.places = (
|
|
1124
1074
|
decimal.Decimal((0, (1,), -places)) if places is not None else None
|
|
@@ -1152,7 +1102,7 @@ class Decimal(Number[decimal.Decimal]):
|
|
|
1152
1102
|
return format(value, "f")
|
|
1153
1103
|
|
|
1154
1104
|
|
|
1155
|
-
class Boolean(Field):
|
|
1105
|
+
class Boolean(Field[bool]):
|
|
1156
1106
|
"""A boolean field.
|
|
1157
1107
|
|
|
1158
1108
|
:param truthy: Values that will (de)serialize to `True`. If an empty
|
|
@@ -1213,7 +1163,7 @@ class Boolean(Field):
|
|
|
1213
1163
|
*,
|
|
1214
1164
|
truthy: typing.Iterable | None = None,
|
|
1215
1165
|
falsy: typing.Iterable | None = None,
|
|
1216
|
-
**kwargs,
|
|
1166
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
1217
1167
|
):
|
|
1218
1168
|
super().__init__(**kwargs)
|
|
1219
1169
|
|
|
@@ -1222,23 +1172,13 @@ class Boolean(Field):
|
|
|
1222
1172
|
if falsy is not None:
|
|
1223
1173
|
self.falsy = set(falsy)
|
|
1224
1174
|
|
|
1225
|
-
def
|
|
1226
|
-
self,
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
if value in self.truthy:
|
|
1233
|
-
return True
|
|
1234
|
-
if value in self.falsy:
|
|
1235
|
-
return False
|
|
1236
|
-
except TypeError:
|
|
1237
|
-
pass
|
|
1238
|
-
|
|
1239
|
-
return bool(value)
|
|
1240
|
-
|
|
1241
|
-
def _deserialize(self, value, attr, data, **kwargs):
|
|
1175
|
+
def _deserialize(
|
|
1176
|
+
self,
|
|
1177
|
+
value: typing.Any,
|
|
1178
|
+
attr: str | None,
|
|
1179
|
+
data: typing.Mapping[str, typing.Any] | None,
|
|
1180
|
+
**kwargs,
|
|
1181
|
+
) -> bool:
|
|
1242
1182
|
if not self.truthy:
|
|
1243
1183
|
return bool(value)
|
|
1244
1184
|
try:
|
|
@@ -1251,69 +1191,45 @@ class Boolean(Field):
|
|
|
1251
1191
|
raise self.make_error("invalid", input=value)
|
|
1252
1192
|
|
|
1253
1193
|
|
|
1254
|
-
|
|
1255
|
-
"""A formatted datetime string.
|
|
1194
|
+
_D = typing.TypeVar("_D", dt.datetime, dt.date, dt.time)
|
|
1256
1195
|
|
|
1257
|
-
Example: ``'2014-12-22T03:12:58.019077+00:00'``
|
|
1258
1196
|
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
If `None`, defaults to "iso".
|
|
1262
|
-
:param kwargs: The same keyword arguments that :class:`Field` receives.
|
|
1197
|
+
class _TemporalField(Field[_D], metaclass=abc.ABCMeta):
|
|
1198
|
+
"""Base field for date and time related fields including common (de)serialization logic."""
|
|
1263
1199
|
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
SERIALIZATION_FUNCS: dict[str, typing.Callable[[typing.Any], str | float]] = {
|
|
1271
|
-
"iso": utils.isoformat,
|
|
1272
|
-
"iso8601": utils.isoformat,
|
|
1273
|
-
"rfc": utils.rfcformat,
|
|
1274
|
-
"rfc822": utils.rfcformat,
|
|
1275
|
-
"timestamp": utils.timestamp,
|
|
1276
|
-
"timestamp_ms": utils.timestamp_ms,
|
|
1277
|
-
}
|
|
1278
|
-
|
|
1279
|
-
DESERIALIZATION_FUNCS: dict[str, typing.Callable[[str], typing.Any]] = {
|
|
1280
|
-
"iso": utils.from_iso_datetime,
|
|
1281
|
-
"iso8601": utils.from_iso_datetime,
|
|
1282
|
-
"rfc": utils.from_rfc,
|
|
1283
|
-
"rfc822": utils.from_rfc,
|
|
1284
|
-
"timestamp": utils.from_timestamp,
|
|
1285
|
-
"timestamp_ms": utils.from_timestamp_ms,
|
|
1286
|
-
}
|
|
1287
|
-
|
|
1288
|
-
DEFAULT_FORMAT = "iso"
|
|
1289
|
-
|
|
1290
|
-
OBJ_TYPE = "datetime"
|
|
1291
|
-
|
|
1292
|
-
SCHEMA_OPTS_VAR_NAME = "datetimeformat"
|
|
1200
|
+
# Subclasses should define each of these class constants
|
|
1201
|
+
SERIALIZATION_FUNCS: dict[str, typing.Callable[[_D], str | float]]
|
|
1202
|
+
DESERIALIZATION_FUNCS: dict[str, typing.Callable[[str], _D]]
|
|
1203
|
+
DEFAULT_FORMAT: str
|
|
1204
|
+
OBJ_TYPE: str
|
|
1205
|
+
SCHEMA_OPTS_VAR_NAME: str
|
|
1293
1206
|
|
|
1294
|
-
#: Default error messages.
|
|
1295
1207
|
default_error_messages = {
|
|
1296
1208
|
"invalid": "Not a valid {obj_type}.",
|
|
1297
1209
|
"invalid_awareness": "Not a valid {awareness} {obj_type}.",
|
|
1298
1210
|
"format": '"{input}" cannot be formatted as a {obj_type}.',
|
|
1299
1211
|
}
|
|
1300
1212
|
|
|
1301
|
-
def __init__(
|
|
1213
|
+
def __init__(
|
|
1214
|
+
self,
|
|
1215
|
+
format: str | None = None, # noqa: A002
|
|
1216
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
1217
|
+
) -> None:
|
|
1302
1218
|
super().__init__(**kwargs)
|
|
1303
1219
|
# Allow this to be None. It may be set later in the ``_serialize``
|
|
1304
1220
|
# or ``_deserialize`` methods. This allows a Schema to dynamically set the
|
|
1305
1221
|
# format, e.g. from a Meta option
|
|
1306
1222
|
self.format = format
|
|
1307
1223
|
|
|
1308
|
-
def _bind_to_schema(self, field_name,
|
|
1309
|
-
super()._bind_to_schema(field_name,
|
|
1224
|
+
def _bind_to_schema(self, field_name, parent):
|
|
1225
|
+
super()._bind_to_schema(field_name, parent)
|
|
1310
1226
|
self.format = (
|
|
1311
1227
|
self.format
|
|
1312
1228
|
or getattr(self.root.opts, self.SCHEMA_OPTS_VAR_NAME)
|
|
1313
1229
|
or self.DEFAULT_FORMAT
|
|
1314
1230
|
)
|
|
1315
1231
|
|
|
1316
|
-
def _serialize(self, value, attr, obj, **kwargs) -> str | float | None:
|
|
1232
|
+
def _serialize(self, value: _D | None, attr, obj, **kwargs) -> str | float | None:
|
|
1317
1233
|
if value is None:
|
|
1318
1234
|
return None
|
|
1319
1235
|
data_format = self.format or self.DEFAULT_FORMAT
|
|
@@ -1322,7 +1238,10 @@ class DateTime(Field):
|
|
|
1322
1238
|
return format_func(value)
|
|
1323
1239
|
return value.strftime(data_format)
|
|
1324
1240
|
|
|
1325
|
-
def _deserialize(self, value, attr, data, **kwargs) ->
|
|
1241
|
+
def _deserialize(self, value, attr, data, **kwargs) -> _D:
|
|
1242
|
+
internal_type: type[_D] = getattr(dt, self.OBJ_TYPE)
|
|
1243
|
+
if isinstance(value, internal_type):
|
|
1244
|
+
return value
|
|
1326
1245
|
data_format = self.format or self.DEFAULT_FORMAT
|
|
1327
1246
|
func = self.DESERIALIZATION_FUNCS.get(data_format)
|
|
1328
1247
|
try:
|
|
@@ -1334,6 +1253,51 @@ class DateTime(Field):
|
|
|
1334
1253
|
"invalid", input=value, obj_type=self.OBJ_TYPE
|
|
1335
1254
|
) from error
|
|
1336
1255
|
|
|
1256
|
+
@staticmethod
|
|
1257
|
+
@abc.abstractmethod
|
|
1258
|
+
def _make_object_from_format(value: typing.Any, data_format: str) -> _D: ...
|
|
1259
|
+
|
|
1260
|
+
|
|
1261
|
+
class DateTime(_TemporalField[dt.datetime]):
|
|
1262
|
+
"""A formatted datetime string.
|
|
1263
|
+
|
|
1264
|
+
Example: ``'2014-12-22T03:12:58.019077+00:00'``
|
|
1265
|
+
|
|
1266
|
+
:param format: Either ``"rfc"`` (for RFC822), ``"iso"`` (for ISO8601),
|
|
1267
|
+
``"timestamp"``, ``"timestamp_ms"`` (for a POSIX timestamp) or a date format string.
|
|
1268
|
+
If `None`, defaults to "iso".
|
|
1269
|
+
:param kwargs: The same keyword arguments that :class:`Field` receives.
|
|
1270
|
+
|
|
1271
|
+
.. versionchanged:: 3.0.0rc9
|
|
1272
|
+
Does not modify timezone information on (de)serialization.
|
|
1273
|
+
.. versionchanged:: 3.19
|
|
1274
|
+
Add timestamp as a format.
|
|
1275
|
+
"""
|
|
1276
|
+
|
|
1277
|
+
SERIALIZATION_FUNCS: dict[str, typing.Callable[[dt.datetime], str | float]] = {
|
|
1278
|
+
"iso": dt.datetime.isoformat,
|
|
1279
|
+
"iso8601": dt.datetime.isoformat,
|
|
1280
|
+
"rfc": email.utils.format_datetime,
|
|
1281
|
+
"rfc822": email.utils.format_datetime,
|
|
1282
|
+
"timestamp": utils.timestamp,
|
|
1283
|
+
"timestamp_ms": utils.timestamp_ms,
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
DESERIALIZATION_FUNCS: dict[str, typing.Callable[[str], dt.datetime]] = {
|
|
1287
|
+
"iso": dt.datetime.fromisoformat,
|
|
1288
|
+
"iso8601": dt.datetime.fromisoformat,
|
|
1289
|
+
"rfc": email.utils.parsedate_to_datetime,
|
|
1290
|
+
"rfc822": email.utils.parsedate_to_datetime,
|
|
1291
|
+
"timestamp": utils.from_timestamp,
|
|
1292
|
+
"timestamp_ms": utils.from_timestamp_ms,
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
DEFAULT_FORMAT = "iso"
|
|
1296
|
+
|
|
1297
|
+
OBJ_TYPE = "datetime"
|
|
1298
|
+
|
|
1299
|
+
SCHEMA_OPTS_VAR_NAME = "datetimeformat"
|
|
1300
|
+
|
|
1337
1301
|
@staticmethod
|
|
1338
1302
|
def _make_object_from_format(value, data_format) -> dt.datetime:
|
|
1339
1303
|
return dt.datetime.strptime(value, data_format)
|
|
@@ -1359,14 +1323,14 @@ class NaiveDateTime(DateTime):
|
|
|
1359
1323
|
format: str | None = None, # noqa: A002
|
|
1360
1324
|
*,
|
|
1361
1325
|
timezone: dt.timezone | None = None,
|
|
1362
|
-
**kwargs,
|
|
1326
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
1363
1327
|
) -> None:
|
|
1364
1328
|
super().__init__(format=format, **kwargs)
|
|
1365
1329
|
self.timezone = timezone
|
|
1366
1330
|
|
|
1367
1331
|
def _deserialize(self, value, attr, data, **kwargs) -> dt.datetime:
|
|
1368
1332
|
ret = super()._deserialize(value, attr, data, **kwargs)
|
|
1369
|
-
if is_aware(ret):
|
|
1333
|
+
if utils.is_aware(ret):
|
|
1370
1334
|
if self.timezone is None:
|
|
1371
1335
|
raise self.make_error(
|
|
1372
1336
|
"invalid_awareness",
|
|
@@ -1396,14 +1360,14 @@ class AwareDateTime(DateTime):
|
|
|
1396
1360
|
format: str | None = None, # noqa: A002
|
|
1397
1361
|
*,
|
|
1398
1362
|
default_timezone: dt.tzinfo | None = None,
|
|
1399
|
-
**kwargs,
|
|
1363
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
1400
1364
|
) -> None:
|
|
1401
1365
|
super().__init__(format=format, **kwargs)
|
|
1402
1366
|
self.default_timezone = default_timezone
|
|
1403
1367
|
|
|
1404
1368
|
def _deserialize(self, value, attr, data, **kwargs) -> dt.datetime:
|
|
1405
1369
|
ret = super()._deserialize(value, attr, data, **kwargs)
|
|
1406
|
-
if not is_aware(ret):
|
|
1370
|
+
if not utils.is_aware(ret):
|
|
1407
1371
|
if self.default_timezone is None:
|
|
1408
1372
|
raise self.make_error(
|
|
1409
1373
|
"invalid_awareness",
|
|
@@ -1414,7 +1378,7 @@ class AwareDateTime(DateTime):
|
|
|
1414
1378
|
return ret
|
|
1415
1379
|
|
|
1416
1380
|
|
|
1417
|
-
class Time(
|
|
1381
|
+
class Time(_TemporalField[dt.time]):
|
|
1418
1382
|
"""A formatted time string.
|
|
1419
1383
|
|
|
1420
1384
|
Example: ``'03:12:58.019077'``
|
|
@@ -1424,9 +1388,15 @@ class Time(DateTime):
|
|
|
1424
1388
|
:param kwargs: The same keyword arguments that :class:`Field` receives.
|
|
1425
1389
|
"""
|
|
1426
1390
|
|
|
1427
|
-
SERIALIZATION_FUNCS = {
|
|
1391
|
+
SERIALIZATION_FUNCS = {
|
|
1392
|
+
"iso": dt.time.isoformat,
|
|
1393
|
+
"iso8601": dt.time.isoformat,
|
|
1394
|
+
}
|
|
1428
1395
|
|
|
1429
|
-
DESERIALIZATION_FUNCS = {
|
|
1396
|
+
DESERIALIZATION_FUNCS = {
|
|
1397
|
+
"iso": dt.time.fromisoformat,
|
|
1398
|
+
"iso8601": dt.time.fromisoformat,
|
|
1399
|
+
}
|
|
1430
1400
|
|
|
1431
1401
|
DEFAULT_FORMAT = "iso"
|
|
1432
1402
|
|
|
@@ -1439,7 +1409,7 @@ class Time(DateTime):
|
|
|
1439
1409
|
return dt.datetime.strptime(value, data_format).time()
|
|
1440
1410
|
|
|
1441
1411
|
|
|
1442
|
-
class Date(
|
|
1412
|
+
class Date(_TemporalField[dt.date]):
|
|
1443
1413
|
"""ISO8601-formatted date string.
|
|
1444
1414
|
|
|
1445
1415
|
:param format: Either ``"iso"`` (for ISO8601) or a date format string.
|
|
@@ -1453,9 +1423,15 @@ class Date(DateTime):
|
|
|
1453
1423
|
"format": '"{input}" cannot be formatted as a date.',
|
|
1454
1424
|
}
|
|
1455
1425
|
|
|
1456
|
-
SERIALIZATION_FUNCS = {
|
|
1426
|
+
SERIALIZATION_FUNCS = {
|
|
1427
|
+
"iso": dt.date.isoformat,
|
|
1428
|
+
"iso8601": dt.date.isoformat,
|
|
1429
|
+
}
|
|
1457
1430
|
|
|
1458
|
-
DESERIALIZATION_FUNCS = {
|
|
1431
|
+
DESERIALIZATION_FUNCS = {
|
|
1432
|
+
"iso": dt.date.fromisoformat,
|
|
1433
|
+
"iso8601": dt.date.fromisoformat,
|
|
1434
|
+
}
|
|
1459
1435
|
|
|
1460
1436
|
DEFAULT_FORMAT = "iso"
|
|
1461
1437
|
|
|
@@ -1468,43 +1444,51 @@ class Date(DateTime):
|
|
|
1468
1444
|
return dt.datetime.strptime(value, data_format).date()
|
|
1469
1445
|
|
|
1470
1446
|
|
|
1471
|
-
class TimeDelta(Field):
|
|
1472
|
-
"""A field that (de)serializes a :class:`datetime.timedelta` object to
|
|
1473
|
-
|
|
1474
|
-
|
|
1447
|
+
class TimeDelta(Field[dt.timedelta]):
|
|
1448
|
+
"""A field that (de)serializes a :class:`datetime.timedelta` object to a `float`.
|
|
1449
|
+
The `float` can represent any time unit that the :class:`datetime.timedelta` constructor
|
|
1450
|
+
supports.
|
|
1475
1451
|
|
|
1476
|
-
:param precision:
|
|
1477
|
-
|
|
1478
|
-
'milliseconds', 'minutes', 'hours' or 'weeks'.
|
|
1479
|
-
:param serialization_type: Whether to (de)serialize to a `int` or `float`.
|
|
1452
|
+
:param precision: The time unit used for (de)serialization. Must be one of 'weeks',
|
|
1453
|
+
'days', 'hours', 'minutes', 'seconds', 'milliseconds' or 'microseconds'.
|
|
1480
1454
|
:param kwargs: The same keyword arguments that :class:`Field` receives.
|
|
1481
1455
|
|
|
1482
|
-
Integer Caveats
|
|
1483
|
-
---------------
|
|
1484
|
-
Any fractional parts (which depends on the precision used) will be truncated
|
|
1485
|
-
when serializing using `int`.
|
|
1486
|
-
|
|
1487
1456
|
Float Caveats
|
|
1488
1457
|
-------------
|
|
1489
|
-
|
|
1490
|
-
|
|
1458
|
+
Precision loss may occur when serializing a highly precise :class:`datetime.timedelta`
|
|
1459
|
+
object using a big ``precision`` unit due to floating point arithmetics.
|
|
1491
1460
|
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1461
|
+
When necessary, the :class:`datetime.timedelta` constructor rounds `float` inputs
|
|
1462
|
+
to whole microseconds during initialization of the object. As a result, deserializing
|
|
1463
|
+
a `float` might be subject to rounding, regardless of `precision`. For example,
|
|
1464
|
+
``TimeDelta().deserialize("1.1234567") == timedelta(seconds=1, microseconds=123457)``.
|
|
1495
1465
|
|
|
1496
1466
|
.. versionchanged:: 3.17.0
|
|
1497
|
-
Allow
|
|
1498
|
-
`int`
|
|
1467
|
+
Allow serialization to `float` through use of a new `serialization_type` parameter.
|
|
1468
|
+
Defaults to `int` for backwards compatibility. Also affects deserialization.
|
|
1469
|
+
.. versionchanged:: 4.0.0
|
|
1470
|
+
Remove `serialization_type` parameter and always serialize to float.
|
|
1471
|
+
Value is cast to a `float` upon deserialization.
|
|
1499
1472
|
"""
|
|
1500
1473
|
|
|
1474
|
+
WEEKS = "weeks"
|
|
1501
1475
|
DAYS = "days"
|
|
1476
|
+
HOURS = "hours"
|
|
1477
|
+
MINUTES = "minutes"
|
|
1502
1478
|
SECONDS = "seconds"
|
|
1503
|
-
MICROSECONDS = "microseconds"
|
|
1504
1479
|
MILLISECONDS = "milliseconds"
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1480
|
+
MICROSECONDS = "microseconds"
|
|
1481
|
+
|
|
1482
|
+
# cache this mapping on class level for performance
|
|
1483
|
+
_unit_to_microseconds_mapping = {
|
|
1484
|
+
WEEKS: 1000000 * 60 * 60 * 24 * 7,
|
|
1485
|
+
DAYS: 1000000 * 60 * 60 * 24,
|
|
1486
|
+
HOURS: 1000000 * 60 * 60,
|
|
1487
|
+
MINUTES: 1000000 * 60,
|
|
1488
|
+
SECONDS: 1000000,
|
|
1489
|
+
MILLISECONDS: 1000,
|
|
1490
|
+
MICROSECONDS: 1,
|
|
1491
|
+
}
|
|
1508
1492
|
|
|
1509
1493
|
#: Default error messages.
|
|
1510
1494
|
default_error_messages = {
|
|
@@ -1515,49 +1499,32 @@ class TimeDelta(Field):
|
|
|
1515
1499
|
def __init__(
|
|
1516
1500
|
self,
|
|
1517
1501
|
precision: str = SECONDS,
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
):
|
|
1502
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
1503
|
+
) -> None:
|
|
1521
1504
|
precision = precision.lower()
|
|
1522
|
-
units = (
|
|
1523
|
-
self.DAYS,
|
|
1524
|
-
self.SECONDS,
|
|
1525
|
-
self.MICROSECONDS,
|
|
1526
|
-
self.MILLISECONDS,
|
|
1527
|
-
self.MINUTES,
|
|
1528
|
-
self.HOURS,
|
|
1529
|
-
self.WEEKS,
|
|
1530
|
-
)
|
|
1531
1505
|
|
|
1532
|
-
if precision not in
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
)
|
|
1506
|
+
if precision not in self._unit_to_microseconds_mapping:
|
|
1507
|
+
units = ", ".join(self._unit_to_microseconds_mapping)
|
|
1508
|
+
msg = f"The precision must be one of: {units}."
|
|
1536
1509
|
raise ValueError(msg)
|
|
1537
1510
|
|
|
1538
|
-
if serialization_type not in (int, float):
|
|
1539
|
-
raise ValueError("The serialization type must be one of int or float")
|
|
1540
|
-
|
|
1541
1511
|
self.precision = precision
|
|
1542
|
-
self.serialization_type = serialization_type
|
|
1543
1512
|
super().__init__(**kwargs)
|
|
1544
1513
|
|
|
1545
|
-
def _serialize(self, value, attr, obj, **kwargs):
|
|
1514
|
+
def _serialize(self, value, attr, obj, **kwargs) -> float | None:
|
|
1546
1515
|
if value is None:
|
|
1547
1516
|
return None
|
|
1548
1517
|
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
unit = utils.timedelta_to_microseconds(base_unit)
|
|
1554
|
-
return delta // unit
|
|
1555
|
-
assert self.serialization_type is float # noqa: S101
|
|
1556
|
-
return value.total_seconds() / base_unit.total_seconds()
|
|
1518
|
+
# limit float arithmetics to a single division to minimize precision loss
|
|
1519
|
+
microseconds: int = utils.timedelta_to_microseconds(value)
|
|
1520
|
+
microseconds_per_unit: int = self._unit_to_microseconds_mapping[self.precision]
|
|
1521
|
+
return microseconds / microseconds_per_unit
|
|
1557
1522
|
|
|
1558
|
-
def _deserialize(self, value, attr, data, **kwargs):
|
|
1523
|
+
def _deserialize(self, value, attr, data, **kwargs) -> dt.timedelta:
|
|
1524
|
+
if isinstance(value, dt.timedelta):
|
|
1525
|
+
return value
|
|
1559
1526
|
try:
|
|
1560
|
-
value =
|
|
1527
|
+
value = float(value)
|
|
1561
1528
|
except (TypeError, ValueError) as error:
|
|
1562
1529
|
raise self.make_error("invalid") from error
|
|
1563
1530
|
|
|
@@ -1569,7 +1536,10 @@ class TimeDelta(Field):
|
|
|
1569
1536
|
raise self.make_error("invalid") from error
|
|
1570
1537
|
|
|
1571
1538
|
|
|
1572
|
-
|
|
1539
|
+
_MappingT = typing.TypeVar("_MappingT", bound=_Mapping)
|
|
1540
|
+
|
|
1541
|
+
|
|
1542
|
+
class Mapping(Field[_MappingT]):
|
|
1573
1543
|
"""An abstract class for objects with key-value pairs. This class should not be used within schemas.
|
|
1574
1544
|
|
|
1575
1545
|
:param keys: A field class or instance for dict keys.
|
|
@@ -1586,7 +1556,7 @@ class Mapping(Field):
|
|
|
1586
1556
|
Use `Dict <marshmallow.fields.Dict>` instead.
|
|
1587
1557
|
"""
|
|
1588
1558
|
|
|
1589
|
-
mapping_type
|
|
1559
|
+
mapping_type: type[_MappingT]
|
|
1590
1560
|
|
|
1591
1561
|
#: Default error messages.
|
|
1592
1562
|
default_error_messages = {"invalid": "Not a valid mapping type."}
|
|
@@ -1595,42 +1565,35 @@ class Mapping(Field):
|
|
|
1595
1565
|
self,
|
|
1596
1566
|
keys: Field | type[Field] | None = None,
|
|
1597
1567
|
values: Field | type[Field] | None = None,
|
|
1598
|
-
**kwargs,
|
|
1568
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
1599
1569
|
):
|
|
1600
|
-
if self.__class__ is Mapping:
|
|
1601
|
-
warnings.warn(
|
|
1602
|
-
"`Mapping` field should not be instantiated. Use `Dict` instead.",
|
|
1603
|
-
ChangedInMarshmallow4Warning,
|
|
1604
|
-
stacklevel=2,
|
|
1605
|
-
)
|
|
1606
1570
|
super().__init__(**kwargs)
|
|
1607
1571
|
if keys is None:
|
|
1608
1572
|
self.key_field = None
|
|
1609
1573
|
else:
|
|
1610
1574
|
try:
|
|
1611
|
-
self.key_field =
|
|
1612
|
-
except
|
|
1575
|
+
self.key_field = _resolve_field_instance(keys)
|
|
1576
|
+
except _FieldInstanceResolutionError as error:
|
|
1613
1577
|
raise ValueError(
|
|
1614
|
-
'"keys" must be a subclass or instance of '
|
|
1615
|
-
"marshmallow.base.FieldABC."
|
|
1578
|
+
'"keys" must be a subclass or instance of marshmallow.fields.Field.'
|
|
1616
1579
|
) from error
|
|
1617
1580
|
|
|
1618
1581
|
if values is None:
|
|
1619
1582
|
self.value_field = None
|
|
1620
1583
|
else:
|
|
1621
1584
|
try:
|
|
1622
|
-
self.value_field =
|
|
1623
|
-
except
|
|
1585
|
+
self.value_field = _resolve_field_instance(values)
|
|
1586
|
+
except _FieldInstanceResolutionError as error:
|
|
1624
1587
|
raise ValueError(
|
|
1625
1588
|
'"values" must be a subclass or instance of '
|
|
1626
|
-
"marshmallow.
|
|
1589
|
+
"marshmallow.fields.Field."
|
|
1627
1590
|
) from error
|
|
1628
1591
|
if isinstance(self.value_field, Nested):
|
|
1629
1592
|
self.only = self.value_field.only
|
|
1630
1593
|
self.exclude = self.value_field.exclude
|
|
1631
1594
|
|
|
1632
|
-
def _bind_to_schema(self, field_name,
|
|
1633
|
-
super()._bind_to_schema(field_name,
|
|
1595
|
+
def _bind_to_schema(self, field_name, parent):
|
|
1596
|
+
super()._bind_to_schema(field_name, parent)
|
|
1634
1597
|
if self.value_field:
|
|
1635
1598
|
self.value_field = copy.deepcopy(self.value_field)
|
|
1636
1599
|
self.value_field._bind_to_schema(field_name, self)
|
|
@@ -1710,9 +1673,8 @@ class Mapping(Field):
|
|
|
1710
1673
|
return result
|
|
1711
1674
|
|
|
1712
1675
|
|
|
1713
|
-
class Dict(Mapping):
|
|
1714
|
-
"""A dict field. Supports dicts and dict-like objects
|
|
1715
|
-
Mapping with dict as the mapping_type.
|
|
1676
|
+
class Dict(Mapping[dict]):
|
|
1677
|
+
"""A dict field. Supports dicts and dict-like objects
|
|
1716
1678
|
|
|
1717
1679
|
Example: ::
|
|
1718
1680
|
|
|
@@ -1748,7 +1710,7 @@ class Url(String):
|
|
|
1748
1710
|
absolute: bool = True,
|
|
1749
1711
|
schemes: types.StrSequenceOrSet | None = None,
|
|
1750
1712
|
require_tld: bool = True,
|
|
1751
|
-
**kwargs,
|
|
1713
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
1752
1714
|
):
|
|
1753
1715
|
super().__init__(**kwargs)
|
|
1754
1716
|
|
|
@@ -1776,14 +1738,14 @@ class Email(String):
|
|
|
1776
1738
|
#: Default error messages.
|
|
1777
1739
|
default_error_messages = {"invalid": "Not a valid email address."}
|
|
1778
1740
|
|
|
1779
|
-
def __init__(self,
|
|
1780
|
-
super().__init__(
|
|
1741
|
+
def __init__(self, **kwargs: Unpack[_BaseFieldKwargs]) -> None:
|
|
1742
|
+
super().__init__(**kwargs)
|
|
1781
1743
|
# Insert validation into self.validators so that multiple errors can be stored.
|
|
1782
1744
|
validator = validate.Email(error=self.error_messages["invalid"])
|
|
1783
1745
|
self.validators.insert(0, validator)
|
|
1784
1746
|
|
|
1785
1747
|
|
|
1786
|
-
class IP(Field):
|
|
1748
|
+
class IP(Field[typing.Union[ipaddress.IPv4Address, ipaddress.IPv6Address]]):
|
|
1787
1749
|
"""A IP address field.
|
|
1788
1750
|
|
|
1789
1751
|
:param exploded: If `True`, serialize ipv6 address in long form, ie. with groups
|
|
@@ -1796,8 +1758,8 @@ class IP(Field):
|
|
|
1796
1758
|
|
|
1797
1759
|
DESERIALIZATION_CLASS: type | None = None
|
|
1798
1760
|
|
|
1799
|
-
def __init__(self,
|
|
1800
|
-
super().__init__(
|
|
1761
|
+
def __init__(self, *, exploded: bool = False, **kwargs: Unpack[_BaseFieldKwargs]):
|
|
1762
|
+
super().__init__(**kwargs)
|
|
1801
1763
|
self.exploded = exploded
|
|
1802
1764
|
|
|
1803
1765
|
def _serialize(self, value, attr, obj, **kwargs) -> str | None:
|
|
@@ -1809,9 +1771,7 @@ class IP(Field):
|
|
|
1809
1771
|
|
|
1810
1772
|
def _deserialize(
|
|
1811
1773
|
self, value, attr, data, **kwargs
|
|
1812
|
-
) -> ipaddress.IPv4Address | ipaddress.IPv6Address
|
|
1813
|
-
if value is None:
|
|
1814
|
-
return None
|
|
1774
|
+
) -> ipaddress.IPv4Address | ipaddress.IPv6Address:
|
|
1815
1775
|
try:
|
|
1816
1776
|
return (self.DESERIALIZATION_CLASS or ipaddress.ip_address)(
|
|
1817
1777
|
utils.ensure_text_type(value)
|
|
@@ -1842,7 +1802,9 @@ class IPv6(IP):
|
|
|
1842
1802
|
DESERIALIZATION_CLASS = ipaddress.IPv6Address
|
|
1843
1803
|
|
|
1844
1804
|
|
|
1845
|
-
class IPInterface(
|
|
1805
|
+
class IPInterface(
|
|
1806
|
+
Field[typing.Union[ipaddress.IPv4Interface, ipaddress.IPv6Interface]]
|
|
1807
|
+
):
|
|
1846
1808
|
"""A IPInterface field.
|
|
1847
1809
|
|
|
1848
1810
|
IP interface is the non-strict form of the IPNetwork type where arbitrary host
|
|
@@ -1860,8 +1822,8 @@ class IPInterface(Field):
|
|
|
1860
1822
|
|
|
1861
1823
|
DESERIALIZATION_CLASS: type | None = None
|
|
1862
1824
|
|
|
1863
|
-
def __init__(self,
|
|
1864
|
-
super().__init__(
|
|
1825
|
+
def __init__(self, *, exploded: bool = False, **kwargs: Unpack[_BaseFieldKwargs]):
|
|
1826
|
+
super().__init__(**kwargs)
|
|
1865
1827
|
self.exploded = exploded
|
|
1866
1828
|
|
|
1867
1829
|
def _serialize(self, value, attr, obj, **kwargs) -> str | None:
|
|
@@ -1871,11 +1833,9 @@ class IPInterface(Field):
|
|
|
1871
1833
|
return value.exploded
|
|
1872
1834
|
return value.compressed
|
|
1873
1835
|
|
|
1874
|
-
def _deserialize(
|
|
1875
|
-
|
|
1876
|
-
):
|
|
1877
|
-
if value is None:
|
|
1878
|
-
return None
|
|
1836
|
+
def _deserialize(
|
|
1837
|
+
self, value, attr, data, **kwargs
|
|
1838
|
+
) -> ipaddress.IPv4Interface | ipaddress.IPv6Interface:
|
|
1879
1839
|
try:
|
|
1880
1840
|
return (self.DESERIALIZATION_CLASS or ipaddress.ip_interface)(
|
|
1881
1841
|
utils.ensure_text_type(value)
|
|
@@ -1900,7 +1860,10 @@ class IPv6Interface(IPInterface):
|
|
|
1900
1860
|
DESERIALIZATION_CLASS = ipaddress.IPv6Interface
|
|
1901
1861
|
|
|
1902
1862
|
|
|
1903
|
-
|
|
1863
|
+
_EnumT = typing.TypeVar("_EnumT", bound=EnumType)
|
|
1864
|
+
|
|
1865
|
+
|
|
1866
|
+
class Enum(Field[_EnumT]):
|
|
1904
1867
|
"""An Enum field (de)serializing enum members by symbol (name) or by value.
|
|
1905
1868
|
|
|
1906
1869
|
:param enum: Enum class
|
|
@@ -1908,7 +1871,7 @@ class Enum(Field):
|
|
|
1908
1871
|
or Field class or instance to use to (de)serialize by value. Defaults to False.
|
|
1909
1872
|
|
|
1910
1873
|
If `by_value` is `False` (default), enum members are (de)serialized by symbol (name).
|
|
1911
|
-
If it is `True`, they are (de)serialized by value using
|
|
1874
|
+
If it is `True`, they are (de)serialized by value using `marshmallow.fields.Raw`.
|
|
1912
1875
|
If it is a field instance or class, they are (de)serialized by value using this field.
|
|
1913
1876
|
|
|
1914
1877
|
.. versionadded:: 3.18.0
|
|
@@ -1920,10 +1883,10 @@ class Enum(Field):
|
|
|
1920
1883
|
|
|
1921
1884
|
def __init__(
|
|
1922
1885
|
self,
|
|
1923
|
-
enum: type[
|
|
1886
|
+
enum: type[_EnumT],
|
|
1924
1887
|
*,
|
|
1925
1888
|
by_value: bool | Field | type[Field] = False,
|
|
1926
|
-
**kwargs,
|
|
1889
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
1927
1890
|
):
|
|
1928
1891
|
super().__init__(**kwargs)
|
|
1929
1892
|
self.enum = enum
|
|
@@ -1941,17 +1904,19 @@ class Enum(Field):
|
|
|
1941
1904
|
self.field = Raw()
|
|
1942
1905
|
else:
|
|
1943
1906
|
try:
|
|
1944
|
-
self.field =
|
|
1945
|
-
except
|
|
1907
|
+
self.field = _resolve_field_instance(by_value)
|
|
1908
|
+
except _FieldInstanceResolutionError as error:
|
|
1946
1909
|
raise ValueError(
|
|
1947
1910
|
'"by_value" must be either a bool or a subclass or instance of '
|
|
1948
|
-
"marshmallow.
|
|
1911
|
+
"marshmallow.fields.Field."
|
|
1949
1912
|
) from error
|
|
1950
1913
|
self.choices_text = ", ".join(
|
|
1951
1914
|
str(self.field._serialize(m.value, None, None)) for m in enum
|
|
1952
1915
|
)
|
|
1953
1916
|
|
|
1954
|
-
def _serialize(
|
|
1917
|
+
def _serialize(
|
|
1918
|
+
self, value: _EnumT | None, attr: str | None, obj: typing.Any, **kwargs
|
|
1919
|
+
) -> typing.Any | None:
|
|
1955
1920
|
if value is None:
|
|
1956
1921
|
return None
|
|
1957
1922
|
if self.by_value:
|
|
@@ -1960,7 +1925,9 @@ class Enum(Field):
|
|
|
1960
1925
|
val = value.name
|
|
1961
1926
|
return self.field._serialize(val, attr, obj, **kwargs)
|
|
1962
1927
|
|
|
1963
|
-
def _deserialize(self, value, attr, data, **kwargs):
|
|
1928
|
+
def _deserialize(self, value, attr, data, **kwargs) -> _EnumT:
|
|
1929
|
+
if isinstance(value, self.enum):
|
|
1930
|
+
return value
|
|
1964
1931
|
val = self.field._deserialize(value, attr, data, **kwargs)
|
|
1965
1932
|
if self.by_value:
|
|
1966
1933
|
try:
|
|
@@ -1983,10 +1950,6 @@ class Method(Field):
|
|
|
1983
1950
|
a value The method must take a single argument ``value``, which is the
|
|
1984
1951
|
value to deserialize.
|
|
1985
1952
|
|
|
1986
|
-
.. versionchanged:: 2.3.0
|
|
1987
|
-
Deprecated ``method_name`` parameter in favor of ``serialize`` and allow
|
|
1988
|
-
``serialize`` to not be passed at all.
|
|
1989
|
-
|
|
1990
1953
|
.. versionchanged:: 3.0.0
|
|
1991
1954
|
Removed ``method_name`` parameter.
|
|
1992
1955
|
"""
|
|
@@ -1997,7 +1960,7 @@ class Method(Field):
|
|
|
1997
1960
|
self,
|
|
1998
1961
|
serialize: str | None = None,
|
|
1999
1962
|
deserialize: str | None = None,
|
|
2000
|
-
**kwargs,
|
|
1963
|
+
**kwargs: Unpack[_BaseFieldKwargs], # FIXME: Omit dump_only and load_only
|
|
2001
1964
|
):
|
|
2002
1965
|
# Set dump_only and load_only based on arguments
|
|
2003
1966
|
kwargs["dump_only"] = bool(serialize) and not bool(deserialize)
|
|
@@ -2008,18 +1971,18 @@ class Method(Field):
|
|
|
2008
1971
|
self._serialize_method = None
|
|
2009
1972
|
self._deserialize_method = None
|
|
2010
1973
|
|
|
2011
|
-
def _bind_to_schema(self, field_name,
|
|
1974
|
+
def _bind_to_schema(self, field_name, parent):
|
|
2012
1975
|
if self.serialize_method_name:
|
|
2013
1976
|
self._serialize_method = utils.callable_or_raise(
|
|
2014
|
-
getattr(
|
|
1977
|
+
getattr(parent, self.serialize_method_name)
|
|
2015
1978
|
)
|
|
2016
1979
|
|
|
2017
1980
|
if self.deserialize_method_name:
|
|
2018
1981
|
self._deserialize_method = utils.callable_or_raise(
|
|
2019
|
-
getattr(
|
|
1982
|
+
getattr(parent, self.deserialize_method_name)
|
|
2020
1983
|
)
|
|
2021
1984
|
|
|
2022
|
-
super()._bind_to_schema(field_name,
|
|
1985
|
+
super()._bind_to_schema(field_name, parent)
|
|
2023
1986
|
|
|
2024
1987
|
def _serialize(self, value, attr, obj, **kwargs):
|
|
2025
1988
|
if self._serialize_method is not None:
|
|
@@ -2037,22 +2000,20 @@ class Function(Field):
|
|
|
2037
2000
|
|
|
2038
2001
|
:param serialize: A callable from which to retrieve the value.
|
|
2039
2002
|
The function must take a single argument ``obj`` which is the object
|
|
2040
|
-
to be serialized.
|
|
2041
|
-
which is a dictionary of context variables passed to the serializer.
|
|
2003
|
+
to be serialized.
|
|
2042
2004
|
If no callable is provided then the ```load_only``` flag will be set
|
|
2043
2005
|
to True.
|
|
2044
2006
|
:param deserialize: A callable from which to retrieve the value.
|
|
2045
2007
|
The function must take a single argument ``value`` which is the value
|
|
2046
|
-
to be deserialized.
|
|
2047
|
-
which is a dictionary of context variables passed to the deserializer.
|
|
2008
|
+
to be deserialized.
|
|
2048
2009
|
If no callable is provided then ```value``` will be passed through
|
|
2049
2010
|
unchanged.
|
|
2050
2011
|
|
|
2051
|
-
.. versionchanged:: 2.3.0
|
|
2052
|
-
Deprecated ``func`` parameter in favor of ``serialize``.
|
|
2053
|
-
|
|
2054
2012
|
.. versionchanged:: 3.0.0a1
|
|
2055
2013
|
Removed ``func`` parameter.
|
|
2014
|
+
|
|
2015
|
+
.. versionchanged:: 4.0.0
|
|
2016
|
+
Don't pass context to serialization and deserialization functions.
|
|
2056
2017
|
"""
|
|
2057
2018
|
|
|
2058
2019
|
_CHECK_ATTRIBUTE = False
|
|
@@ -2069,7 +2030,7 @@ class Function(Field):
|
|
|
2069
2030
|
| typing.Callable[[typing.Any, dict], typing.Any]
|
|
2070
2031
|
| None
|
|
2071
2032
|
) = None,
|
|
2072
|
-
**kwargs,
|
|
2033
|
+
**kwargs: Unpack[_BaseFieldKwargs], # FIXME: Omit dump_only and load_only
|
|
2073
2034
|
):
|
|
2074
2035
|
# Set dump_only and load_only based on arguments
|
|
2075
2036
|
kwargs["dump_only"] = bool(serialize) and not bool(deserialize)
|
|
@@ -2079,23 +2040,18 @@ class Function(Field):
|
|
|
2079
2040
|
self.deserialize_func = deserialize and utils.callable_or_raise(deserialize)
|
|
2080
2041
|
|
|
2081
2042
|
def _serialize(self, value, attr, obj, **kwargs):
|
|
2082
|
-
return self.
|
|
2043
|
+
return self.serialize_func(obj)
|
|
2083
2044
|
|
|
2084
2045
|
def _deserialize(self, value, attr, data, **kwargs):
|
|
2085
2046
|
if self.deserialize_func:
|
|
2086
|
-
return self.
|
|
2047
|
+
return self.deserialize_func(value)
|
|
2087
2048
|
return value
|
|
2088
2049
|
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
if self.parent.context is None:
|
|
2092
|
-
msg = f"No context available for Function field {attr!r}"
|
|
2093
|
-
raise ValidationError(msg)
|
|
2094
|
-
return func(value, self.parent.context)
|
|
2095
|
-
return func(value)
|
|
2050
|
+
|
|
2051
|
+
_ContantT = typing.TypeVar("_ContantT")
|
|
2096
2052
|
|
|
2097
2053
|
|
|
2098
|
-
class Constant(Field):
|
|
2054
|
+
class Constant(Field[_ContantT]):
|
|
2099
2055
|
"""A field that (de)serializes to a preset constant. If you only want the
|
|
2100
2056
|
constant added for serialization or deserialization, you should use
|
|
2101
2057
|
``dump_only=True`` or ``load_only=True`` respectively.
|
|
@@ -2105,49 +2061,22 @@ class Constant(Field):
|
|
|
2105
2061
|
|
|
2106
2062
|
_CHECK_ATTRIBUTE = False
|
|
2107
2063
|
|
|
2108
|
-
def __init__(self, constant:
|
|
2064
|
+
def __init__(self, constant: _ContantT, **kwargs: Unpack[_BaseFieldKwargs]):
|
|
2109
2065
|
super().__init__(**kwargs)
|
|
2110
2066
|
self.constant = constant
|
|
2111
2067
|
self.load_default = constant
|
|
2112
2068
|
self.dump_default = constant
|
|
2113
2069
|
|
|
2114
|
-
def _serialize(self, value, *args, **kwargs):
|
|
2070
|
+
def _serialize(self, value, *args, **kwargs) -> _ContantT:
|
|
2115
2071
|
return self.constant
|
|
2116
2072
|
|
|
2117
|
-
def _deserialize(self, value, *args, **kwargs):
|
|
2073
|
+
def _deserialize(self, value, *args, **kwargs) -> _ContantT:
|
|
2118
2074
|
return self.constant
|
|
2119
2075
|
|
|
2120
2076
|
|
|
2121
|
-
class Inferred(Field):
|
|
2122
|
-
"""A field that infers how to serialize, based on the value type.
|
|
2123
|
-
|
|
2124
|
-
.. warning::
|
|
2125
|
-
|
|
2126
|
-
This class is treated as private API.
|
|
2127
|
-
Users should not need to use this class directly.
|
|
2128
|
-
"""
|
|
2129
|
-
|
|
2130
|
-
def __init__(self):
|
|
2131
|
-
super().__init__()
|
|
2132
|
-
# We memoize the fields to avoid creating and binding new fields
|
|
2133
|
-
# every time on serialization.
|
|
2134
|
-
self._field_cache = {}
|
|
2135
|
-
|
|
2136
|
-
def _serialize(self, value, attr, obj, **kwargs):
|
|
2137
|
-
field_cls = self.root.TYPE_MAPPING.get(type(value))
|
|
2138
|
-
if field_cls is None:
|
|
2139
|
-
field = super()
|
|
2140
|
-
else:
|
|
2141
|
-
field = self._field_cache.get(field_cls)
|
|
2142
|
-
if field is None:
|
|
2143
|
-
field = field_cls()
|
|
2144
|
-
field._bind_to_schema(self.name, self.parent)
|
|
2145
|
-
self._field_cache[field_cls] = field
|
|
2146
|
-
return field._serialize(value, attr, obj, **kwargs)
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
2077
|
# Aliases
|
|
2150
2078
|
URL = Url
|
|
2079
|
+
|
|
2151
2080
|
Str = String
|
|
2152
2081
|
Bool = Boolean
|
|
2153
2082
|
Int = Integer
|