marshmallow 3.21.2__py3-none-any.whl → 3.22.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/decorators.py CHANGED
@@ -68,6 +68,7 @@ Example: ::
68
68
  from __future__ import annotations
69
69
 
70
70
  import functools
71
+ from collections import defaultdict
71
72
  from typing import Any, Callable, cast
72
73
 
73
74
  PRE_DUMP = "pre_dump"
@@ -79,7 +80,7 @@ VALIDATES_SCHEMA = "validates_schema"
79
80
 
80
81
 
81
82
  class MarshmallowHook:
82
- __marshmallow_hook__: dict[tuple[str, bool] | str, Any] | None = None
83
+ __marshmallow_hook__: dict[str, list[tuple[bool, Any]]] | None = None
83
84
 
84
85
 
85
86
  def validates(field_name: str) -> Callable[..., Any]:
@@ -117,7 +118,8 @@ def validates_schema(
117
118
  """
118
119
  return set_hook(
119
120
  fn,
120
- (VALIDATES_SCHEMA, pass_many),
121
+ VALIDATES_SCHEMA,
122
+ many=pass_many,
121
123
  pass_original=pass_original,
122
124
  skip_on_field_errors=skip_on_field_errors,
123
125
  )
@@ -136,7 +138,7 @@ def pre_dump(
136
138
  .. versionchanged:: 3.0.0
137
139
  ``many`` is always passed as a keyword arguments to the decorated method.
138
140
  """
139
- return set_hook(fn, (PRE_DUMP, pass_many))
141
+ return set_hook(fn, PRE_DUMP, many=pass_many)
140
142
 
141
143
 
142
144
  def post_dump(
@@ -157,7 +159,7 @@ def post_dump(
157
159
  .. versionchanged:: 3.0.0
158
160
  ``many`` is always passed as a keyword arguments to the decorated method.
159
161
  """
160
- return set_hook(fn, (POST_DUMP, pass_many), pass_original=pass_original)
162
+ return set_hook(fn, POST_DUMP, many=pass_many, pass_original=pass_original)
161
163
 
162
164
 
163
165
  def pre_load(
@@ -174,7 +176,7 @@ def pre_load(
174
176
  ``partial`` and ``many`` are always passed as keyword arguments to
175
177
  the decorated method.
176
178
  """
177
- return set_hook(fn, (PRE_LOAD, pass_many))
179
+ return set_hook(fn, PRE_LOAD, many=pass_many)
178
180
 
179
181
 
180
182
  def post_load(
@@ -196,11 +198,11 @@ def post_load(
196
198
  ``partial`` and ``many`` are always passed as keyword arguments to
197
199
  the decorated method.
198
200
  """
199
- return set_hook(fn, (POST_LOAD, pass_many), pass_original=pass_original)
201
+ return set_hook(fn, POST_LOAD, many=pass_many, pass_original=pass_original)
200
202
 
201
203
 
202
204
  def set_hook(
203
- fn: Callable[..., Any] | None, key: tuple[str, bool] | str, **kwargs: Any
205
+ fn: Callable[..., Any] | None, tag: str, many: bool = False, **kwargs: Any
204
206
  ) -> Callable[..., Any]:
205
207
  """Mark decorated function as a hook to be picked up later.
206
208
  You should not need to use this method directly.
@@ -214,7 +216,7 @@ def set_hook(
214
216
  """
215
217
  # Allow using this as either a decorator or a decorator factory.
216
218
  if fn is None:
217
- return functools.partial(set_hook, key=key, **kwargs)
219
+ return functools.partial(set_hook, tag=tag, many=many, **kwargs)
218
220
 
219
221
  # Set a __marshmallow_hook__ attribute instead of wrapping in some class,
220
222
  # because I still want this to end up as a normal (unbound) method.
@@ -222,10 +224,10 @@ def set_hook(
222
224
  try:
223
225
  hook_config = function.__marshmallow_hook__
224
226
  except AttributeError:
225
- function.__marshmallow_hook__ = hook_config = {}
227
+ function.__marshmallow_hook__ = hook_config = defaultdict(list)
226
228
  # Also save the kwargs for the tagged function on
227
- # __marshmallow_hook__, keyed by (<tag>, <pass_many>)
229
+ # __marshmallow_hook__, keyed by <tag>
228
230
  if hook_config is not None:
229
- hook_config[key] = kwargs
231
+ hook_config[tag].append((many, kwargs))
230
232
 
231
233
  return fn
marshmallow/schema.py CHANGED
@@ -13,7 +13,6 @@ import warnings
13
13
  from abc import ABCMeta
14
14
  from collections import OrderedDict, defaultdict
15
15
  from collections.abc import Mapping
16
- from functools import lru_cache
17
16
 
18
17
  from marshmallow import base, class_registry, types
19
18
  from marshmallow import fields as ma_fields
@@ -149,7 +148,7 @@ class SchemaMeta(ABCMeta):
149
148
  class_registry.register(name, cls)
150
149
  cls._hooks = cls.resolve_hooks()
151
150
 
152
- def resolve_hooks(cls) -> dict[types.Tag, list[str]]:
151
+ def resolve_hooks(cls) -> dict[str, list[tuple[str, bool, dict]]]:
153
152
  """Add in the decorated processors
154
153
 
155
154
  By doing this after constructing the class, we let standard inheritance
@@ -157,7 +156,7 @@ class SchemaMeta(ABCMeta):
157
156
  """
158
157
  mro = inspect.getmro(cls)
159
158
 
160
- hooks = defaultdict(list) # type: typing.Dict[types.Tag, typing.List[str]]
159
+ hooks = defaultdict(list) # type: typing.Dict[str, typing.List[typing.Tuple[str, bool, dict]]]
161
160
 
162
161
  for attr_name in dir(cls):
163
162
  # Need to look up the actual descriptor, not whatever might be
@@ -177,14 +176,16 @@ class SchemaMeta(ABCMeta):
177
176
  continue
178
177
 
179
178
  try:
180
- hook_config = attr.__marshmallow_hook__
179
+ hook_config = attr.__marshmallow_hook__ # type: typing.Dict[str, typing.List[typing.Tuple[bool, dict]]]
181
180
  except AttributeError:
182
181
  pass
183
182
  else:
184
- for key in hook_config.keys():
183
+ for tag, config in hook_config.items():
185
184
  # Use name here so we can get the bound method later, in
186
185
  # case the processor was a descriptor or something.
187
- hooks[key].append(attr_name)
186
+ hooks[tag].extend(
187
+ (attr_name, many, kwargs) for many, kwargs in config
188
+ )
188
189
 
189
190
  return hooks
190
191
 
@@ -227,6 +228,7 @@ class SchemaOpts:
227
228
  self.dump_only = getattr(meta, "dump_only", ())
228
229
  self.unknown = validate_unknown_parameter_value(getattr(meta, "unknown", RAISE))
229
230
  self.register = getattr(meta, "register", True)
231
+ self.many = getattr(meta, "many", False)
230
232
 
231
233
 
232
234
  class Schema(base.SchemaABC, metaclass=SchemaMeta):
@@ -320,7 +322,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
320
322
  # These get set by SchemaMeta
321
323
  opts = None # type: SchemaOpts
322
324
  _declared_fields = {} # type: typing.Dict[str, ma_fields.Field]
323
- _hooks = {} # type: typing.Dict[types.Tag, typing.List[str]]
325
+ _hooks = {} # type: typing.Dict[str, typing.List[typing.Tuple[str, bool, dict]]]
324
326
 
325
327
  class Meta:
326
328
  """Options object for a Schema.
@@ -343,6 +345,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
343
345
  `OrderedDict`.
344
346
  - ``exclude``: Tuple or list of fields to exclude in the serialized result.
345
347
  Nested fields can be represented with dot delimiters.
348
+ - ``many``: Whether the data is a collection by default.
346
349
  - ``dateformat``: Default format for `Date <fields.Date>` fields.
347
350
  - ``datetimeformat``: Default format for `DateTime <fields.DateTime>` fields.
348
351
  - ``timeformat``: Default format for `Time <fields.Time>` fields.
@@ -366,7 +369,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
366
369
  *,
367
370
  only: types.StrSequenceOrSet | None = None,
368
371
  exclude: types.StrSequenceOrSet = (),
369
- many: bool = False,
372
+ many: bool | None = None,
370
373
  context: dict | None = None,
371
374
  load_only: types.StrSequenceOrSet = (),
372
375
  dump_only: types.StrSequenceOrSet = (),
@@ -380,7 +383,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
380
383
  raise StringNotCollectionError('"exclude" should be a list of strings')
381
384
  # copy declared fields from metaclass
382
385
  self.declared_fields = copy.deepcopy(self._declared_fields)
383
- self.many = many
386
+ self.many = self.opts.many if many is None else many
384
387
  self.only = only
385
388
  self.exclude: set[typing.Any] | typing.MutableSet[typing.Any] = set(
386
389
  self.opts.exclude
@@ -540,7 +543,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
540
543
  Validation no longer occurs upon serialization.
541
544
  """
542
545
  many = self.many if many is None else bool(many)
543
- if self._has_processors(PRE_DUMP):
546
+ if self._hooks[PRE_DUMP]:
544
547
  processed_obj = self._invoke_dump_processors(
545
548
  PRE_DUMP, obj, many=many, original_data=obj
546
549
  )
@@ -549,7 +552,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
549
552
 
550
553
  result = self._serialize(processed_obj, many=many)
551
554
 
552
- if self._has_processors(POST_DUMP):
555
+ if self._hooks[POST_DUMP]:
553
556
  result = self._invoke_dump_processors(
554
557
  POST_DUMP, result, many=many, original_data=obj
555
558
  )
@@ -847,7 +850,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
847
850
  if partial is None:
848
851
  partial = self.partial
849
852
  # Run preprocessors
850
- if self._has_processors(PRE_LOAD):
853
+ if self._hooks[PRE_LOAD]:
851
854
  try:
852
855
  processed_data = self._invoke_load_processors(
853
856
  PRE_LOAD, data, many=many, original_data=data, partial=partial
@@ -871,7 +874,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
871
874
  error_store=error_store, data=result, many=many
872
875
  )
873
876
  # Run schema-level validation
874
- if self._has_processors(VALIDATES_SCHEMA):
877
+ if self._hooks[VALIDATES_SCHEMA]:
875
878
  field_errors = bool(error_store.errors)
876
879
  self._invoke_schema_validators(
877
880
  error_store=error_store,
@@ -893,7 +896,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
893
896
  )
894
897
  errors = error_store.errors
895
898
  # Run post processors
896
- if not errors and postprocess and self._has_processors(POST_LOAD):
899
+ if not errors and postprocess and self._hooks[POST_LOAD]:
897
900
  try:
898
901
  result = self._invoke_load_processors(
899
902
  POST_LOAD,
@@ -1056,10 +1059,6 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
1056
1059
  raise error
1057
1060
  self.on_bind_field(field_name, field_obj)
1058
1061
 
1059
- @lru_cache(maxsize=8) # noqa (https://github.com/PyCQA/flake8-bugbear/issues/310)
1060
- def _has_processors(self, tag) -> bool:
1061
- return bool(self._hooks[(tag, True)] or self._hooks[(tag, False)])
1062
-
1063
1062
  def _invoke_dump_processors(
1064
1063
  self, tag: str, data, *, many: bool, original_data=None
1065
1064
  ):
@@ -1104,9 +1103,8 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
1104
1103
  return data
1105
1104
 
1106
1105
  def _invoke_field_validators(self, *, error_store: ErrorStore, data, many: bool):
1107
- for attr_name in self._hooks[VALIDATES]:
1106
+ for attr_name, _, validator_kwargs in self._hooks[VALIDATES]:
1108
1107
  validator = getattr(self, attr_name)
1109
- validator_kwargs = validator.__marshmallow_hook__[VALIDATES]
1110
1108
  field_name = validator_kwargs["field_name"]
1111
1109
 
1112
1110
  try:
@@ -1161,11 +1159,10 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
1161
1159
  partial: bool | types.StrSequenceOrSet | None,
1162
1160
  field_errors: bool = False,
1163
1161
  ):
1164
- for attr_name in self._hooks[(VALIDATES_SCHEMA, pass_many)]:
1162
+ for attr_name, hook_many, validator_kwargs in self._hooks[VALIDATES_SCHEMA]:
1163
+ if hook_many != pass_many:
1164
+ continue
1165
1165
  validator = getattr(self, attr_name)
1166
- validator_kwargs = validator.__marshmallow_hook__[
1167
- (VALIDATES_SCHEMA, pass_many)
1168
- ]
1169
1166
  if field_errors and validator_kwargs["skip_on_field_errors"]:
1170
1167
  continue
1171
1168
  pass_original = validator_kwargs.get("pass_original", False)
@@ -1203,12 +1200,11 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
1203
1200
  original_data=None,
1204
1201
  **kwargs,
1205
1202
  ):
1206
- key = (tag, pass_many)
1207
- for attr_name in self._hooks[key]:
1203
+ for attr_name, hook_many, processor_kwargs in self._hooks[tag]:
1204
+ if hook_many != pass_many:
1205
+ continue
1208
1206
  # This will be a bound method.
1209
1207
  processor = getattr(self, attr_name)
1210
-
1211
- processor_kwargs = processor.__marshmallow_hook__[key]
1212
1208
  pass_original = processor_kwargs.get("pass_original", False)
1213
1209
 
1214
1210
  if many and not pass_many:
marshmallow/types.py CHANGED
@@ -8,5 +8,4 @@
8
8
  import typing
9
9
 
10
10
  StrSequenceOrSet = typing.Union[typing.Sequence[str], typing.AbstractSet[str]]
11
- Tag = typing.Union[str, typing.Tuple[str, bool]]
12
11
  Validator = typing.Callable[[typing.Any], typing.Any]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: marshmallow
3
- Version: 3.21.2
3
+ Version: 3.22.0
4
4
  Summary: A lightweight library for converting complex datatypes to and from native Python datatypes.
5
5
  Author-email: Steven Loria <sloria1@gmail.com>
6
6
  Maintainer-email: Steven Loria <sloria1@gmail.com>, Jérôme Lafréchoux <jerome@jolimont.fr>, Jared Deckard <jared@shademaps.com>
@@ -19,11 +19,11 @@ Requires-Dist: packaging>=17.0
19
19
  Requires-Dist: marshmallow[tests] ; extra == "dev"
20
20
  Requires-Dist: tox ; extra == "dev"
21
21
  Requires-Dist: pre-commit~=3.5 ; extra == "dev"
22
- Requires-Dist: sphinx==7.3.7 ; extra == "docs"
22
+ Requires-Dist: sphinx==8.0.2 ; extra == "docs"
23
23
  Requires-Dist: sphinx-issues==4.1.0 ; extra == "docs"
24
- Requires-Dist: alabaster==0.7.16 ; extra == "docs"
24
+ Requires-Dist: alabaster==1.0.0 ; extra == "docs"
25
25
  Requires-Dist: sphinx-version-warning==1.1.2 ; extra == "docs"
26
- Requires-Dist: autodocsumm==0.2.12 ; extra == "docs"
26
+ Requires-Dist: autodocsumm==0.2.13 ; extra == "docs"
27
27
  Requires-Dist: pytest ; extra == "tests"
28
28
  Requires-Dist: pytz ; extra == "tests"
29
29
  Requires-Dist: simplejson ; extra == "tests"
@@ -40,19 +40,21 @@ Provides-Extra: tests
40
40
  marshmallow: simplified object serialization
41
41
  ********************************************
42
42
 
43
- .. image:: https://badgen.net/pypi/v/marshmallow
43
+ |pypi| |build-status| |pre-commit| |docs|
44
+
45
+ .. |pypi| image:: https://badgen.net/pypi/v/marshmallow
44
46
  :target: https://pypi.org/project/marshmallow/
45
47
  :alt: Latest version
46
48
 
47
- .. image:: https://github.com/marshmallow-code/marshmallow/actions/workflows/build-release.yml/badge.svg
49
+ .. |build-status| image:: https://github.com/marshmallow-code/marshmallow/actions/workflows/build-release.yml/badge.svg
48
50
  :target: https://github.com/marshmallow-code/marshmallow/actions/workflows/build-release.yml
49
51
  :alt: Build status
50
52
 
51
- .. image:: https://results.pre-commit.ci/badge/github/marshmallow-code/marshmallow/dev.svg
53
+ .. |pre-commit| image:: https://results.pre-commit.ci/badge/github/marshmallow-code/marshmallow/dev.svg
52
54
  :target: https://results.pre-commit.ci/latest/github/marshmallow-code/marshmallow/dev
53
55
  :alt: pre-commit.ci status
54
56
 
55
- .. image:: https://readthedocs.org/projects/marshmallow/badge/
57
+ .. |docs| image:: https://readthedocs.org/projects/marshmallow/badge/
56
58
  :target: https://marshmallow.readthedocs.io/
57
59
  :alt: Documentation
58
60
 
@@ -152,15 +154,16 @@ Thank you to all our backers! [`Become a backer`_]
152
154
  Sponsors
153
155
  --------
154
156
 
155
- Support this project by becoming a sponsor (or ask your company to support this project by becoming a sponsor).
156
- Your logo will show up here with a link to your website. [`Become a sponsor`_]
157
+ marshmallow is sponsored by `Route4Me <https://route4me.com>`_.
157
158
 
158
- .. _`Become a sponsor`: https://opencollective.com/marshmallow#sponsor
159
+ .. image:: https://github.com/user-attachments/assets/018c2e23-032e-4a11-98da-8b6dc25b9054
160
+ :target: https://route4me.com
161
+ :alt: Routing Planner
159
162
 
160
- .. image:: https://opencollective.com/static/images/become_sponsor.svg
161
- :target: https://opencollective.com/marshmallow#sponsor
162
- :alt: Become a sponsor
163
+ Support this project by becoming a sponsor (or ask your company to support this project by becoming a sponsor).
164
+ Your logo will be displayed here with a link to your website. [`Become a sponsor`_]
163
165
 
166
+ .. _`Become a sponsor`: https://opencollective.com/marshmallow#sponsor
164
167
 
165
168
  Professional Support
166
169
  ====================
@@ -1,18 +1,18 @@
1
1
  marshmallow/__init__.py,sha256=C-zbaQJ9dlJLJxotIqTa5OOaD6ojGNRqW8moGrMsGr8,2387
2
2
  marshmallow/base.py,sha256=jZ68DZxxSCvRg2GTcxQcf2JjTxqEn-xFNrBEMK3CinU,1346
3
3
  marshmallow/class_registry.py,sha256=Uvg-Obos0MejwTrpOHNEH4VF_SfGXKgR-4F2LnCbt1A,2811
4
- marshmallow/decorators.py,sha256=aa0tmqYW7-EJSae0ztZU9fHiQ3YIBdvIgaCrl1ChLNA,8300
4
+ marshmallow/decorators.py,sha256=vmQFgBgdV0s1Fw8ySyZyQKvjKBTNf5JB3SCldEIl29o,8385
5
5
  marshmallow/error_store.py,sha256=A7AxgLMw9ffSmaxRH4x3wcBWibx-DuGH4LwSDpVn50I,2223
6
6
  marshmallow/exceptions.py,sha256=DuARdOcirCdJxmlp16V97hQKAXOokvdW12jXtYOlGyk,2326
7
7
  marshmallow/fields.py,sha256=3mWgT1TiLzKgdhgWTJYsGe6lVgUz02g8sb63FEEn5hc,72995
8
8
  marshmallow/orderedset.py,sha256=C2aAG6w1faIL1phinbAltbe3AUAnF5MN6n7fzESNDhI,2922
9
9
  marshmallow/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- marshmallow/schema.py,sha256=lcaRjOkHph5NwokwIlJOuQmP_96FIwO6TzwSBd3c_98,49057
11
- marshmallow/types.py,sha256=eHMwQR8-ICX2RHf_i6bgjnhzdanbpBqXuzXuP6jHcNI,332
10
+ marshmallow/schema.py,sha256=l71HiKwPPfwIMQxXkr4oUoKybdT_vImoLkuj6ZTnUyg,49044
11
+ marshmallow/types.py,sha256=RDS4IfasIehvH2rGWh9e4RTBtsMp-JFFtjApajV22zc,283
12
12
  marshmallow/utils.py,sha256=sWciesZ6tS08uX9Z9fzu2lbuut5eh8TKABU-TwgqSms,11886
13
13
  marshmallow/validate.py,sha256=hS7fYC6byDHK9A7A4is0McDMZEzu6GkKke-7unLt2hE,23857
14
14
  marshmallow/warnings.py,sha256=vHQu7AluuWqLhvlw5noXtWWbya13zDXY6JMaVSUzmDs,65
15
- marshmallow-3.21.2.dist-info/LICENSE,sha256=kGtdkFHkJhRMsXOtkRZnuOvQWpxYTCwmwTWzKj7RIAE,1064
16
- marshmallow-3.21.2.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
17
- marshmallow-3.21.2.dist-info/METADATA,sha256=C7GEuX08QNTfpUvjjiD3GpZtWcPf4kHOJmMYNe6IoW0,7079
18
- marshmallow-3.21.2.dist-info/RECORD,,
15
+ marshmallow-3.22.0.dist-info/LICENSE,sha256=kGtdkFHkJhRMsXOtkRZnuOvQWpxYTCwmwTWzKj7RIAE,1064
16
+ marshmallow-3.22.0.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
17
+ marshmallow-3.22.0.dist-info/METADATA,sha256=7rCpX93_5nP-u7BEcVtaAa56-99xuwHcg-ALtEgm4-w,7225
18
+ marshmallow-3.22.0.dist-info/RECORD,,