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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
marshmallow/__init__.py CHANGED
@@ -1,10 +1,4 @@
1
- from __future__ import annotations
2
-
3
- import importlib.metadata
4
- import typing
5
-
6
- from packaging.version import Version
7
-
1
+ from marshmallow.constants import EXCLUDE, INCLUDE, RAISE, missing
8
2
  from marshmallow.decorators import (
9
3
  post_dump,
10
4
  post_load,
@@ -15,53 +9,9 @@ from marshmallow.decorators import (
15
9
  )
16
10
  from marshmallow.exceptions import ValidationError
17
11
  from marshmallow.schema import Schema, SchemaOpts
18
- from marshmallow.utils import EXCLUDE, INCLUDE, RAISE, missing, pprint
19
12
 
20
13
  from . import fields
21
14
 
22
-
23
- def __getattr__(name: str) -> typing.Any:
24
- import warnings
25
-
26
- if name == "__version__":
27
- warnings.warn(
28
- "The '__version__' attribute is deprecated and will be removed in"
29
- " in a future version. Use feature detection or"
30
- " 'importlib.metadata.version(\"marshmallow\")' instead.",
31
- DeprecationWarning,
32
- stacklevel=2,
33
- )
34
- return importlib.metadata.version("marshmallow")
35
-
36
- if name == "__parsed_version__":
37
- warnings.warn(
38
- "The '__parsed_version__' attribute is deprecated and will be removed in"
39
- " in a future version. Use feature detection or"
40
- " 'packaging.Version(importlib.metadata.version(\"marshmallow\"))' instead.",
41
- DeprecationWarning,
42
- stacklevel=2,
43
- )
44
- return Version(importlib.metadata.version("marshmallow"))
45
-
46
- if name == "__version_info__":
47
- warnings.warn(
48
- "The '__version_info__' attribute is deprecated and will be removed in"
49
- " in a future version. Use feature detection or"
50
- " 'packaging.Version(importlib.metadata.version(\"marshmallow\")).release' instead.",
51
- DeprecationWarning,
52
- stacklevel=2,
53
- )
54
- __parsed_version__ = Version(importlib.metadata.version("marshmallow"))
55
- __version_info__: tuple[int, int, int] | tuple[int, int, int, str, int] = (
56
- __parsed_version__.release # type: ignore[assignment]
57
- )
58
- if __parsed_version__.pre:
59
- __version_info__ += __parsed_version__.pre # type: ignore[assignment]
60
- return __version_info__
61
-
62
- raise AttributeError(name)
63
-
64
-
65
15
  __all__ = [
66
16
  "EXCLUDE",
67
17
  "INCLUDE",
@@ -73,7 +23,6 @@ __all__ = [
73
23
  "missing",
74
24
  "post_dump",
75
25
  "post_load",
76
- "pprint",
77
26
  "pre_dump",
78
27
  "pre_load",
79
28
  "validates",
@@ -0,0 +1,22 @@
1
+ import typing
2
+
3
+ EXCLUDE: typing.Final = "exclude"
4
+ INCLUDE: typing.Final = "include"
5
+ RAISE: typing.Final = "raise"
6
+
7
+
8
+ class _Missing:
9
+ def __bool__(self):
10
+ return False
11
+
12
+ def __copy__(self):
13
+ return self
14
+
15
+ def __deepcopy__(self, _):
16
+ return self
17
+
18
+ def __repr__(self):
19
+ return "<marshmallow.missing>"
20
+
21
+
22
+ missing: typing.Final = _Missing()
marshmallow/decorators.py CHANGED
@@ -35,12 +35,12 @@ Example: ::
35
35
  item["email"] = item["email"].lower().strip()
36
36
  return item
37
37
 
38
- @pre_load(pass_many=True)
38
+ @pre_load(pass_collection=True)
39
39
  def remove_envelope(self, data, many, **kwargs):
40
40
  namespace = "results" if many else "result"
41
41
  return data[namespace]
42
42
 
43
- @post_dump(pass_many=True)
43
+ @post_dump(pass_collection=True)
44
44
  def add_envelope(self, data, many, **kwargs):
45
45
  namespace = "results" if many else "result"
46
46
  return {namespace: data}
@@ -83,25 +83,29 @@ class MarshmallowHook:
83
83
  __marshmallow_hook__: dict[str, list[tuple[bool, Any]]] | None = None
84
84
 
85
85
 
86
- def validates(field_name: str) -> Callable[..., Any]:
87
- """Register a field validator.
86
+ def validates(*field_names: str) -> Callable[..., Any]:
87
+ """Register a validator method for field(s).
88
88
 
89
- :param field_name: Name of the field that the method validates.
89
+ :param field_names: Names of the fields that the method validates.
90
+
91
+ .. versionchanged:: 4.0.0 Accepts multiple field names as positional arguments.
92
+ .. versionchanged:: 4.0.0 Decorated methods receive ``data_key`` as a keyword argument.
90
93
  """
91
- return set_hook(None, VALIDATES, field_name=field_name)
94
+ return set_hook(None, VALIDATES, field_names=field_names)
92
95
 
93
96
 
94
97
  def validates_schema(
95
98
  fn: Callable[..., Any] | None = None,
96
- pass_many: bool = False, # noqa: FBT001, FBT002
97
- pass_original: bool = False, # noqa: FBT001, FBT002
98
- skip_on_field_errors: bool = True, # noqa: FBT001, FBT002
99
+ *,
100
+ pass_collection: bool = False,
101
+ pass_original: bool = False,
102
+ skip_on_field_errors: bool = True,
99
103
  ) -> Callable[..., Any]:
100
104
  """Register a schema-level validator.
101
105
 
102
106
  By default it receives a single object at a time, transparently handling the ``many``
103
107
  argument passed to the `Schema <marshmallow.Schema>`'s :func:`~marshmallow.Schema.validate` call.
104
- If ``pass_many=True``, the raw data (which may be a collection) is passed.
108
+ If ``pass_collection=True``, the raw data (which may be a collection) is passed.
105
109
 
106
110
  If ``pass_original=True``, the original data (before unmarshalling) will be passed as
107
111
  an additional argument to the method.
@@ -109,17 +113,18 @@ def validates_schema(
109
113
  If ``skip_on_field_errors=True``, this validation method will be skipped whenever
110
114
  validation errors have been detected when validating fields.
111
115
 
112
- .. versionchanged:: 3.0.0b1
113
- ``skip_on_field_errors`` defaults to `True`.
114
-
115
- .. versionchanged:: 3.0.0
116
- ``partial`` and ``many`` are always passed as keyword arguments to
116
+ .. versionchanged:: 3.0.0b1 ``skip_on_field_errors`` defaults to `True`.
117
+ .. versionchanged:: 3.0.0 ``partial`` and ``many`` are always passed as keyword arguments to
117
118
  the decorated method.
119
+ .. versionchanged:: 4.0.0 ``unknown`` is passed as a keyword argument to the decorated method.
120
+ .. versionchanged:: 4.0.0 ``pass_many`` is renamed to ``pass_collection``.
121
+ .. versionchanged:: 4.0.0 ``pass_collection``, ``pass_original``, and ``skip_on_field_errors``
122
+ are keyword-only arguments.
118
123
  """
119
124
  return set_hook(
120
125
  fn,
121
126
  VALIDATES_SCHEMA,
122
- many=pass_many,
127
+ many=pass_collection,
123
128
  pass_original=pass_original,
124
129
  skip_on_field_errors=skip_on_field_errors,
125
130
  )
@@ -127,86 +132,97 @@ def validates_schema(
127
132
 
128
133
  def pre_dump(
129
134
  fn: Callable[..., Any] | None = None,
130
- pass_many: bool = False, # noqa: FBT001, FBT002
135
+ *,
136
+ pass_collection: bool = False,
131
137
  ) -> Callable[..., Any]:
132
138
  """Register a method to invoke before serializing an object. The method
133
139
  receives the object to be serialized and returns the processed object.
134
140
 
135
141
  By default it receives a single object at a time, transparently handling the ``many``
136
142
  argument passed to the `Schema <marshmallow.Schema>`'s :func:`~marshmallow.Schema.dump` call.
137
- If ``pass_many=True``, the raw data (which may be a collection) is passed.
143
+ If ``pass_collection=True``, the raw data (which may be a collection) is passed.
138
144
 
139
- .. versionchanged:: 3.0.0
140
- ``many`` is always passed as a keyword arguments to the decorated method.
145
+ .. versionchanged:: 3.0.0 ``many`` is always passed as a keyword arguments to the decorated method.
146
+ .. versionchanged:: 4.0.0 ``pass_many`` is renamed to ``pass_collection``.
147
+ .. versionchanged:: 4.0.0 ``pass_collection`` is a keyword-only argument.
141
148
  """
142
- return set_hook(fn, PRE_DUMP, many=pass_many)
149
+ return set_hook(fn, PRE_DUMP, many=pass_collection)
143
150
 
144
151
 
145
152
  def post_dump(
146
153
  fn: Callable[..., Any] | None = None,
147
- pass_many: bool = False, # noqa: FBT001, FBT002
148
- pass_original: bool = False, # noqa: FBT001, FBT002
154
+ *,
155
+ pass_collection: bool = False,
156
+ pass_original: bool = False,
149
157
  ) -> Callable[..., Any]:
150
158
  """Register a method to invoke after serializing an object. The method
151
159
  receives the serialized object and returns the processed object.
152
160
 
153
161
  By default it receives a single object at a time, transparently handling the ``many``
154
162
  argument passed to the `Schema <marshmallow.Schema>`'s :func:`~marshmallow.Schema.dump` call.
155
- If ``pass_many=True``, the raw data (which may be a collection) is passed.
163
+ If ``pass_collection=True``, the raw data (which may be a collection) is passed.
156
164
 
157
165
  If ``pass_original=True``, the original data (before serializing) will be passed as
158
166
  an additional argument to the method.
159
167
 
160
- .. versionchanged:: 3.0.0
161
- ``many`` is always passed as a keyword arguments to the decorated method.
168
+ .. versionchanged:: 3.0.0 ``many`` is always passed as a keyword arguments to the decorated method.
169
+ .. versionchanged:: 4.0.0 ``pass_many`` is renamed to ``pass_collection``.
170
+ .. versionchanged:: 4.0.0 ``pass_collection`` and ``pass_original`` are keyword-only arguments.
162
171
  """
163
- return set_hook(fn, POST_DUMP, many=pass_many, pass_original=pass_original)
172
+ return set_hook(fn, POST_DUMP, many=pass_collection, pass_original=pass_original)
164
173
 
165
174
 
166
175
  def pre_load(
167
176
  fn: Callable[..., Any] | None = None,
168
- pass_many: bool = False, # noqa: FBT001, FBT002
177
+ *,
178
+ pass_collection: bool = False,
169
179
  ) -> Callable[..., Any]:
170
180
  """Register a method to invoke before deserializing an object. The method
171
181
  receives the data to be deserialized and returns the processed data.
172
182
 
173
183
  By default it receives a single object at a time, transparently handling the ``many``
174
184
  argument passed to the `Schema <marshmallow.Schema>`'s :func:`~marshmallow.Schema.load` call.
175
- If ``pass_many=True``, the raw data (which may be a collection) is passed.
185
+ If ``pass_collection=True``, the raw data (which may be a collection) is passed.
176
186
 
177
- .. versionchanged:: 3.0.0
178
- ``partial`` and ``many`` are always passed as keyword arguments to
187
+ .. versionchanged:: 3.0.0 ``partial`` and ``many`` are always passed as keyword arguments to
179
188
  the decorated method.
189
+ .. versionchanged:: 4.0.0 ``pass_many`` is renamed to ``pass_collection``.
190
+ .. versionchanged:: 4.0.0 ``pass_collection`` is a keyword-only argument.
191
+ .. versionchanged:: 4.0.0 ``unknown`` is passed as a keyword argument to the decorated method.
180
192
  """
181
- return set_hook(fn, PRE_LOAD, many=pass_many)
193
+ return set_hook(fn, PRE_LOAD, many=pass_collection)
182
194
 
183
195
 
184
196
  def post_load(
185
197
  fn: Callable[..., Any] | None = None,
186
- pass_many: bool = False, # noqa: FBT001, FBT002
187
- pass_original: bool = False, # noqa: FBT001, FBT002
198
+ *,
199
+ pass_collection: bool = False,
200
+ pass_original: bool = False,
188
201
  ) -> Callable[..., Any]:
189
202
  """Register a method to invoke after deserializing an object. The method
190
203
  receives the deserialized data and returns the processed data.
191
204
 
192
205
  By default it receives a single object at a time, transparently handling the ``many``
193
206
  argument passed to the `Schema <marshmallow.Schema>`'s :func:`~marshmallow.Schema.load` call.
194
- If ``pass_many=True``, the raw data (which may be a collection) is passed.
207
+ If ``pass_collection=True``, the raw data (which may be a collection) is passed.
195
208
 
196
209
  If ``pass_original=True``, the original data (before deserializing) will be passed as
197
210
  an additional argument to the method.
198
211
 
199
- .. versionchanged:: 3.0.0
200
- ``partial`` and ``many`` are always passed as keyword arguments to
212
+ .. versionchanged:: 3.0.0 ``partial`` and ``many`` are always passed as keyword arguments to
201
213
  the decorated method.
214
+ .. versionchanged:: 4.0.0 ``pass_many`` is renamed to ``pass_collection``.
215
+ .. versionchanged:: 4.0.0 ``pass_collection`` and ``pass_original`` are keyword-only arguments.
216
+ .. versionchanged:: 4.0.0 ``unknown`` is passed as a keyword argument to the decorated method.
202
217
  """
203
- return set_hook(fn, POST_LOAD, many=pass_many, pass_original=pass_original)
218
+ return set_hook(fn, POST_LOAD, many=pass_collection, pass_original=pass_original)
204
219
 
205
220
 
206
221
  def set_hook(
207
222
  fn: Callable[..., Any] | None,
208
223
  tag: str,
209
- many: bool = False, # noqa: FBT001, FBT002
224
+ *,
225
+ many: bool = False,
210
226
  **kwargs: Any,
211
227
  ) -> Callable[..., Any]:
212
228
  """Mark decorated function as a hook to be picked up later.
@@ -225,7 +241,7 @@ def set_hook(
225
241
 
226
242
  # Set a __marshmallow_hook__ attribute instead of wrapping in some class,
227
243
  # because I still want this to end up as a normal (unbound) method.
228
- function = cast(MarshmallowHook, fn)
244
+ function = cast("MarshmallowHook", fn)
229
245
  try:
230
246
  hook_config = function.__marshmallow_hook__
231
247
  except AttributeError:
marshmallow/exceptions.py CHANGED
@@ -20,7 +20,6 @@ class ValidationError(MarshmallowError):
20
20
  :param message: An error message, list of error messages, or dict of
21
21
  error messages. If a dict, the keys are subitems and the values are error messages.
22
22
  :param field_name: Field name to store the error on.
23
- If `None`, the error is stored as schema-level error.
24
23
  :param data: Raw input data.
25
24
  :param valid_data: Valid (de)serialized data.
26
25
  """
@@ -32,7 +31,7 @@ class ValidationError(MarshmallowError):
32
31
  data: typing.Mapping[str, typing.Any]
33
32
  | typing.Iterable[typing.Mapping[str, typing.Any]]
34
33
  | None = None,
35
- valid_data: list[dict[str, typing.Any]] | dict[str, typing.Any] | None = None,
34
+ valid_data: list[typing.Any] | dict[str, typing.Any] | None = None,
36
35
  **kwargs,
37
36
  ):
38
37
  self.messages = [message] if isinstance(message, (str, bytes)) else message
@@ -67,5 +66,5 @@ class StringNotCollectionError(MarshmallowError, TypeError):
67
66
  """Raised when a string is passed when a list of strings is expected."""
68
67
 
69
68
 
70
- class FieldInstanceResolutionError(MarshmallowError, TypeError):
71
- """Raised when schema to instantiate is neither a Schema class nor an instance."""
69
+ class _FieldInstanceResolutionError(MarshmallowError, TypeError):
70
+ """Raised when an argument is passed to a field class that cannot be resolved to a Field instance."""
@@ -0,0 +1,5 @@
1
+ """Experimental features.
2
+
3
+ The features in this subpackage are experimental. Breaking changes may be
4
+ introduced in minor marshmallow versions.
5
+ """
@@ -0,0 +1,73 @@
1
+ """Helper API for setting serialization/deserialization context.
2
+
3
+ Example usage:
4
+
5
+ .. code-block:: python
6
+
7
+ import typing
8
+
9
+ from marshmallow import Schema, fields
10
+ from marshmallow.experimental.context import Context
11
+
12
+
13
+ class UserContext(typing.TypedDict):
14
+ suffix: str
15
+
16
+
17
+ UserSchemaContext = Context[UserContext]
18
+
19
+
20
+ class UserSchema(Schema):
21
+ name_suffixed = fields.Function(
22
+ lambda user: user["name"] + UserSchemaContext.get()["suffix"]
23
+ )
24
+
25
+
26
+ with UserSchemaContext({"suffix": "bar"}):
27
+ print(UserSchema().dump({"name": "foo"}))
28
+ # {'name_suffixed': 'foobar'}
29
+ """
30
+
31
+ from __future__ import annotations
32
+
33
+ import contextlib
34
+ import contextvars
35
+ import typing
36
+
37
+ try:
38
+ from types import EllipsisType
39
+ except ImportError: # Python<3.10
40
+ EllipsisType = type(Ellipsis) # type: ignore[misc]
41
+
42
+ _ContextT = typing.TypeVar("_ContextT")
43
+ _DefaultT = typing.TypeVar("_DefaultT")
44
+ _CURRENT_CONTEXT: contextvars.ContextVar = contextvars.ContextVar("context")
45
+
46
+
47
+ class Context(contextlib.AbstractContextManager, typing.Generic[_ContextT]):
48
+ """Context manager for setting and retrieving context.
49
+
50
+ :param context: The context to use within the context manager scope.
51
+ """
52
+
53
+ def __init__(self, context: _ContextT) -> None:
54
+ self.context = context
55
+ self.token: contextvars.Token | None = None
56
+
57
+ def __enter__(self) -> Context[_ContextT]:
58
+ self.token = _CURRENT_CONTEXT.set(self.context)
59
+ return self
60
+
61
+ def __exit__(self, *args, **kwargs) -> None:
62
+ _CURRENT_CONTEXT.reset(typing.cast("contextvars.Token", self.token))
63
+
64
+ @classmethod
65
+ def get(cls, default: _DefaultT | EllipsisType = ...) -> _ContextT | _DefaultT:
66
+ """Get the current context.
67
+
68
+ :param default: Default value to return if no context is set.
69
+ If not provided and no context is set, a :exc:`LookupError` is raised.
70
+ """
71
+ if default is not ...:
72
+ return _CURRENT_CONTEXT.get(default)
73
+ return _CURRENT_CONTEXT.get()