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