marshmallow 3.26.1__py3-none-any.whl → 4.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
marshmallow/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,7 +348,8 @@ 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
355
  fields: typing.ClassVar[tuple[str, ...] | list[str]]
@@ -415,15 +383,13 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
415
383
  """ Module to use for `loads <marshmallow.Schema.loads>` and `dumps <marshmallow.Schema.dumps>`.
416
384
  Defaults to `json` from the standard library.
417
385
  """
418
- ordered: typing.ClassVar[bool]
419
- """If `True`, `Schema.dump <marshmallow.Schema.dump>` is a `collections.OrderedDict`."""
420
386
  index_errors: typing.ClassVar[bool]
421
387
  """If `True`, errors dictionaries will include the index of invalid items in a collection."""
422
388
  load_only: typing.ClassVar[tuple[str, ...] | list[str]]
423
389
  """Fields to exclude from serialized results"""
424
390
  dump_only: typing.ClassVar[tuple[str, ...] | list[str]]
425
391
  """Fields to exclude from serialized results"""
426
- unknown: typing.ClassVar[str]
392
+ unknown: typing.ClassVar[types.UnknownOption]
427
393
  """Whether to exclude, include, or raise an error for unknown fields in the data.
428
394
  Use `EXCLUDE`, `INCLUDE` or `RAISE`.
429
395
  """
@@ -440,11 +406,10 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
440
406
  only: types.StrSequenceOrSet | None = None,
441
407
  exclude: types.StrSequenceOrSet = (),
442
408
  many: bool | None = None,
443
- context: dict | None = None,
444
409
  load_only: types.StrSequenceOrSet = (),
445
410
  dump_only: types.StrSequenceOrSet = (),
446
411
  partial: bool | types.StrSequenceOrSet | None = None,
447
- unknown: str | None = None,
412
+ unknown: types.UnknownOption | None = None,
448
413
  ):
449
414
  # Raise error if only or exclude is passed as string, not list of strings
450
415
  if only is not None and not is_collection(only):
@@ -458,23 +423,12 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
458
423
  self.exclude: set[typing.Any] | typing.MutableSet[typing.Any] = set(
459
424
  self.opts.exclude
460
425
  ) | set(exclude)
461
- self.ordered = self.opts.ordered
462
426
  self.load_only = set(load_only) or set(self.opts.load_only)
463
427
  self.dump_only = set(dump_only) or set(self.opts.dump_only)
464
428
  self.partial = partial
465
- self.unknown = (
466
- self.opts.unknown
467
- if unknown is None
468
- else validate_unknown_parameter_value(unknown)
429
+ self.unknown: types.UnknownOption = (
430
+ self.opts.unknown if unknown is None else unknown
469
431
  )
470
- if context:
471
- warnings.warn(
472
- "The `context` parameter is deprecated and will be removed in marshmallow 4.0. "
473
- "Use `contextvars.ContextVar` to pass context instead.",
474
- RemovedInMarshmallow4Warning,
475
- stacklevel=2,
476
- )
477
- self.context = context or {}
478
432
  self._normalize_nested_options()
479
433
  #: Dictionary mapping field_names -> :class:`Field` objects
480
434
  self.fields: dict[str, Field] = {}
@@ -491,13 +445,6 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
491
445
  def __repr__(self) -> str:
492
446
  return f"<{self.__class__.__name__}(many={self.many})>"
493
447
 
494
- @property
495
- def dict_class(self) -> type[dict]:
496
- """`dict` type to return when serializing."""
497
- if self.ordered:
498
- return OrderedDict
499
- return dict
500
-
501
448
  @classmethod
502
449
  def from_dict(
503
450
  cls,
@@ -602,9 +549,8 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
602
549
  for `self.many` is used.
603
550
  :return: Serialized data
604
551
 
605
- .. versionadded:: 1.0.0
606
552
  .. versionchanged:: 3.0.0b7
607
- This method returns the serialized data rather than a ``(data, errors)`` duple.
553
+ This method returns the serialized data rather than a ``(data, errors)`` tuple.
608
554
  A :exc:`ValidationError <marshmallow.exceptions.ValidationError>` is raised
609
555
  if ``obj`` is invalid.
610
556
  .. versionchanged:: 3.0.0rc9
@@ -635,9 +581,8 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
635
581
  for `self.many` is used.
636
582
  :return: A ``json`` string
637
583
 
638
- .. versionadded:: 1.0.0
639
584
  .. versionchanged:: 3.0.0b7
640
- This method returns the serialized data rather than a ``(data, errors)`` duple.
585
+ This method returns the serialized data rather than a ``(data, errors)`` tuple.
641
586
  A :exc:`ValidationError <marshmallow.exceptions.ValidationError>` is raised
642
587
  if ``obj`` is invalid.
643
588
  """
@@ -646,15 +591,12 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
646
591
 
647
592
  def _deserialize(
648
593
  self,
649
- data: (
650
- typing.Mapping[str, typing.Any]
651
- | typing.Iterable[typing.Mapping[str, typing.Any]]
652
- ),
594
+ data: Mapping[str, typing.Any] | Sequence[Mapping[str, typing.Any]],
653
595
  *,
654
596
  error_store: ErrorStore,
655
597
  many: bool = False,
656
598
  partial=None,
657
- unknown=RAISE,
599
+ unknown: types.UnknownOption = RAISE,
658
600
  index=None,
659
601
  ) -> typing.Any | list[typing.Any]:
660
602
  """Deserialize ``data``.
@@ -676,13 +618,13 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
676
618
  index_errors = self.opts.index_errors
677
619
  index = index if index_errors else None
678
620
  if many:
679
- if not is_collection(data):
621
+ if not is_sequence_but_not_string(data):
680
622
  error_store.store_error([self.error_messages["type"]], index=index)
681
623
  ret_l = []
682
624
  else:
683
625
  ret_l = [
684
626
  self._deserialize(
685
- typing.cast(dict, d),
627
+ d,
686
628
  error_store=error_store,
687
629
  many=False,
688
630
  partial=partial,
@@ -760,14 +702,11 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
760
702
 
761
703
  def load(
762
704
  self,
763
- data: (
764
- typing.Mapping[str, typing.Any]
765
- | typing.Iterable[typing.Mapping[str, typing.Any]]
766
- ),
705
+ data: Mapping[str, typing.Any] | Sequence[Mapping[str, typing.Any]],
767
706
  *,
768
707
  many: bool | None = None,
769
708
  partial: bool | types.StrSequenceOrSet | None = None,
770
- unknown: str | None = None,
709
+ unknown: types.UnknownOption | None = None,
771
710
  ):
772
711
  """Deserialize a data structure to an object defined by this Schema's fields.
773
712
 
@@ -783,9 +722,8 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
783
722
  If `None`, the value for `self.unknown` is used.
784
723
  :return: Deserialized data
785
724
 
786
- .. versionadded:: 1.0.0
787
725
  .. versionchanged:: 3.0.0b7
788
- This method returns the deserialized data rather than a ``(data, errors)`` duple.
726
+ This method returns the deserialized data rather than a ``(data, errors)`` tuple.
789
727
  A :exc:`ValidationError <marshmallow.exceptions.ValidationError>` is raised
790
728
  if invalid data are passed.
791
729
  """
@@ -795,17 +733,18 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
795
733
 
796
734
  def loads(
797
735
  self,
798
- json_data: str | bytes | bytearray,
736
+ s: str | bytes | bytearray,
737
+ /,
799
738
  *,
800
739
  many: bool | None = None,
801
740
  partial: bool | types.StrSequenceOrSet | None = None,
802
- unknown: str | None = None,
741
+ unknown: types.UnknownOption | None = None,
803
742
  **kwargs,
804
743
  ):
805
744
  """Same as :meth:`load`, except it uses `marshmallow.Schema.Meta.render_module` to deserialize
806
745
  the passed string before passing data to :meth:`load`.
807
746
 
808
- :param json_data: A string of the data to deserialize.
747
+ :param s: A string of the data to deserialize.
809
748
  :param many: Whether to deserialize `obj` as a collection. If `None`, the
810
749
  value for `self.many` is used.
811
750
  :param partial: Whether to ignore missing fields and not require
@@ -817,13 +756,14 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
817
756
  If `None`, the value for `self.unknown` is used.
818
757
  :return: Deserialized data
819
758
 
820
- .. versionadded:: 1.0.0
821
759
  .. versionchanged:: 3.0.0b7
822
- This method returns the deserialized data rather than a ``(data, errors)`` duple.
760
+ This method returns the deserialized data rather than a ``(data, errors)`` tuple.
823
761
  A :exc:`ValidationError <marshmallow.exceptions.ValidationError>` is raised
824
762
  if invalid data are passed.
763
+ .. versionchanged:: 4.0.0
764
+ Rename ``json_module`` parameter to ``s``.
825
765
  """
826
- data = self.opts.render_module.loads(json_data, **kwargs)
766
+ data = self.opts.render_module.loads(s, **kwargs)
827
767
  return self.load(data, many=many, partial=partial, unknown=unknown)
828
768
 
829
769
  def _run_validator(
@@ -835,14 +775,17 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
835
775
  error_store: ErrorStore,
836
776
  many: bool,
837
777
  partial: bool | types.StrSequenceOrSet | None,
778
+ unknown: types.UnknownOption | None,
838
779
  pass_original: bool,
839
780
  index: int | None = None,
840
781
  ):
841
782
  try:
842
783
  if pass_original: # Pass original, raw data (before unmarshalling)
843
- validator_func(output, original_data, partial=partial, many=many)
784
+ validator_func(
785
+ output, original_data, partial=partial, many=many, unknown=unknown
786
+ )
844
787
  else:
845
- validator_func(output, partial=partial, many=many)
788
+ validator_func(output, partial=partial, many=many, unknown=unknown)
846
789
  except ValidationError as err:
847
790
  field_name = err.field_name
848
791
  data_key: str
@@ -867,10 +810,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
867
810
 
868
811
  def validate(
869
812
  self,
870
- data: (
871
- typing.Mapping[str, typing.Any]
872
- | typing.Iterable[typing.Mapping[str, typing.Any]]
873
- ),
813
+ data: Mapping[str, typing.Any] | Sequence[Mapping[str, typing.Any]],
874
814
  *,
875
815
  many: bool | None = None,
876
816
  partial: bool | types.StrSequenceOrSet | None = None,
@@ -886,27 +826,22 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
886
826
  its value is an iterable, only missing fields listed in that iterable
887
827
  will be ignored. Use dot delimiters to specify nested fields.
888
828
  :return: A dictionary of validation errors.
889
-
890
- .. versionadded:: 1.1.0
891
829
  """
892
830
  try:
893
831
  self._do_load(data, many=many, partial=partial, postprocess=False)
894
832
  except ValidationError as exc:
895
- return typing.cast(dict[str, list[str]], exc.messages)
833
+ return typing.cast("dict[str, list[str]]", exc.messages)
896
834
  return {}
897
835
 
898
836
  ##### Private Helpers #####
899
837
 
900
838
  def _do_load(
901
839
  self,
902
- data: (
903
- typing.Mapping[str, typing.Any]
904
- | typing.Iterable[typing.Mapping[str, typing.Any]]
905
- ),
840
+ data: (Mapping[str, typing.Any] | Sequence[Mapping[str, typing.Any]]),
906
841
  *,
907
842
  many: bool | None = None,
908
843
  partial: bool | types.StrSequenceOrSet | None = None,
909
- unknown: str | None = None,
844
+ unknown: types.UnknownOption | None = None,
910
845
  postprocess: bool = True,
911
846
  ):
912
847
  """Deserialize `data`, returning the deserialized result.
@@ -928,18 +863,19 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
928
863
  error_store = ErrorStore()
929
864
  errors: dict[str, list[str]] = {}
930
865
  many = self.many if many is None else bool(many)
931
- unknown = (
932
- self.unknown
933
- if unknown is None
934
- else validate_unknown_parameter_value(unknown)
935
- )
866
+ unknown = self.unknown if unknown is None else unknown
936
867
  if partial is None:
937
868
  partial = self.partial
938
869
  # Run preprocessors
939
870
  if self._hooks[PRE_LOAD]:
940
871
  try:
941
872
  processed_data = self._invoke_load_processors(
942
- 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,
943
879
  )
944
880
  except ValidationError as err:
945
881
  errors = err.normalized_messages()
@@ -964,20 +900,22 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
964
900
  field_errors = bool(error_store.errors)
965
901
  self._invoke_schema_validators(
966
902
  error_store=error_store,
967
- pass_many=True,
903
+ pass_collection=True,
968
904
  data=result,
969
905
  original_data=data,
970
906
  many=many,
971
907
  partial=partial,
908
+ unknown=unknown,
972
909
  field_errors=field_errors,
973
910
  )
974
911
  self._invoke_schema_validators(
975
912
  error_store=error_store,
976
- pass_many=False,
913
+ pass_collection=False,
977
914
  data=result,
978
915
  original_data=data,
979
916
  many=many,
980
917
  partial=partial,
918
+ unknown=unknown,
981
919
  field_errors=field_errors,
982
920
  )
983
921
  errors = error_store.errors
@@ -990,6 +928,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
990
928
  many=many,
991
929
  original_data=data,
992
930
  partial=partial,
931
+ unknown=unknown,
993
932
  )
994
933
  except ValidationError as err:
995
934
  errors = err.normalized_messages()
@@ -1044,8 +983,6 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
1044
983
  available_field_names = self.set_class(self.opts.fields)
1045
984
  else:
1046
985
  available_field_names = self.set_class(self.declared_fields.keys())
1047
- if self.opts.additional:
1048
- available_field_names |= self.set_class(self.opts.additional)
1049
986
 
1050
987
  invalid_fields = self.set_class()
1051
988
 
@@ -1070,7 +1007,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
1070
1007
 
1071
1008
  fields_dict = self.dict_class()
1072
1009
  for field_name in field_names:
1073
- field_obj = self.declared_fields.get(field_name, ma_fields.Inferred())
1010
+ field_obj = self.declared_fields[field_name]
1074
1011
  self._bind_field(field_name, field_obj)
1075
1012
  fields_dict[field_name] = field_obj
1076
1013
 
@@ -1129,129 +1066,128 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
1129
1066
  field_obj.load_only = True
1130
1067
  if field_name in self.dump_only:
1131
1068
  field_obj.dump_only = True
1132
- try:
1133
- field_obj._bind_to_schema(field_name, self) # noqa: SLF001
1134
- except TypeError as error:
1135
- # Field declared as a class, not an instance. Ignore type checking because
1136
- # we handle unsupported arg types, i.e. this is dead code from
1137
- # the type checker's perspective.
1138
- if isinstance(field_obj, type) and issubclass(field_obj, base.FieldABC):
1139
- msg = (
1140
- f'Field for "{field_name}" must be declared as a '
1141
- "Field instance, not a class. "
1142
- f'Did you mean "fields.{field_obj.__name__}()"?' # type: ignore[attr-defined]
1143
- )
1144
- raise TypeError(msg) from error
1145
- raise
1069
+ field_obj._bind_to_schema(field_name, self)
1146
1070
  self.on_bind_field(field_name, field_obj)
1147
1071
 
1148
1072
  def _invoke_dump_processors(
1149
1073
  self, tag: str, data, *, many: bool, original_data=None
1150
1074
  ):
1151
- # The pass_many post-dump processors may do things like add an envelope, so
1152
- # 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
1153
1077
  # to get a list of items.
1154
1078
  data = self._invoke_processors(
1155
- 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,
1156
1084
  )
1157
1085
  return self._invoke_processors(
1158
- 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
1159
1087
  )
1160
1088
 
1161
1089
  def _invoke_load_processors(
1162
1090
  self,
1163
1091
  tag: str,
1164
- data,
1092
+ data: Mapping[str, typing.Any] | Sequence[Mapping[str, typing.Any]],
1165
1093
  *,
1166
1094
  many: bool,
1167
1095
  original_data,
1168
1096
  partial: bool | types.StrSequenceOrSet | None,
1097
+ unknown: types.UnknownOption | None,
1169
1098
  ):
1170
- # 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
1171
1100
  # processors first.
1172
1101
  data = self._invoke_processors(
1173
1102
  tag,
1174
- pass_many=True,
1103
+ pass_collection=True,
1175
1104
  data=data,
1176
1105
  many=many,
1177
1106
  original_data=original_data,
1178
1107
  partial=partial,
1108
+ unknown=unknown,
1179
1109
  )
1180
1110
  return self._invoke_processors(
1181
1111
  tag,
1182
- pass_many=False,
1112
+ pass_collection=False,
1183
1113
  data=data,
1184
1114
  many=many,
1185
1115
  original_data=original_data,
1186
1116
  partial=partial,
1117
+ unknown=unknown,
1187
1118
  )
1188
1119
 
1189
1120
  def _invoke_field_validators(self, *, error_store: ErrorStore, data, many: bool):
1190
1121
  for attr_name, _, validator_kwargs in self._hooks[VALIDATES]:
1191
1122
  validator = getattr(self, attr_name)
1192
- field_name = validator_kwargs["field_name"]
1193
1123
 
1194
- try:
1195
- field_obj = self.fields[field_name]
1196
- except KeyError as error:
1197
- if field_name in self.declared_fields:
1198
- continue
1199
- raise ValueError(f'"{field_name}" field does not exist.') from error
1124
+ field_names = validator_kwargs["field_names"]
1200
1125
 
1201
- data_key = (
1202
- field_obj.data_key if field_obj.data_key is not None else field_name
1203
- )
1204
- if many:
1205
- 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:
1206
1156
  try:
1207
- value = item[field_obj.attribute or field_name]
1157
+ value = data[field_obj.attribute or field_name]
1208
1158
  except KeyError:
1209
1159
  pass
1210
1160
  else:
1211
1161
  validated_value = self._call_and_store(
1212
- getter_func=validator,
1162
+ getter_func=do_validate,
1213
1163
  data=value,
1214
1164
  field_name=data_key,
1215
1165
  error_store=error_store,
1216
- index=(idx if self.opts.index_errors else None),
1217
1166
  )
1218
1167
  if validated_value is missing:
1219
- item.pop(field_name, None)
1220
- else:
1221
- try:
1222
- value = data[field_obj.attribute or field_name]
1223
- except KeyError:
1224
- pass
1225
- else:
1226
- validated_value = self._call_and_store(
1227
- getter_func=validator,
1228
- data=value,
1229
- field_name=data_key,
1230
- error_store=error_store,
1231
- )
1232
- if validated_value is missing:
1233
- data.pop(field_name, None)
1168
+ data.pop(field_name, None)
1234
1169
 
1235
1170
  def _invoke_schema_validators(
1236
1171
  self,
1237
1172
  *,
1238
1173
  error_store: ErrorStore,
1239
- pass_many: bool,
1174
+ pass_collection: bool,
1240
1175
  data,
1241
1176
  original_data,
1242
1177
  many: bool,
1243
1178
  partial: bool | types.StrSequenceOrSet | None,
1244
1179
  field_errors: bool = False,
1180
+ unknown: types.UnknownOption | None,
1245
1181
  ):
1246
1182
  for attr_name, hook_many, validator_kwargs in self._hooks[VALIDATES_SCHEMA]:
1247
- if hook_many != pass_many:
1183
+ if hook_many != pass_collection:
1248
1184
  continue
1249
1185
  validator = getattr(self, attr_name)
1250
1186
  if field_errors and validator_kwargs["skip_on_field_errors"]:
1251
1187
  continue
1252
1188
  pass_original = validator_kwargs.get("pass_original", False)
1253
1189
 
1254
- if many and not pass_many:
1190
+ if many and not pass_collection:
1255
1191
  for idx, (item, orig) in enumerate(zip(data, original_data)):
1256
1192
  self._run_validator(
1257
1193
  validator,
@@ -1260,6 +1196,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
1260
1196
  error_store=error_store,
1261
1197
  many=many,
1262
1198
  partial=partial,
1199
+ unknown=unknown,
1263
1200
  index=idx,
1264
1201
  pass_original=pass_original,
1265
1202
  )
@@ -1272,26 +1209,27 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
1272
1209
  many=many,
1273
1210
  pass_original=pass_original,
1274
1211
  partial=partial,
1212
+ unknown=unknown,
1275
1213
  )
1276
1214
 
1277
1215
  def _invoke_processors(
1278
1216
  self,
1279
1217
  tag: str,
1280
1218
  *,
1281
- pass_many: bool,
1282
- data,
1219
+ pass_collection: bool,
1220
+ data: Mapping[str, typing.Any] | Sequence[Mapping[str, typing.Any]],
1283
1221
  many: bool,
1284
1222
  original_data=None,
1285
1223
  **kwargs,
1286
1224
  ):
1287
1225
  for attr_name, hook_many, processor_kwargs in self._hooks[tag]:
1288
- if hook_many != pass_many:
1226
+ if hook_many != pass_collection:
1289
1227
  continue
1290
1228
  # This will be a bound method.
1291
1229
  processor = getattr(self, attr_name)
1292
1230
  pass_original = processor_kwargs.get("pass_original", False)
1293
1231
 
1294
- if many and not pass_many:
1232
+ if many and not pass_collection:
1295
1233
  if pass_original:
1296
1234
  data = [
1297
1235
  processor(item, original, many=many, **kwargs)