marshmallow 3.26.0__py3-none-any.whl → 4.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- marshmallow/__init__.py +1 -51
- marshmallow/constants.py +22 -0
- marshmallow/decorators.py +56 -40
- marshmallow/exceptions.py +3 -3
- marshmallow/experimental/__init__.py +5 -0
- marshmallow/experimental/context.py +73 -0
- marshmallow/fields.py +379 -452
- marshmallow/schema.py +140 -199
- marshmallow/types.py +14 -6
- marshmallow/utils.py +17 -226
- marshmallow/validate.py +2 -16
- {marshmallow-3.26.0.dist-info → marshmallow-4.0.0.dist-info}/METADATA +8 -6
- marshmallow-4.0.0.dist-info/RECORD +19 -0
- {marshmallow-3.26.0.dist-info → marshmallow-4.0.0.dist-info}/WHEEL +1 -1
- marshmallow/base.py +0 -61
- marshmallow/warnings.py +0 -10
- marshmallow-3.26.0.dist-info/RECORD +0 -18
- {marshmallow-3.26.0.dist-info → marshmallow-4.0.0.dist-info/licenses}/LICENSE +0 -0
marshmallow/fields.py
CHANGED
|
@@ -1,42 +1,43 @@
|
|
|
1
1
|
# ruff: noqa: F841, 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,46 +500,31 @@ 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:
|
|
@@ -603,9 +537,8 @@ class Nested(Field):
|
|
|
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
561
|
schema_class = class_registry.get_class(nested, all=False)
|
|
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
|
)
|
|
@@ -675,10 +605,10 @@ class Nested(Field):
|
|
|
675
605
|
self,
|
|
676
606
|
value: typing.Any,
|
|
677
607
|
attr: str | None,
|
|
678
|
-
data: typing.Mapping[str, typing.Any] | None
|
|
608
|
+
data: typing.Mapping[str, typing.Any] | None,
|
|
679
609
|
partial: bool | types.StrSequenceOrSet | None = None,
|
|
680
610
|
**kwargs,
|
|
681
|
-
)
|
|
611
|
+
):
|
|
682
612
|
"""Same as :meth:`Field._deserialize` with additional ``partial`` argument.
|
|
683
613
|
|
|
684
614
|
:param partial: For nested schemas, the ``partial``
|
|
@@ -712,9 +642,8 @@ class Pluck(Nested):
|
|
|
712
642
|
loaded = AlbumSchema().load(in_data) # => {'artist': {'id': 42}}
|
|
713
643
|
dumped = AlbumSchema().dump(loaded) # => {'artist': 42}
|
|
714
644
|
|
|
715
|
-
:param nested: The Schema class or class name (string)
|
|
716
|
-
|
|
717
|
-
:param field_name: The key to pluck a value from.
|
|
645
|
+
:param nested: The Schema class or class name (string) to nest
|
|
646
|
+
:param str field_name: The key to pluck a value from.
|
|
718
647
|
:param kwargs: The same keyword arguments that :class:`Nested` receives.
|
|
719
648
|
"""
|
|
720
649
|
|
|
@@ -724,8 +653,8 @@ class Pluck(Nested):
|
|
|
724
653
|
field_name: str,
|
|
725
654
|
*,
|
|
726
655
|
many: bool = False,
|
|
727
|
-
unknown:
|
|
728
|
-
**kwargs,
|
|
656
|
+
unknown: types.UnknownOption | None = None,
|
|
657
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
729
658
|
):
|
|
730
659
|
super().__init__(
|
|
731
660
|
nested, only=(field_name,), many=many, unknown=unknown, **kwargs
|
|
@@ -754,7 +683,7 @@ class Pluck(Nested):
|
|
|
754
683
|
return self._load(value, partial=partial)
|
|
755
684
|
|
|
756
685
|
|
|
757
|
-
class List(Field):
|
|
686
|
+
class List(Field[list[typing.Optional[_InternalT]]]):
|
|
758
687
|
"""A list field, composed with another `Field` class or
|
|
759
688
|
instance.
|
|
760
689
|
|
|
@@ -772,33 +701,37 @@ class List(Field):
|
|
|
772
701
|
#: Default error messages.
|
|
773
702
|
default_error_messages = {"invalid": "Not a valid list."}
|
|
774
703
|
|
|
775
|
-
def __init__(
|
|
704
|
+
def __init__(
|
|
705
|
+
self,
|
|
706
|
+
cls_or_instance: Field[_InternalT] | type[Field[_InternalT]],
|
|
707
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
708
|
+
):
|
|
776
709
|
super().__init__(**kwargs)
|
|
777
710
|
try:
|
|
778
|
-
self.inner =
|
|
779
|
-
except
|
|
711
|
+
self.inner: Field[_InternalT] = _resolve_field_instance(cls_or_instance)
|
|
712
|
+
except _FieldInstanceResolutionError as error:
|
|
780
713
|
raise ValueError(
|
|
781
714
|
"The list elements must be a subclass or instance of "
|
|
782
|
-
"marshmallow.
|
|
715
|
+
"marshmallow.fields.Field."
|
|
783
716
|
) from error
|
|
784
717
|
if isinstance(self.inner, Nested):
|
|
785
718
|
self.only = self.inner.only
|
|
786
719
|
self.exclude = self.inner.exclude
|
|
787
720
|
|
|
788
|
-
def _bind_to_schema(self, field_name: str,
|
|
789
|
-
super()._bind_to_schema(field_name,
|
|
721
|
+
def _bind_to_schema(self, field_name: str, parent: Schema | Field) -> None:
|
|
722
|
+
super()._bind_to_schema(field_name, parent)
|
|
790
723
|
self.inner = copy.deepcopy(self.inner)
|
|
791
724
|
self.inner._bind_to_schema(field_name, self)
|
|
792
725
|
if isinstance(self.inner, Nested):
|
|
793
726
|
self.inner.only = self.only
|
|
794
727
|
self.inner.exclude = self.exclude
|
|
795
728
|
|
|
796
|
-
def _serialize(self, value, attr, obj, **kwargs) -> list[
|
|
729
|
+
def _serialize(self, value, attr, obj, **kwargs) -> list[_InternalT] | None:
|
|
797
730
|
if value is None:
|
|
798
731
|
return None
|
|
799
732
|
return [self.inner._serialize(each, attr, obj, **kwargs) for each in value]
|
|
800
733
|
|
|
801
|
-
def _deserialize(self, value, attr, data, **kwargs) -> list[
|
|
734
|
+
def _deserialize(self, value, attr, data, **kwargs) -> list[_InternalT | None]:
|
|
802
735
|
if not utils.is_collection(value):
|
|
803
736
|
raise self.make_error("invalid")
|
|
804
737
|
|
|
@@ -809,14 +742,14 @@ class List(Field):
|
|
|
809
742
|
result.append(self.inner.deserialize(each, **kwargs))
|
|
810
743
|
except ValidationError as error:
|
|
811
744
|
if error.valid_data is not None:
|
|
812
|
-
result.append(error.valid_data)
|
|
745
|
+
result.append(typing.cast("_InternalT", error.valid_data))
|
|
813
746
|
errors.update({idx: error.messages})
|
|
814
747
|
if errors:
|
|
815
748
|
raise ValidationError(errors, valid_data=result)
|
|
816
749
|
return result
|
|
817
750
|
|
|
818
751
|
|
|
819
|
-
class Tuple(Field):
|
|
752
|
+
class Tuple(Field[tuple]):
|
|
820
753
|
"""A tuple field, composed of a fixed number of other `Field` classes or
|
|
821
754
|
instances
|
|
822
755
|
|
|
@@ -842,7 +775,7 @@ class Tuple(Field):
|
|
|
842
775
|
def __init__(
|
|
843
776
|
self,
|
|
844
777
|
tuple_fields: typing.Iterable[Field] | typing.Iterable[type[Field]],
|
|
845
|
-
**kwargs,
|
|
778
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
846
779
|
):
|
|
847
780
|
super().__init__(**kwargs)
|
|
848
781
|
if not utils.is_collection(tuple_fields):
|
|
@@ -852,19 +785,19 @@ class Tuple(Field):
|
|
|
852
785
|
|
|
853
786
|
try:
|
|
854
787
|
self.tuple_fields = [
|
|
855
|
-
|
|
788
|
+
_resolve_field_instance(cls_or_instance)
|
|
856
789
|
for cls_or_instance in tuple_fields
|
|
857
790
|
]
|
|
858
|
-
except
|
|
791
|
+
except _FieldInstanceResolutionError as error:
|
|
859
792
|
raise ValueError(
|
|
860
793
|
'Elements of "tuple_fields" must be subclasses or '
|
|
861
|
-
"instances of marshmallow.
|
|
794
|
+
"instances of marshmallow.fields.Field."
|
|
862
795
|
) from error
|
|
863
796
|
|
|
864
797
|
self.validate_length = Length(equal=len(self.tuple_fields))
|
|
865
798
|
|
|
866
|
-
def _bind_to_schema(self, field_name: str,
|
|
867
|
-
super()._bind_to_schema(field_name,
|
|
799
|
+
def _bind_to_schema(self, field_name: str, parent: Schema | Field) -> None:
|
|
800
|
+
super()._bind_to_schema(field_name, parent)
|
|
868
801
|
new_tuple_fields = []
|
|
869
802
|
for field in self.tuple_fields:
|
|
870
803
|
new_field = copy.deepcopy(field)
|
|
@@ -873,7 +806,9 @@ class Tuple(Field):
|
|
|
873
806
|
|
|
874
807
|
self.tuple_fields = new_tuple_fields
|
|
875
808
|
|
|
876
|
-
def _serialize(
|
|
809
|
+
def _serialize(
|
|
810
|
+
self, value: tuple | None, attr: str | None, obj: typing.Any, **kwargs
|
|
811
|
+
) -> tuple | None:
|
|
877
812
|
if value is None:
|
|
878
813
|
return None
|
|
879
814
|
|
|
@@ -882,8 +817,14 @@ class Tuple(Field):
|
|
|
882
817
|
for field, each in zip(self.tuple_fields, value)
|
|
883
818
|
)
|
|
884
819
|
|
|
885
|
-
def _deserialize(
|
|
886
|
-
|
|
820
|
+
def _deserialize(
|
|
821
|
+
self,
|
|
822
|
+
value: typing.Any,
|
|
823
|
+
attr: str | None,
|
|
824
|
+
data: typing.Mapping[str, typing.Any] | None,
|
|
825
|
+
**kwargs,
|
|
826
|
+
) -> tuple:
|
|
827
|
+
if not utils.is_sequence_but_not_string(value):
|
|
887
828
|
raise self.make_error("invalid")
|
|
888
829
|
|
|
889
830
|
self.validate_length(value)
|
|
@@ -904,7 +845,7 @@ class Tuple(Field):
|
|
|
904
845
|
return tuple(result)
|
|
905
846
|
|
|
906
847
|
|
|
907
|
-
class String(Field):
|
|
848
|
+
class String(Field[str]):
|
|
908
849
|
"""A string field.
|
|
909
850
|
|
|
910
851
|
:param kwargs: The same keyword arguments that :class:`Field` receives.
|
|
@@ -921,7 +862,7 @@ class String(Field):
|
|
|
921
862
|
return None
|
|
922
863
|
return utils.ensure_text_type(value)
|
|
923
864
|
|
|
924
|
-
def _deserialize(self, value, attr, data, **kwargs) ->
|
|
865
|
+
def _deserialize(self, value, attr, data, **kwargs) -> str:
|
|
925
866
|
if not isinstance(value, (str, bytes)):
|
|
926
867
|
raise self.make_error("invalid")
|
|
927
868
|
try:
|
|
@@ -930,16 +871,14 @@ class String(Field):
|
|
|
930
871
|
raise self.make_error("invalid_utf8") from error
|
|
931
872
|
|
|
932
873
|
|
|
933
|
-
class UUID(
|
|
874
|
+
class UUID(Field[uuid.UUID]):
|
|
934
875
|
"""A UUID field."""
|
|
935
876
|
|
|
936
877
|
#: Default error messages.
|
|
937
878
|
default_error_messages = {"invalid_uuid": "Not a valid UUID."}
|
|
938
879
|
|
|
939
|
-
def _validated(self, value) -> uuid.UUID
|
|
880
|
+
def _validated(self, value) -> uuid.UUID:
|
|
940
881
|
"""Format the value or raise a :exc:`ValidationError` if an error occurs."""
|
|
941
|
-
if value is None:
|
|
942
|
-
return None
|
|
943
882
|
if isinstance(value, uuid.UUID):
|
|
944
883
|
return value
|
|
945
884
|
try:
|
|
@@ -949,15 +888,20 @@ class UUID(String):
|
|
|
949
888
|
except (ValueError, AttributeError, TypeError) as error:
|
|
950
889
|
raise self.make_error("invalid_uuid") from error
|
|
951
890
|
|
|
952
|
-
def
|
|
891
|
+
def _serialize(self, value, attr, obj, **kwargs) -> str | None:
|
|
892
|
+
if value is None:
|
|
893
|
+
return None
|
|
894
|
+
return str(value)
|
|
895
|
+
|
|
896
|
+
def _deserialize(self, value, attr, data, **kwargs) -> uuid.UUID:
|
|
953
897
|
return self._validated(value)
|
|
954
898
|
|
|
955
899
|
|
|
956
|
-
|
|
900
|
+
_NumT = typing.TypeVar("_NumT")
|
|
957
901
|
|
|
958
902
|
|
|
959
|
-
class Number(Field
|
|
960
|
-
"""Base class for number fields.
|
|
903
|
+
class Number(Field[_NumT]):
|
|
904
|
+
"""Base class for number fields. This class should not be used within schemas.
|
|
961
905
|
|
|
962
906
|
:param as_string: If `True`, format the serialized value as a string.
|
|
963
907
|
:param kwargs: The same keyword arguments that :class:`Field` receives.
|
|
@@ -967,7 +911,7 @@ class Number(Field, typing.Generic[_NumType]):
|
|
|
967
911
|
Use `Integer <marshmallow.fields.Integer>`, `Float <marshmallow.fields.Float>`, or `Decimal <marshmallow.fields.Decimal>` instead.
|
|
968
912
|
"""
|
|
969
913
|
|
|
970
|
-
num_type: type
|
|
914
|
+
num_type: type[_NumT]
|
|
971
915
|
|
|
972
916
|
#: Default error messages.
|
|
973
917
|
default_error_messages = {
|
|
@@ -975,21 +919,15 @@ class Number(Field, typing.Generic[_NumType]):
|
|
|
975
919
|
"too_large": "Number too large.",
|
|
976
920
|
}
|
|
977
921
|
|
|
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
|
-
)
|
|
922
|
+
def __init__(self, *, as_string: bool = False, **kwargs: Unpack[_BaseFieldKwargs]):
|
|
985
923
|
self.as_string = as_string
|
|
986
924
|
super().__init__(**kwargs)
|
|
987
925
|
|
|
988
|
-
def _format_num(self, value) ->
|
|
926
|
+
def _format_num(self, value) -> _NumT:
|
|
989
927
|
"""Return the number value for value, given this field's `num_type`."""
|
|
990
|
-
return self.num_type(value)
|
|
928
|
+
return self.num_type(value) # type: ignore[call-arg]
|
|
991
929
|
|
|
992
|
-
def _validated(self, value: typing.Any) ->
|
|
930
|
+
def _validated(self, value: typing.Any) -> _NumT:
|
|
993
931
|
"""Format the value or raise a :exc:`ValidationError` if an error occurs."""
|
|
994
932
|
# (value is True or value is False) is ~5x faster than isinstance(value, bool)
|
|
995
933
|
if value is True or value is False:
|
|
@@ -1001,17 +939,17 @@ class Number(Field, typing.Generic[_NumType]):
|
|
|
1001
939
|
except OverflowError as error:
|
|
1002
940
|
raise self.make_error("too_large", input=value) from error
|
|
1003
941
|
|
|
1004
|
-
def _to_string(self, value:
|
|
942
|
+
def _to_string(self, value: _NumT) -> str:
|
|
1005
943
|
return str(value)
|
|
1006
944
|
|
|
1007
|
-
def _serialize(self, value, attr, obj, **kwargs) -> str |
|
|
945
|
+
def _serialize(self, value, attr, obj, **kwargs) -> str | _NumT | None:
|
|
1008
946
|
"""Return a string if `self.as_string=True`, otherwise return this field's `num_type`."""
|
|
1009
947
|
if value is None:
|
|
1010
948
|
return None
|
|
1011
|
-
ret:
|
|
949
|
+
ret: _NumT = self._format_num(value)
|
|
1012
950
|
return self._to_string(ret) if self.as_string else ret
|
|
1013
951
|
|
|
1014
|
-
def _deserialize(self, value, attr, data, **kwargs) ->
|
|
952
|
+
def _deserialize(self, value, attr, data, **kwargs) -> _NumT:
|
|
1015
953
|
return self._validated(value)
|
|
1016
954
|
|
|
1017
955
|
|
|
@@ -1028,9 +966,15 @@ class Integer(Number[int]):
|
|
|
1028
966
|
#: Default error messages.
|
|
1029
967
|
default_error_messages = {"invalid": "Not a valid integer."}
|
|
1030
968
|
|
|
1031
|
-
def __init__(
|
|
969
|
+
def __init__(
|
|
970
|
+
self,
|
|
971
|
+
*,
|
|
972
|
+
strict: bool = False,
|
|
973
|
+
as_string: bool = False,
|
|
974
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
975
|
+
):
|
|
1032
976
|
self.strict = strict
|
|
1033
|
-
super().__init__(**kwargs)
|
|
977
|
+
super().__init__(as_string=as_string, **kwargs)
|
|
1034
978
|
|
|
1035
979
|
# override Number
|
|
1036
980
|
def _validated(self, value: typing.Any) -> int:
|
|
@@ -1055,7 +999,13 @@ class Float(Number[float]):
|
|
|
1055
999
|
"special": "Special numeric values (nan or infinity) are not permitted."
|
|
1056
1000
|
}
|
|
1057
1001
|
|
|
1058
|
-
def __init__(
|
|
1002
|
+
def __init__(
|
|
1003
|
+
self,
|
|
1004
|
+
*,
|
|
1005
|
+
allow_nan: bool = False,
|
|
1006
|
+
as_string: bool = False,
|
|
1007
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
1008
|
+
):
|
|
1059
1009
|
self.allow_nan = allow_nan
|
|
1060
1010
|
super().__init__(as_string=as_string, **kwargs)
|
|
1061
1011
|
|
|
@@ -1100,8 +1050,6 @@ class Decimal(Number[decimal.Decimal]):
|
|
|
1100
1050
|
:param as_string: If `True`, serialize to a string instead of a Python
|
|
1101
1051
|
`decimal.Decimal` type.
|
|
1102
1052
|
:param kwargs: The same keyword arguments that :class:`Number` receives.
|
|
1103
|
-
|
|
1104
|
-
.. versionadded:: 1.2.0
|
|
1105
1053
|
"""
|
|
1106
1054
|
|
|
1107
1055
|
num_type = decimal.Decimal
|
|
@@ -1118,7 +1066,7 @@ class Decimal(Number[decimal.Decimal]):
|
|
|
1118
1066
|
*,
|
|
1119
1067
|
allow_nan: bool = False,
|
|
1120
1068
|
as_string: bool = False,
|
|
1121
|
-
**kwargs,
|
|
1069
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
1122
1070
|
):
|
|
1123
1071
|
self.places = (
|
|
1124
1072
|
decimal.Decimal((0, (1,), -places)) if places is not None else None
|
|
@@ -1152,7 +1100,7 @@ class Decimal(Number[decimal.Decimal]):
|
|
|
1152
1100
|
return format(value, "f")
|
|
1153
1101
|
|
|
1154
1102
|
|
|
1155
|
-
class Boolean(Field):
|
|
1103
|
+
class Boolean(Field[bool]):
|
|
1156
1104
|
"""A boolean field.
|
|
1157
1105
|
|
|
1158
1106
|
:param truthy: Values that will (de)serialize to `True`. If an empty
|
|
@@ -1213,7 +1161,7 @@ class Boolean(Field):
|
|
|
1213
1161
|
*,
|
|
1214
1162
|
truthy: typing.Iterable | None = None,
|
|
1215
1163
|
falsy: typing.Iterable | None = None,
|
|
1216
|
-
**kwargs,
|
|
1164
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
1217
1165
|
):
|
|
1218
1166
|
super().__init__(**kwargs)
|
|
1219
1167
|
|
|
@@ -1222,23 +1170,13 @@ class Boolean(Field):
|
|
|
1222
1170
|
if falsy is not None:
|
|
1223
1171
|
self.falsy = set(falsy)
|
|
1224
1172
|
|
|
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):
|
|
1173
|
+
def _deserialize(
|
|
1174
|
+
self,
|
|
1175
|
+
value: typing.Any,
|
|
1176
|
+
attr: str | None,
|
|
1177
|
+
data: typing.Mapping[str, typing.Any] | None,
|
|
1178
|
+
**kwargs,
|
|
1179
|
+
) -> bool:
|
|
1242
1180
|
if not self.truthy:
|
|
1243
1181
|
return bool(value)
|
|
1244
1182
|
try:
|
|
@@ -1251,69 +1189,45 @@ class Boolean(Field):
|
|
|
1251
1189
|
raise self.make_error("invalid", input=value)
|
|
1252
1190
|
|
|
1253
1191
|
|
|
1254
|
-
|
|
1255
|
-
"""A formatted datetime string.
|
|
1192
|
+
_D = typing.TypeVar("_D", dt.datetime, dt.date, dt.time)
|
|
1256
1193
|
|
|
1257
|
-
Example: ``'2014-12-22T03:12:58.019077+00:00'``
|
|
1258
1194
|
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
If `None`, defaults to "iso".
|
|
1262
|
-
:param kwargs: The same keyword arguments that :class:`Field` receives.
|
|
1195
|
+
class _TemporalField(Field[_D], metaclass=abc.ABCMeta):
|
|
1196
|
+
"""Base field for date and time related fields including common (de)serialization logic."""
|
|
1263
1197
|
|
|
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"
|
|
1198
|
+
# Subclasses should define each of these class constants
|
|
1199
|
+
SERIALIZATION_FUNCS: dict[str, typing.Callable[[_D], str | float]]
|
|
1200
|
+
DESERIALIZATION_FUNCS: dict[str, typing.Callable[[str], _D]]
|
|
1201
|
+
DEFAULT_FORMAT: str
|
|
1202
|
+
OBJ_TYPE: str
|
|
1203
|
+
SCHEMA_OPTS_VAR_NAME: str
|
|
1293
1204
|
|
|
1294
|
-
#: Default error messages.
|
|
1295
1205
|
default_error_messages = {
|
|
1296
1206
|
"invalid": "Not a valid {obj_type}.",
|
|
1297
1207
|
"invalid_awareness": "Not a valid {awareness} {obj_type}.",
|
|
1298
1208
|
"format": '"{input}" cannot be formatted as a {obj_type}.',
|
|
1299
1209
|
}
|
|
1300
1210
|
|
|
1301
|
-
def __init__(
|
|
1211
|
+
def __init__(
|
|
1212
|
+
self,
|
|
1213
|
+
format: str | None = None, # noqa: A002
|
|
1214
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
1215
|
+
) -> None:
|
|
1302
1216
|
super().__init__(**kwargs)
|
|
1303
1217
|
# Allow this to be None. It may be set later in the ``_serialize``
|
|
1304
1218
|
# or ``_deserialize`` methods. This allows a Schema to dynamically set the
|
|
1305
1219
|
# format, e.g. from a Meta option
|
|
1306
1220
|
self.format = format
|
|
1307
1221
|
|
|
1308
|
-
def _bind_to_schema(self, field_name,
|
|
1309
|
-
super()._bind_to_schema(field_name,
|
|
1222
|
+
def _bind_to_schema(self, field_name, parent):
|
|
1223
|
+
super()._bind_to_schema(field_name, parent)
|
|
1310
1224
|
self.format = (
|
|
1311
1225
|
self.format
|
|
1312
1226
|
or getattr(self.root.opts, self.SCHEMA_OPTS_VAR_NAME)
|
|
1313
1227
|
or self.DEFAULT_FORMAT
|
|
1314
1228
|
)
|
|
1315
1229
|
|
|
1316
|
-
def _serialize(self, value, attr, obj, **kwargs) -> str | float | None:
|
|
1230
|
+
def _serialize(self, value: _D | None, attr, obj, **kwargs) -> str | float | None:
|
|
1317
1231
|
if value is None:
|
|
1318
1232
|
return None
|
|
1319
1233
|
data_format = self.format or self.DEFAULT_FORMAT
|
|
@@ -1322,7 +1236,10 @@ class DateTime(Field):
|
|
|
1322
1236
|
return format_func(value)
|
|
1323
1237
|
return value.strftime(data_format)
|
|
1324
1238
|
|
|
1325
|
-
def _deserialize(self, value, attr, data, **kwargs) ->
|
|
1239
|
+
def _deserialize(self, value, attr, data, **kwargs) -> _D:
|
|
1240
|
+
internal_type: type[_D] = getattr(dt, self.OBJ_TYPE)
|
|
1241
|
+
if isinstance(value, internal_type):
|
|
1242
|
+
return value
|
|
1326
1243
|
data_format = self.format or self.DEFAULT_FORMAT
|
|
1327
1244
|
func = self.DESERIALIZATION_FUNCS.get(data_format)
|
|
1328
1245
|
try:
|
|
@@ -1334,6 +1251,51 @@ class DateTime(Field):
|
|
|
1334
1251
|
"invalid", input=value, obj_type=self.OBJ_TYPE
|
|
1335
1252
|
) from error
|
|
1336
1253
|
|
|
1254
|
+
@staticmethod
|
|
1255
|
+
@abc.abstractmethod
|
|
1256
|
+
def _make_object_from_format(value: typing.Any, data_format: str) -> _D: ...
|
|
1257
|
+
|
|
1258
|
+
|
|
1259
|
+
class DateTime(_TemporalField[dt.datetime]):
|
|
1260
|
+
"""A formatted datetime string.
|
|
1261
|
+
|
|
1262
|
+
Example: ``'2014-12-22T03:12:58.019077+00:00'``
|
|
1263
|
+
|
|
1264
|
+
:param format: Either ``"rfc"`` (for RFC822), ``"iso"`` (for ISO8601),
|
|
1265
|
+
``"timestamp"``, ``"timestamp_ms"`` (for a POSIX timestamp) or a date format string.
|
|
1266
|
+
If `None`, defaults to "iso".
|
|
1267
|
+
:param kwargs: The same keyword arguments that :class:`Field` receives.
|
|
1268
|
+
|
|
1269
|
+
.. versionchanged:: 3.0.0rc9
|
|
1270
|
+
Does not modify timezone information on (de)serialization.
|
|
1271
|
+
.. versionchanged:: 3.19
|
|
1272
|
+
Add timestamp as a format.
|
|
1273
|
+
"""
|
|
1274
|
+
|
|
1275
|
+
SERIALIZATION_FUNCS: dict[str, typing.Callable[[dt.datetime], str | float]] = {
|
|
1276
|
+
"iso": dt.datetime.isoformat,
|
|
1277
|
+
"iso8601": dt.datetime.isoformat,
|
|
1278
|
+
"rfc": email.utils.format_datetime,
|
|
1279
|
+
"rfc822": email.utils.format_datetime,
|
|
1280
|
+
"timestamp": utils.timestamp,
|
|
1281
|
+
"timestamp_ms": utils.timestamp_ms,
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
DESERIALIZATION_FUNCS: dict[str, typing.Callable[[str], dt.datetime]] = {
|
|
1285
|
+
"iso": dt.datetime.fromisoformat,
|
|
1286
|
+
"iso8601": dt.datetime.fromisoformat,
|
|
1287
|
+
"rfc": email.utils.parsedate_to_datetime,
|
|
1288
|
+
"rfc822": email.utils.parsedate_to_datetime,
|
|
1289
|
+
"timestamp": utils.from_timestamp,
|
|
1290
|
+
"timestamp_ms": utils.from_timestamp_ms,
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
DEFAULT_FORMAT = "iso"
|
|
1294
|
+
|
|
1295
|
+
OBJ_TYPE = "datetime"
|
|
1296
|
+
|
|
1297
|
+
SCHEMA_OPTS_VAR_NAME = "datetimeformat"
|
|
1298
|
+
|
|
1337
1299
|
@staticmethod
|
|
1338
1300
|
def _make_object_from_format(value, data_format) -> dt.datetime:
|
|
1339
1301
|
return dt.datetime.strptime(value, data_format)
|
|
@@ -1359,14 +1321,14 @@ class NaiveDateTime(DateTime):
|
|
|
1359
1321
|
format: str | None = None, # noqa: A002
|
|
1360
1322
|
*,
|
|
1361
1323
|
timezone: dt.timezone | None = None,
|
|
1362
|
-
**kwargs,
|
|
1324
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
1363
1325
|
) -> None:
|
|
1364
1326
|
super().__init__(format=format, **kwargs)
|
|
1365
1327
|
self.timezone = timezone
|
|
1366
1328
|
|
|
1367
1329
|
def _deserialize(self, value, attr, data, **kwargs) -> dt.datetime:
|
|
1368
1330
|
ret = super()._deserialize(value, attr, data, **kwargs)
|
|
1369
|
-
if is_aware(ret):
|
|
1331
|
+
if utils.is_aware(ret):
|
|
1370
1332
|
if self.timezone is None:
|
|
1371
1333
|
raise self.make_error(
|
|
1372
1334
|
"invalid_awareness",
|
|
@@ -1396,14 +1358,14 @@ class AwareDateTime(DateTime):
|
|
|
1396
1358
|
format: str | None = None, # noqa: A002
|
|
1397
1359
|
*,
|
|
1398
1360
|
default_timezone: dt.tzinfo | None = None,
|
|
1399
|
-
**kwargs,
|
|
1361
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
1400
1362
|
) -> None:
|
|
1401
1363
|
super().__init__(format=format, **kwargs)
|
|
1402
1364
|
self.default_timezone = default_timezone
|
|
1403
1365
|
|
|
1404
1366
|
def _deserialize(self, value, attr, data, **kwargs) -> dt.datetime:
|
|
1405
1367
|
ret = super()._deserialize(value, attr, data, **kwargs)
|
|
1406
|
-
if not is_aware(ret):
|
|
1368
|
+
if not utils.is_aware(ret):
|
|
1407
1369
|
if self.default_timezone is None:
|
|
1408
1370
|
raise self.make_error(
|
|
1409
1371
|
"invalid_awareness",
|
|
@@ -1414,7 +1376,7 @@ class AwareDateTime(DateTime):
|
|
|
1414
1376
|
return ret
|
|
1415
1377
|
|
|
1416
1378
|
|
|
1417
|
-
class Time(
|
|
1379
|
+
class Time(_TemporalField[dt.time]):
|
|
1418
1380
|
"""A formatted time string.
|
|
1419
1381
|
|
|
1420
1382
|
Example: ``'03:12:58.019077'``
|
|
@@ -1424,9 +1386,15 @@ class Time(DateTime):
|
|
|
1424
1386
|
:param kwargs: The same keyword arguments that :class:`Field` receives.
|
|
1425
1387
|
"""
|
|
1426
1388
|
|
|
1427
|
-
SERIALIZATION_FUNCS = {
|
|
1389
|
+
SERIALIZATION_FUNCS = {
|
|
1390
|
+
"iso": dt.time.isoformat,
|
|
1391
|
+
"iso8601": dt.time.isoformat,
|
|
1392
|
+
}
|
|
1428
1393
|
|
|
1429
|
-
DESERIALIZATION_FUNCS = {
|
|
1394
|
+
DESERIALIZATION_FUNCS = {
|
|
1395
|
+
"iso": dt.time.fromisoformat,
|
|
1396
|
+
"iso8601": dt.time.fromisoformat,
|
|
1397
|
+
}
|
|
1430
1398
|
|
|
1431
1399
|
DEFAULT_FORMAT = "iso"
|
|
1432
1400
|
|
|
@@ -1439,7 +1407,7 @@ class Time(DateTime):
|
|
|
1439
1407
|
return dt.datetime.strptime(value, data_format).time()
|
|
1440
1408
|
|
|
1441
1409
|
|
|
1442
|
-
class Date(
|
|
1410
|
+
class Date(_TemporalField[dt.date]):
|
|
1443
1411
|
"""ISO8601-formatted date string.
|
|
1444
1412
|
|
|
1445
1413
|
:param format: Either ``"iso"`` (for ISO8601) or a date format string.
|
|
@@ -1453,9 +1421,15 @@ class Date(DateTime):
|
|
|
1453
1421
|
"format": '"{input}" cannot be formatted as a date.',
|
|
1454
1422
|
}
|
|
1455
1423
|
|
|
1456
|
-
SERIALIZATION_FUNCS = {
|
|
1424
|
+
SERIALIZATION_FUNCS = {
|
|
1425
|
+
"iso": dt.date.isoformat,
|
|
1426
|
+
"iso8601": dt.date.isoformat,
|
|
1427
|
+
}
|
|
1457
1428
|
|
|
1458
|
-
DESERIALIZATION_FUNCS = {
|
|
1429
|
+
DESERIALIZATION_FUNCS = {
|
|
1430
|
+
"iso": dt.date.fromisoformat,
|
|
1431
|
+
"iso8601": dt.date.fromisoformat,
|
|
1432
|
+
}
|
|
1459
1433
|
|
|
1460
1434
|
DEFAULT_FORMAT = "iso"
|
|
1461
1435
|
|
|
@@ -1468,43 +1442,51 @@ class Date(DateTime):
|
|
|
1468
1442
|
return dt.datetime.strptime(value, data_format).date()
|
|
1469
1443
|
|
|
1470
1444
|
|
|
1471
|
-
class TimeDelta(Field):
|
|
1472
|
-
"""A field that (de)serializes a :class:`datetime.timedelta` object to
|
|
1473
|
-
|
|
1474
|
-
|
|
1445
|
+
class TimeDelta(Field[dt.timedelta]):
|
|
1446
|
+
"""A field that (de)serializes a :class:`datetime.timedelta` object to a `float`.
|
|
1447
|
+
The `float` can represent any time unit that the :class:`datetime.timedelta` constructor
|
|
1448
|
+
supports.
|
|
1475
1449
|
|
|
1476
|
-
:param precision:
|
|
1477
|
-
|
|
1478
|
-
'milliseconds', 'minutes', 'hours' or 'weeks'.
|
|
1479
|
-
:param serialization_type: Whether to (de)serialize to a `int` or `float`.
|
|
1450
|
+
:param precision: The time unit used for (de)serialization. Must be one of 'weeks',
|
|
1451
|
+
'days', 'hours', 'minutes', 'seconds', 'milliseconds' or 'microseconds'.
|
|
1480
1452
|
:param kwargs: The same keyword arguments that :class:`Field` receives.
|
|
1481
1453
|
|
|
1482
|
-
Integer Caveats
|
|
1483
|
-
---------------
|
|
1484
|
-
Any fractional parts (which depends on the precision used) will be truncated
|
|
1485
|
-
when serializing using `int`.
|
|
1486
|
-
|
|
1487
1454
|
Float Caveats
|
|
1488
1455
|
-------------
|
|
1489
|
-
|
|
1490
|
-
|
|
1456
|
+
Precision loss may occur when serializing a highly precise :class:`datetime.timedelta`
|
|
1457
|
+
object using a big ``precision`` unit due to floating point arithmetics.
|
|
1491
1458
|
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1459
|
+
When necessary, the :class:`datetime.timedelta` constructor rounds `float` inputs
|
|
1460
|
+
to whole microseconds during initialization of the object. As a result, deserializing
|
|
1461
|
+
a `float` might be subject to rounding, regardless of `precision`. For example,
|
|
1462
|
+
``TimeDelta().deserialize("1.1234567") == timedelta(seconds=1, microseconds=123457)``.
|
|
1495
1463
|
|
|
1496
1464
|
.. versionchanged:: 3.17.0
|
|
1497
|
-
Allow
|
|
1498
|
-
`int`
|
|
1465
|
+
Allow serialization to `float` through use of a new `serialization_type` parameter.
|
|
1466
|
+
Defaults to `int` for backwards compatibility. Also affects deserialization.
|
|
1467
|
+
.. versionchanged:: 4.0.0
|
|
1468
|
+
Remove `serialization_type` parameter and always serialize to float.
|
|
1469
|
+
Value is cast to a `float` upon deserialization.
|
|
1499
1470
|
"""
|
|
1500
1471
|
|
|
1472
|
+
WEEKS = "weeks"
|
|
1501
1473
|
DAYS = "days"
|
|
1474
|
+
HOURS = "hours"
|
|
1475
|
+
MINUTES = "minutes"
|
|
1502
1476
|
SECONDS = "seconds"
|
|
1503
|
-
MICROSECONDS = "microseconds"
|
|
1504
1477
|
MILLISECONDS = "milliseconds"
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1478
|
+
MICROSECONDS = "microseconds"
|
|
1479
|
+
|
|
1480
|
+
# cache this mapping on class level for performance
|
|
1481
|
+
_unit_to_microseconds_mapping = {
|
|
1482
|
+
WEEKS: 1000000 * 60 * 60 * 24 * 7,
|
|
1483
|
+
DAYS: 1000000 * 60 * 60 * 24,
|
|
1484
|
+
HOURS: 1000000 * 60 * 60,
|
|
1485
|
+
MINUTES: 1000000 * 60,
|
|
1486
|
+
SECONDS: 1000000,
|
|
1487
|
+
MILLISECONDS: 1000,
|
|
1488
|
+
MICROSECONDS: 1,
|
|
1489
|
+
}
|
|
1508
1490
|
|
|
1509
1491
|
#: Default error messages.
|
|
1510
1492
|
default_error_messages = {
|
|
@@ -1515,49 +1497,32 @@ class TimeDelta(Field):
|
|
|
1515
1497
|
def __init__(
|
|
1516
1498
|
self,
|
|
1517
1499
|
precision: str = SECONDS,
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
):
|
|
1500
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
1501
|
+
) -> None:
|
|
1521
1502
|
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
1503
|
|
|
1532
|
-
if precision not in
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
)
|
|
1504
|
+
if precision not in self._unit_to_microseconds_mapping:
|
|
1505
|
+
units = ", ".join(self._unit_to_microseconds_mapping)
|
|
1506
|
+
msg = f"The precision must be one of: {units}."
|
|
1536
1507
|
raise ValueError(msg)
|
|
1537
1508
|
|
|
1538
|
-
if serialization_type not in (int, float):
|
|
1539
|
-
raise ValueError("The serialization type must be one of int or float")
|
|
1540
|
-
|
|
1541
1509
|
self.precision = precision
|
|
1542
|
-
self.serialization_type = serialization_type
|
|
1543
1510
|
super().__init__(**kwargs)
|
|
1544
1511
|
|
|
1545
|
-
def _serialize(self, value, attr, obj, **kwargs):
|
|
1512
|
+
def _serialize(self, value, attr, obj, **kwargs) -> float | None:
|
|
1546
1513
|
if value is None:
|
|
1547
1514
|
return None
|
|
1548
1515
|
|
|
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()
|
|
1516
|
+
# limit float arithmetics to a single division to minimize precision loss
|
|
1517
|
+
microseconds: int = utils.timedelta_to_microseconds(value)
|
|
1518
|
+
microseconds_per_unit: int = self._unit_to_microseconds_mapping[self.precision]
|
|
1519
|
+
return microseconds / microseconds_per_unit
|
|
1557
1520
|
|
|
1558
|
-
def _deserialize(self, value, attr, data, **kwargs):
|
|
1521
|
+
def _deserialize(self, value, attr, data, **kwargs) -> dt.timedelta:
|
|
1522
|
+
if isinstance(value, dt.timedelta):
|
|
1523
|
+
return value
|
|
1559
1524
|
try:
|
|
1560
|
-
value =
|
|
1525
|
+
value = float(value)
|
|
1561
1526
|
except (TypeError, ValueError) as error:
|
|
1562
1527
|
raise self.make_error("invalid") from error
|
|
1563
1528
|
|
|
@@ -1569,7 +1534,10 @@ class TimeDelta(Field):
|
|
|
1569
1534
|
raise self.make_error("invalid") from error
|
|
1570
1535
|
|
|
1571
1536
|
|
|
1572
|
-
|
|
1537
|
+
_MappingT = typing.TypeVar("_MappingT", bound=_Mapping)
|
|
1538
|
+
|
|
1539
|
+
|
|
1540
|
+
class Mapping(Field[_MappingT]):
|
|
1573
1541
|
"""An abstract class for objects with key-value pairs. This class should not be used within schemas.
|
|
1574
1542
|
|
|
1575
1543
|
:param keys: A field class or instance for dict keys.
|
|
@@ -1586,7 +1554,7 @@ class Mapping(Field):
|
|
|
1586
1554
|
Use `Dict <marshmallow.fields.Dict>` instead.
|
|
1587
1555
|
"""
|
|
1588
1556
|
|
|
1589
|
-
mapping_type
|
|
1557
|
+
mapping_type: type[_MappingT]
|
|
1590
1558
|
|
|
1591
1559
|
#: Default error messages.
|
|
1592
1560
|
default_error_messages = {"invalid": "Not a valid mapping type."}
|
|
@@ -1595,42 +1563,35 @@ class Mapping(Field):
|
|
|
1595
1563
|
self,
|
|
1596
1564
|
keys: Field | type[Field] | None = None,
|
|
1597
1565
|
values: Field | type[Field] | None = None,
|
|
1598
|
-
**kwargs,
|
|
1566
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
1599
1567
|
):
|
|
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
1568
|
super().__init__(**kwargs)
|
|
1607
1569
|
if keys is None:
|
|
1608
1570
|
self.key_field = None
|
|
1609
1571
|
else:
|
|
1610
1572
|
try:
|
|
1611
|
-
self.key_field =
|
|
1612
|
-
except
|
|
1573
|
+
self.key_field = _resolve_field_instance(keys)
|
|
1574
|
+
except _FieldInstanceResolutionError as error:
|
|
1613
1575
|
raise ValueError(
|
|
1614
|
-
'"keys" must be a subclass or instance of '
|
|
1615
|
-
"marshmallow.base.FieldABC."
|
|
1576
|
+
'"keys" must be a subclass or instance of marshmallow.fields.Field.'
|
|
1616
1577
|
) from error
|
|
1617
1578
|
|
|
1618
1579
|
if values is None:
|
|
1619
1580
|
self.value_field = None
|
|
1620
1581
|
else:
|
|
1621
1582
|
try:
|
|
1622
|
-
self.value_field =
|
|
1623
|
-
except
|
|
1583
|
+
self.value_field = _resolve_field_instance(values)
|
|
1584
|
+
except _FieldInstanceResolutionError as error:
|
|
1624
1585
|
raise ValueError(
|
|
1625
1586
|
'"values" must be a subclass or instance of '
|
|
1626
|
-
"marshmallow.
|
|
1587
|
+
"marshmallow.fields.Field."
|
|
1627
1588
|
) from error
|
|
1628
1589
|
if isinstance(self.value_field, Nested):
|
|
1629
1590
|
self.only = self.value_field.only
|
|
1630
1591
|
self.exclude = self.value_field.exclude
|
|
1631
1592
|
|
|
1632
|
-
def _bind_to_schema(self, field_name,
|
|
1633
|
-
super()._bind_to_schema(field_name,
|
|
1593
|
+
def _bind_to_schema(self, field_name, parent):
|
|
1594
|
+
super()._bind_to_schema(field_name, parent)
|
|
1634
1595
|
if self.value_field:
|
|
1635
1596
|
self.value_field = copy.deepcopy(self.value_field)
|
|
1636
1597
|
self.value_field._bind_to_schema(field_name, self)
|
|
@@ -1710,9 +1671,8 @@ class Mapping(Field):
|
|
|
1710
1671
|
return result
|
|
1711
1672
|
|
|
1712
1673
|
|
|
1713
|
-
class Dict(Mapping):
|
|
1714
|
-
"""A dict field. Supports dicts and dict-like objects
|
|
1715
|
-
Mapping with dict as the mapping_type.
|
|
1674
|
+
class Dict(Mapping[dict]):
|
|
1675
|
+
"""A dict field. Supports dicts and dict-like objects
|
|
1716
1676
|
|
|
1717
1677
|
Example: ::
|
|
1718
1678
|
|
|
@@ -1748,7 +1708,7 @@ class Url(String):
|
|
|
1748
1708
|
absolute: bool = True,
|
|
1749
1709
|
schemes: types.StrSequenceOrSet | None = None,
|
|
1750
1710
|
require_tld: bool = True,
|
|
1751
|
-
**kwargs,
|
|
1711
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
1752
1712
|
):
|
|
1753
1713
|
super().__init__(**kwargs)
|
|
1754
1714
|
|
|
@@ -1776,14 +1736,14 @@ class Email(String):
|
|
|
1776
1736
|
#: Default error messages.
|
|
1777
1737
|
default_error_messages = {"invalid": "Not a valid email address."}
|
|
1778
1738
|
|
|
1779
|
-
def __init__(self,
|
|
1780
|
-
super().__init__(
|
|
1739
|
+
def __init__(self, **kwargs: Unpack[_BaseFieldKwargs]) -> None:
|
|
1740
|
+
super().__init__(**kwargs)
|
|
1781
1741
|
# Insert validation into self.validators so that multiple errors can be stored.
|
|
1782
1742
|
validator = validate.Email(error=self.error_messages["invalid"])
|
|
1783
1743
|
self.validators.insert(0, validator)
|
|
1784
1744
|
|
|
1785
1745
|
|
|
1786
|
-
class IP(Field):
|
|
1746
|
+
class IP(Field[typing.Union[ipaddress.IPv4Address, ipaddress.IPv6Address]]):
|
|
1787
1747
|
"""A IP address field.
|
|
1788
1748
|
|
|
1789
1749
|
:param exploded: If `True`, serialize ipv6 address in long form, ie. with groups
|
|
@@ -1796,8 +1756,8 @@ class IP(Field):
|
|
|
1796
1756
|
|
|
1797
1757
|
DESERIALIZATION_CLASS: type | None = None
|
|
1798
1758
|
|
|
1799
|
-
def __init__(self,
|
|
1800
|
-
super().__init__(
|
|
1759
|
+
def __init__(self, *, exploded: bool = False, **kwargs: Unpack[_BaseFieldKwargs]):
|
|
1760
|
+
super().__init__(**kwargs)
|
|
1801
1761
|
self.exploded = exploded
|
|
1802
1762
|
|
|
1803
1763
|
def _serialize(self, value, attr, obj, **kwargs) -> str | None:
|
|
@@ -1809,9 +1769,7 @@ class IP(Field):
|
|
|
1809
1769
|
|
|
1810
1770
|
def _deserialize(
|
|
1811
1771
|
self, value, attr, data, **kwargs
|
|
1812
|
-
) -> ipaddress.IPv4Address | ipaddress.IPv6Address
|
|
1813
|
-
if value is None:
|
|
1814
|
-
return None
|
|
1772
|
+
) -> ipaddress.IPv4Address | ipaddress.IPv6Address:
|
|
1815
1773
|
try:
|
|
1816
1774
|
return (self.DESERIALIZATION_CLASS or ipaddress.ip_address)(
|
|
1817
1775
|
utils.ensure_text_type(value)
|
|
@@ -1842,7 +1800,9 @@ class IPv6(IP):
|
|
|
1842
1800
|
DESERIALIZATION_CLASS = ipaddress.IPv6Address
|
|
1843
1801
|
|
|
1844
1802
|
|
|
1845
|
-
class IPInterface(
|
|
1803
|
+
class IPInterface(
|
|
1804
|
+
Field[typing.Union[ipaddress.IPv4Interface, ipaddress.IPv6Interface]]
|
|
1805
|
+
):
|
|
1846
1806
|
"""A IPInterface field.
|
|
1847
1807
|
|
|
1848
1808
|
IP interface is the non-strict form of the IPNetwork type where arbitrary host
|
|
@@ -1860,8 +1820,8 @@ class IPInterface(Field):
|
|
|
1860
1820
|
|
|
1861
1821
|
DESERIALIZATION_CLASS: type | None = None
|
|
1862
1822
|
|
|
1863
|
-
def __init__(self,
|
|
1864
|
-
super().__init__(
|
|
1823
|
+
def __init__(self, *, exploded: bool = False, **kwargs: Unpack[_BaseFieldKwargs]):
|
|
1824
|
+
super().__init__(**kwargs)
|
|
1865
1825
|
self.exploded = exploded
|
|
1866
1826
|
|
|
1867
1827
|
def _serialize(self, value, attr, obj, **kwargs) -> str | None:
|
|
@@ -1871,11 +1831,9 @@ class IPInterface(Field):
|
|
|
1871
1831
|
return value.exploded
|
|
1872
1832
|
return value.compressed
|
|
1873
1833
|
|
|
1874
|
-
def _deserialize(
|
|
1875
|
-
|
|
1876
|
-
):
|
|
1877
|
-
if value is None:
|
|
1878
|
-
return None
|
|
1834
|
+
def _deserialize(
|
|
1835
|
+
self, value, attr, data, **kwargs
|
|
1836
|
+
) -> ipaddress.IPv4Interface | ipaddress.IPv6Interface:
|
|
1879
1837
|
try:
|
|
1880
1838
|
return (self.DESERIALIZATION_CLASS or ipaddress.ip_interface)(
|
|
1881
1839
|
utils.ensure_text_type(value)
|
|
@@ -1900,7 +1858,10 @@ class IPv6Interface(IPInterface):
|
|
|
1900
1858
|
DESERIALIZATION_CLASS = ipaddress.IPv6Interface
|
|
1901
1859
|
|
|
1902
1860
|
|
|
1903
|
-
|
|
1861
|
+
_EnumT = typing.TypeVar("_EnumT", bound=EnumType)
|
|
1862
|
+
|
|
1863
|
+
|
|
1864
|
+
class Enum(Field[_EnumT]):
|
|
1904
1865
|
"""An Enum field (de)serializing enum members by symbol (name) or by value.
|
|
1905
1866
|
|
|
1906
1867
|
:param enum: Enum class
|
|
@@ -1908,7 +1869,7 @@ class Enum(Field):
|
|
|
1908
1869
|
or Field class or instance to use to (de)serialize by value. Defaults to False.
|
|
1909
1870
|
|
|
1910
1871
|
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
|
|
1872
|
+
If it is `True`, they are (de)serialized by value using `marshmallow.fields.Raw`.
|
|
1912
1873
|
If it is a field instance or class, they are (de)serialized by value using this field.
|
|
1913
1874
|
|
|
1914
1875
|
.. versionadded:: 3.18.0
|
|
@@ -1920,10 +1881,10 @@ class Enum(Field):
|
|
|
1920
1881
|
|
|
1921
1882
|
def __init__(
|
|
1922
1883
|
self,
|
|
1923
|
-
enum: type[
|
|
1884
|
+
enum: type[_EnumT],
|
|
1924
1885
|
*,
|
|
1925
1886
|
by_value: bool | Field | type[Field] = False,
|
|
1926
|
-
**kwargs,
|
|
1887
|
+
**kwargs: Unpack[_BaseFieldKwargs],
|
|
1927
1888
|
):
|
|
1928
1889
|
super().__init__(**kwargs)
|
|
1929
1890
|
self.enum = enum
|
|
@@ -1941,17 +1902,19 @@ class Enum(Field):
|
|
|
1941
1902
|
self.field = Raw()
|
|
1942
1903
|
else:
|
|
1943
1904
|
try:
|
|
1944
|
-
self.field =
|
|
1945
|
-
except
|
|
1905
|
+
self.field = _resolve_field_instance(by_value)
|
|
1906
|
+
except _FieldInstanceResolutionError as error:
|
|
1946
1907
|
raise ValueError(
|
|
1947
1908
|
'"by_value" must be either a bool or a subclass or instance of '
|
|
1948
|
-
"marshmallow.
|
|
1909
|
+
"marshmallow.fields.Field."
|
|
1949
1910
|
) from error
|
|
1950
1911
|
self.choices_text = ", ".join(
|
|
1951
1912
|
str(self.field._serialize(m.value, None, None)) for m in enum
|
|
1952
1913
|
)
|
|
1953
1914
|
|
|
1954
|
-
def _serialize(
|
|
1915
|
+
def _serialize(
|
|
1916
|
+
self, value: _EnumT | None, attr: str | None, obj: typing.Any, **kwargs
|
|
1917
|
+
) -> typing.Any | None:
|
|
1955
1918
|
if value is None:
|
|
1956
1919
|
return None
|
|
1957
1920
|
if self.by_value:
|
|
@@ -1960,7 +1923,9 @@ class Enum(Field):
|
|
|
1960
1923
|
val = value.name
|
|
1961
1924
|
return self.field._serialize(val, attr, obj, **kwargs)
|
|
1962
1925
|
|
|
1963
|
-
def _deserialize(self, value, attr, data, **kwargs):
|
|
1926
|
+
def _deserialize(self, value, attr, data, **kwargs) -> _EnumT:
|
|
1927
|
+
if isinstance(value, self.enum):
|
|
1928
|
+
return value
|
|
1964
1929
|
val = self.field._deserialize(value, attr, data, **kwargs)
|
|
1965
1930
|
if self.by_value:
|
|
1966
1931
|
try:
|
|
@@ -1983,10 +1948,6 @@ class Method(Field):
|
|
|
1983
1948
|
a value The method must take a single argument ``value``, which is the
|
|
1984
1949
|
value to deserialize.
|
|
1985
1950
|
|
|
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
1951
|
.. versionchanged:: 3.0.0
|
|
1991
1952
|
Removed ``method_name`` parameter.
|
|
1992
1953
|
"""
|
|
@@ -1997,7 +1958,7 @@ class Method(Field):
|
|
|
1997
1958
|
self,
|
|
1998
1959
|
serialize: str | None = None,
|
|
1999
1960
|
deserialize: str | None = None,
|
|
2000
|
-
**kwargs,
|
|
1961
|
+
**kwargs: Unpack[_BaseFieldKwargs], # FIXME: Omit dump_only and load_only
|
|
2001
1962
|
):
|
|
2002
1963
|
# Set dump_only and load_only based on arguments
|
|
2003
1964
|
kwargs["dump_only"] = bool(serialize) and not bool(deserialize)
|
|
@@ -2008,18 +1969,18 @@ class Method(Field):
|
|
|
2008
1969
|
self._serialize_method = None
|
|
2009
1970
|
self._deserialize_method = None
|
|
2010
1971
|
|
|
2011
|
-
def _bind_to_schema(self, field_name,
|
|
1972
|
+
def _bind_to_schema(self, field_name, parent):
|
|
2012
1973
|
if self.serialize_method_name:
|
|
2013
1974
|
self._serialize_method = utils.callable_or_raise(
|
|
2014
|
-
getattr(
|
|
1975
|
+
getattr(parent, self.serialize_method_name)
|
|
2015
1976
|
)
|
|
2016
1977
|
|
|
2017
1978
|
if self.deserialize_method_name:
|
|
2018
1979
|
self._deserialize_method = utils.callable_or_raise(
|
|
2019
|
-
getattr(
|
|
1980
|
+
getattr(parent, self.deserialize_method_name)
|
|
2020
1981
|
)
|
|
2021
1982
|
|
|
2022
|
-
super()._bind_to_schema(field_name,
|
|
1983
|
+
super()._bind_to_schema(field_name, parent)
|
|
2023
1984
|
|
|
2024
1985
|
def _serialize(self, value, attr, obj, **kwargs):
|
|
2025
1986
|
if self._serialize_method is not None:
|
|
@@ -2037,22 +1998,20 @@ class Function(Field):
|
|
|
2037
1998
|
|
|
2038
1999
|
:param serialize: A callable from which to retrieve the value.
|
|
2039
2000
|
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.
|
|
2001
|
+
to be serialized.
|
|
2042
2002
|
If no callable is provided then the ```load_only``` flag will be set
|
|
2043
2003
|
to True.
|
|
2044
2004
|
:param deserialize: A callable from which to retrieve the value.
|
|
2045
2005
|
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.
|
|
2006
|
+
to be deserialized.
|
|
2048
2007
|
If no callable is provided then ```value``` will be passed through
|
|
2049
2008
|
unchanged.
|
|
2050
2009
|
|
|
2051
|
-
.. versionchanged:: 2.3.0
|
|
2052
|
-
Deprecated ``func`` parameter in favor of ``serialize``.
|
|
2053
|
-
|
|
2054
2010
|
.. versionchanged:: 3.0.0a1
|
|
2055
2011
|
Removed ``func`` parameter.
|
|
2012
|
+
|
|
2013
|
+
.. versionchanged:: 4.0.0
|
|
2014
|
+
Don't pass context to serialization and deserialization functions.
|
|
2056
2015
|
"""
|
|
2057
2016
|
|
|
2058
2017
|
_CHECK_ATTRIBUTE = False
|
|
@@ -2069,7 +2028,7 @@ class Function(Field):
|
|
|
2069
2028
|
| typing.Callable[[typing.Any, dict], typing.Any]
|
|
2070
2029
|
| None
|
|
2071
2030
|
) = None,
|
|
2072
|
-
**kwargs,
|
|
2031
|
+
**kwargs: Unpack[_BaseFieldKwargs], # FIXME: Omit dump_only and load_only
|
|
2073
2032
|
):
|
|
2074
2033
|
# Set dump_only and load_only based on arguments
|
|
2075
2034
|
kwargs["dump_only"] = bool(serialize) and not bool(deserialize)
|
|
@@ -2079,23 +2038,18 @@ class Function(Field):
|
|
|
2079
2038
|
self.deserialize_func = deserialize and utils.callable_or_raise(deserialize)
|
|
2080
2039
|
|
|
2081
2040
|
def _serialize(self, value, attr, obj, **kwargs):
|
|
2082
|
-
return self.
|
|
2041
|
+
return self.serialize_func(obj)
|
|
2083
2042
|
|
|
2084
2043
|
def _deserialize(self, value, attr, data, **kwargs):
|
|
2085
2044
|
if self.deserialize_func:
|
|
2086
|
-
return self.
|
|
2045
|
+
return self.deserialize_func(value)
|
|
2087
2046
|
return value
|
|
2088
2047
|
|
|
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)
|
|
2048
|
+
|
|
2049
|
+
_ContantT = typing.TypeVar("_ContantT")
|
|
2096
2050
|
|
|
2097
2051
|
|
|
2098
|
-
class Constant(Field):
|
|
2052
|
+
class Constant(Field[_ContantT]):
|
|
2099
2053
|
"""A field that (de)serializes to a preset constant. If you only want the
|
|
2100
2054
|
constant added for serialization or deserialization, you should use
|
|
2101
2055
|
``dump_only=True`` or ``load_only=True`` respectively.
|
|
@@ -2105,49 +2059,22 @@ class Constant(Field):
|
|
|
2105
2059
|
|
|
2106
2060
|
_CHECK_ATTRIBUTE = False
|
|
2107
2061
|
|
|
2108
|
-
def __init__(self, constant:
|
|
2062
|
+
def __init__(self, constant: _ContantT, **kwargs: Unpack[_BaseFieldKwargs]):
|
|
2109
2063
|
super().__init__(**kwargs)
|
|
2110
2064
|
self.constant = constant
|
|
2111
2065
|
self.load_default = constant
|
|
2112
2066
|
self.dump_default = constant
|
|
2113
2067
|
|
|
2114
|
-
def _serialize(self, value, *args, **kwargs):
|
|
2068
|
+
def _serialize(self, value, *args, **kwargs) -> _ContantT:
|
|
2115
2069
|
return self.constant
|
|
2116
2070
|
|
|
2117
|
-
def _deserialize(self, value, *args, **kwargs):
|
|
2071
|
+
def _deserialize(self, value, *args, **kwargs) -> _ContantT:
|
|
2118
2072
|
return self.constant
|
|
2119
2073
|
|
|
2120
2074
|
|
|
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
2075
|
# Aliases
|
|
2150
2076
|
URL = Url
|
|
2077
|
+
|
|
2151
2078
|
Str = String
|
|
2152
2079
|
Bool = Boolean
|
|
2153
2080
|
Int = Integer
|