marshmallow 4.0.1__tar.gz → 4.1.1__tar.gz
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-4.0.1 → marshmallow-4.1.1}/CHANGELOG.rst +19 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/PKG-INFO +7 -7
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/code_of_conduct.rst +3 -3
- {marshmallow-4.0.1 → marshmallow-4.1.1}/pyproject.toml +8 -7
- {marshmallow-4.0.1 → marshmallow-4.1.1}/src/marshmallow/constants.py +3 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/src/marshmallow/decorators.py +17 -17
- {marshmallow-4.0.1 → marshmallow-4.1.1}/src/marshmallow/fields.py +5 -7
- {marshmallow-4.0.1 → marshmallow-4.1.1}/src/marshmallow/schema.py +3 -1
- {marshmallow-4.0.1 → marshmallow-4.1.1}/src/marshmallow/types.py +3 -10
- {marshmallow-4.0.1 → marshmallow-4.1.1}/src/marshmallow/utils.py +4 -10
- {marshmallow-4.0.1 → marshmallow-4.1.1}/src/marshmallow/validate.py +2 -3
- {marshmallow-4.0.1 → marshmallow-4.1.1}/tests/test_decorators.py +2 -2
- {marshmallow-4.0.1 → marshmallow-4.1.1}/tests/test_deserialization.py +12 -2
- {marshmallow-4.0.1 → marshmallow-4.1.1}/tests/test_schema.py +1 -1
- {marshmallow-4.0.1 → marshmallow-4.1.1}/tests/test_utils.py +1 -1
- {marshmallow-4.0.1 → marshmallow-4.1.1}/tests/test_validate.py +8 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/tox.ini +1 -1
- {marshmallow-4.0.1 → marshmallow-4.1.1}/CONTRIBUTING.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/LICENSE +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/NOTICE +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/README.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/SECURITY.md +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/.gitignore +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/_static/apple-touch-icon.png +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/_static/custom.css +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/_static/favicon.ico +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/_static/marshmallow-logo-200.png +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/_static/marshmallow-logo-with-title-for-dark-theme.png +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/_static/marshmallow-logo-with-title.png +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/_static/marshmallow-logo.png +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/api_reference.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/authors.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/changelog.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/conf.py +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/contributing.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/custom_fields.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/dashing.json +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/donate.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/examples/index.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/examples/inflection.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/examples/quotes_api.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/examples/validating_package_json.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/extending/custom_error_handling.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/extending/custom_error_messages.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/extending/custom_options.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/extending/index.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/extending/overriding_attribute_access.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/extending/pre_and_post_processing_methods.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/extending/schema_validation.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/extending/using_original_input_data.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/index.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/install.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/kudos.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/license.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/marshmallow.class_registry.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/marshmallow.decorators.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/marshmallow.error_store.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/marshmallow.exceptions.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/marshmallow.experimental.context.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/marshmallow.fields.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/marshmallow.schema.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/marshmallow.types.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/marshmallow.utils.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/marshmallow.validate.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/nesting.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/quickstart.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/top_level.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/upgrading.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/whos_using.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/docs/why.rst +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/src/marshmallow/__init__.py +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/src/marshmallow/class_registry.py +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/src/marshmallow/error_store.py +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/src/marshmallow/exceptions.py +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/src/marshmallow/experimental/__init__.py +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/src/marshmallow/experimental/context.py +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/src/marshmallow/orderedset.py +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/src/marshmallow/py.typed +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/tests/__init__.py +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/tests/base.py +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/tests/conftest.py +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/tests/foo_serializer.py +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/tests/mypy_test_cases/test_class_registry.py +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/tests/mypy_test_cases/test_schema.py +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/tests/mypy_test_cases/test_validation_error.py +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/tests/test_context.py +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/tests/test_error_store.py +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/tests/test_exceptions.py +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/tests/test_fields.py +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/tests/test_options.py +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/tests/test_registry.py +0 -0
- {marshmallow-4.0.1 → marshmallow-4.1.1}/tests/test_serialization.py +0 -0
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
Changelog
|
|
2
2
|
---------
|
|
3
3
|
|
|
4
|
+
4.1.1 (2025-11-05)
|
|
5
|
+
++++++++++++++++++
|
|
6
|
+
|
|
7
|
+
Bug fixes:
|
|
8
|
+
|
|
9
|
+
- Ensure ``URL`` validator is case-insensitive when using custom schemes (:pr:`2874`).
|
|
10
|
+
Thanks :user:`T90REAL` for the PR.
|
|
11
|
+
|
|
12
|
+
4.1.0 (2025-11-01)
|
|
13
|
+
++++++++++++++++++
|
|
14
|
+
|
|
15
|
+
Other changes:
|
|
16
|
+
|
|
17
|
+
- Add `__len__` implementation to `missing` so that it can be used with
|
|
18
|
+
`validate.Length <marshmallow.validate.Length>` (:pr:`2861`).
|
|
19
|
+
Thanks :user:`agentgodzilla` for the PR.
|
|
20
|
+
- Drop support for Python 3.9 (:pr:`2363`).
|
|
21
|
+
- Test against Python 3.14 (:pr:`2864`).
|
|
22
|
+
|
|
4
23
|
4.0.1 (2025-08-28)
|
|
5
24
|
++++++++++++++++++
|
|
6
25
|
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: marshmallow
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.1.1
|
|
4
4
|
Summary: A lightweight library for converting complex datatypes to and from native Python datatypes.
|
|
5
|
-
Author-email: Steven Loria <
|
|
6
|
-
Maintainer-email: Steven Loria <
|
|
7
|
-
Requires-Python: >=3.
|
|
5
|
+
Author-email: Steven Loria <oss@stevenloria.com>
|
|
6
|
+
Maintainer-email: Steven Loria <oss@stevenloria.com>, Jérôme Lafréchoux <jerome@jolimont.fr>, Jared Deckard <jared@shademaps.com>
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
8
|
Description-Content-Type: text/x-rst
|
|
9
9
|
Classifier: Development Status :: 5 - Production/Stable
|
|
10
10
|
Classifier: Intended Audience :: Developers
|
|
11
11
|
Classifier: License :: OSI Approved :: MIT License
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.10
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
18
|
License-File: LICENSE
|
|
19
19
|
Requires-Dist: backports-datetime-fromisoformat; python_version < '3.11'
|
|
20
20
|
Requires-Dist: typing-extensions; python_version < '3.11'
|
|
@@ -22,11 +22,11 @@ Requires-Dist: marshmallow[tests] ; extra == "dev"
|
|
|
22
22
|
Requires-Dist: tox ; extra == "dev"
|
|
23
23
|
Requires-Dist: pre-commit>=3.5,<5.0 ; extra == "dev"
|
|
24
24
|
Requires-Dist: autodocsumm==0.2.14 ; extra == "docs"
|
|
25
|
-
Requires-Dist: furo==2025.
|
|
25
|
+
Requires-Dist: furo==2025.9.25 ; extra == "docs"
|
|
26
26
|
Requires-Dist: sphinx-copybutton==0.5.2 ; extra == "docs"
|
|
27
27
|
Requires-Dist: sphinx-issues==5.0.1 ; extra == "docs"
|
|
28
28
|
Requires-Dist: sphinx==8.2.3 ; extra == "docs"
|
|
29
|
-
Requires-Dist: sphinxext-opengraph==0.
|
|
29
|
+
Requires-Dist: sphinxext-opengraph==0.13.0 ; extra == "docs"
|
|
30
30
|
Requires-Dist: pytest ; extra == "tests"
|
|
31
31
|
Requires-Dist: simplejson ; extra == "tests"
|
|
32
32
|
Project-URL: Changelog, https://marshmallow.readthedocs.io/en/latest/changelog.html
|
|
@@ -136,10 +136,10 @@ members. This section covers actual concrete steps.
|
|
|
136
136
|
Contacting maintainers
|
|
137
137
|
~~~~~~~~~~~~~~~~~~~~~~
|
|
138
138
|
|
|
139
|
-
As a small
|
|
139
|
+
As a small project, we don't yet have a Code of Conduct
|
|
140
140
|
enforcement team. Hopefully that will be addressed as we grow, but for
|
|
141
|
-
now, any issues should be addressed to
|
|
142
|
-
<https://github.com/sloria>`__, via `email <mailto:
|
|
141
|
+
now, any issues should be addressed to `@sloria
|
|
142
|
+
<https://github.com/sloria>`__, via `email <mailto:oss@stevenloria.com>`__
|
|
143
143
|
or any other medium that you feel comfortable with. Using words like
|
|
144
144
|
"marshmallow code of conduct" in your subject will help make sure your
|
|
145
145
|
message is noticed quickly.
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "marshmallow"
|
|
3
|
-
version = "4.
|
|
3
|
+
version = "4.1.1"
|
|
4
4
|
description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
|
|
5
5
|
readme = "README.rst"
|
|
6
6
|
license = { file = "LICENSE" }
|
|
7
|
-
authors = [{ name = "Steven Loria", email = "
|
|
7
|
+
authors = [{ name = "Steven Loria", email = "oss@stevenloria.com" }]
|
|
8
8
|
maintainers = [
|
|
9
|
-
{ name = "Steven Loria", email = "
|
|
9
|
+
{ name = "Steven Loria", email = "oss@stevenloria.com" },
|
|
10
10
|
{ name = "Jérôme Lafréchoux", email = "jerome@jolimont.fr" },
|
|
11
11
|
{ name = "Jared Deckard", email = "jared@shademaps.com" },
|
|
12
12
|
]
|
|
@@ -15,13 +15,13 @@ classifiers = [
|
|
|
15
15
|
"Intended Audience :: Developers",
|
|
16
16
|
"License :: OSI Approved :: MIT License",
|
|
17
17
|
"Programming Language :: Python :: 3",
|
|
18
|
-
"Programming Language :: Python :: 3.9",
|
|
19
18
|
"Programming Language :: Python :: 3.10",
|
|
20
19
|
"Programming Language :: Python :: 3.11",
|
|
21
20
|
"Programming Language :: Python :: 3.12",
|
|
22
21
|
"Programming Language :: Python :: 3.13",
|
|
22
|
+
"Programming Language :: Python :: 3.14",
|
|
23
23
|
]
|
|
24
|
-
requires-python = ">=3.
|
|
24
|
+
requires-python = ">=3.10"
|
|
25
25
|
dependencies = [
|
|
26
26
|
"backports-datetime-fromisoformat; python_version < '3.11'",
|
|
27
27
|
"typing-extensions; python_version < '3.11'",
|
|
@@ -37,11 +37,11 @@ Tidelift = "https://tidelift.com/subscription/pkg/pypi-marshmallow?utm_source=py
|
|
|
37
37
|
[project.optional-dependencies]
|
|
38
38
|
docs = [
|
|
39
39
|
"autodocsumm==0.2.14",
|
|
40
|
-
"furo==2025.
|
|
40
|
+
"furo==2025.9.25",
|
|
41
41
|
"sphinx-copybutton==0.5.2",
|
|
42
42
|
"sphinx-issues==5.0.1",
|
|
43
43
|
"sphinx==8.2.3",
|
|
44
|
-
"sphinxext-opengraph==0.
|
|
44
|
+
"sphinxext-opengraph==0.13.0",
|
|
45
45
|
]
|
|
46
46
|
tests = ["pytest", "simplejson"]
|
|
47
47
|
dev = ["marshmallow[tests]", "tox", "pre-commit>=3.5,<5.0"]
|
|
@@ -108,6 +108,7 @@ ignore = [
|
|
|
108
108
|
"PLR0915", # allow lots of statements
|
|
109
109
|
"PT007", # ignore false positives due to https://github.com/astral-sh/ruff/issues/14743
|
|
110
110
|
"PT011", # don't require match when using pytest.raises
|
|
111
|
+
"RUF043", # allow metacharacters in match patterns
|
|
111
112
|
"S", # allow asserts
|
|
112
113
|
"SIM117", # allow nested with statements because it's more readable sometimes
|
|
113
114
|
"SLF001", # allow private attribute access
|
|
@@ -68,8 +68,8 @@ Example: ::
|
|
|
68
68
|
from __future__ import annotations
|
|
69
69
|
|
|
70
70
|
import functools
|
|
71
|
+
import typing
|
|
71
72
|
from collections import defaultdict
|
|
72
|
-
from typing import Any, Callable, cast
|
|
73
73
|
|
|
74
74
|
PRE_DUMP = "pre_dump"
|
|
75
75
|
POST_DUMP = "post_dump"
|
|
@@ -80,10 +80,10 @@ VALIDATES_SCHEMA = "validates_schema"
|
|
|
80
80
|
|
|
81
81
|
|
|
82
82
|
class MarshmallowHook:
|
|
83
|
-
__marshmallow_hook__: dict[str, list[tuple[bool, Any]]] | None = None
|
|
83
|
+
__marshmallow_hook__: dict[str, list[tuple[bool, typing.Any]]] | None = None
|
|
84
84
|
|
|
85
85
|
|
|
86
|
-
def validates(*field_names: str) -> Callable[..., Any]:
|
|
86
|
+
def validates(*field_names: str) -> typing.Callable[..., typing.Any]:
|
|
87
87
|
"""Register a validator method for field(s).
|
|
88
88
|
|
|
89
89
|
:param field_names: Names of the fields that the method validates.
|
|
@@ -95,12 +95,12 @@ def validates(*field_names: str) -> Callable[..., Any]:
|
|
|
95
95
|
|
|
96
96
|
|
|
97
97
|
def validates_schema(
|
|
98
|
-
fn: Callable[..., Any] | None = None,
|
|
98
|
+
fn: typing.Callable[..., typing.Any] | None = None,
|
|
99
99
|
*,
|
|
100
100
|
pass_collection: bool = False,
|
|
101
101
|
pass_original: bool = False,
|
|
102
102
|
skip_on_field_errors: bool = True,
|
|
103
|
-
) -> Callable[..., Any]:
|
|
103
|
+
) -> typing.Callable[..., typing.Any]:
|
|
104
104
|
"""Register a schema-level validator.
|
|
105
105
|
|
|
106
106
|
By default it receives a single object at a time, transparently handling the ``many``
|
|
@@ -131,10 +131,10 @@ def validates_schema(
|
|
|
131
131
|
|
|
132
132
|
|
|
133
133
|
def pre_dump(
|
|
134
|
-
fn: Callable[..., Any] | None = None,
|
|
134
|
+
fn: typing.Callable[..., typing.Any] | None = None,
|
|
135
135
|
*,
|
|
136
136
|
pass_collection: bool = False,
|
|
137
|
-
) -> Callable[..., Any]:
|
|
137
|
+
) -> typing.Callable[..., typing.Any]:
|
|
138
138
|
"""Register a method to invoke before serializing an object. The method
|
|
139
139
|
receives the object to be serialized and returns the processed object.
|
|
140
140
|
|
|
@@ -150,11 +150,11 @@ def pre_dump(
|
|
|
150
150
|
|
|
151
151
|
|
|
152
152
|
def post_dump(
|
|
153
|
-
fn: Callable[..., Any] | None = None,
|
|
153
|
+
fn: typing.Callable[..., typing.Any] | None = None,
|
|
154
154
|
*,
|
|
155
155
|
pass_collection: bool = False,
|
|
156
156
|
pass_original: bool = False,
|
|
157
|
-
) -> Callable[..., Any]:
|
|
157
|
+
) -> typing.Callable[..., typing.Any]:
|
|
158
158
|
"""Register a method to invoke after serializing an object. The method
|
|
159
159
|
receives the serialized object and returns the processed object.
|
|
160
160
|
|
|
@@ -173,10 +173,10 @@ def post_dump(
|
|
|
173
173
|
|
|
174
174
|
|
|
175
175
|
def pre_load(
|
|
176
|
-
fn: Callable[..., Any] | None = None,
|
|
176
|
+
fn: typing.Callable[..., typing.Any] | None = None,
|
|
177
177
|
*,
|
|
178
178
|
pass_collection: bool = False,
|
|
179
|
-
) -> Callable[..., Any]:
|
|
179
|
+
) -> typing.Callable[..., typing.Any]:
|
|
180
180
|
"""Register a method to invoke before deserializing an object. The method
|
|
181
181
|
receives the data to be deserialized and returns the processed data.
|
|
182
182
|
|
|
@@ -194,11 +194,11 @@ def pre_load(
|
|
|
194
194
|
|
|
195
195
|
|
|
196
196
|
def post_load(
|
|
197
|
-
fn: Callable[..., Any] | None = None,
|
|
197
|
+
fn: typing.Callable[..., typing.Any] | None = None,
|
|
198
198
|
*,
|
|
199
199
|
pass_collection: bool = False,
|
|
200
200
|
pass_original: bool = False,
|
|
201
|
-
) -> Callable[..., Any]:
|
|
201
|
+
) -> typing.Callable[..., typing.Any]:
|
|
202
202
|
"""Register a method to invoke after deserializing an object. The method
|
|
203
203
|
receives the deserialized data and returns the processed data.
|
|
204
204
|
|
|
@@ -219,12 +219,12 @@ def post_load(
|
|
|
219
219
|
|
|
220
220
|
|
|
221
221
|
def set_hook(
|
|
222
|
-
fn: Callable[..., Any] | None,
|
|
222
|
+
fn: typing.Callable[..., typing.Any] | None,
|
|
223
223
|
tag: str,
|
|
224
224
|
*,
|
|
225
225
|
many: bool = False,
|
|
226
|
-
**kwargs: Any,
|
|
227
|
-
) -> Callable[..., Any]:
|
|
226
|
+
**kwargs: typing.Any,
|
|
227
|
+
) -> typing.Callable[..., typing.Any]:
|
|
228
228
|
"""Mark decorated function as a hook to be picked up later.
|
|
229
229
|
You should not need to use this method directly.
|
|
230
230
|
|
|
@@ -241,7 +241,7 @@ def set_hook(
|
|
|
241
241
|
|
|
242
242
|
# Set a __marshmallow_hook__ attribute instead of wrapping in some class,
|
|
243
243
|
# because I still want this to end up as a normal (unbound) method.
|
|
244
|
-
function = cast("MarshmallowHook", fn)
|
|
244
|
+
function = typing.cast("MarshmallowHook", fn)
|
|
245
245
|
try:
|
|
246
246
|
hook_config = function.__marshmallow_hook__
|
|
247
247
|
except AttributeError:
|
|
@@ -685,7 +685,7 @@ class Pluck(Nested):
|
|
|
685
685
|
return self._load(value, partial=partial)
|
|
686
686
|
|
|
687
687
|
|
|
688
|
-
class List(Field[list[
|
|
688
|
+
class List(Field[list[_InternalT | None]]):
|
|
689
689
|
"""A list field, composed with another `Field` class or
|
|
690
690
|
instance.
|
|
691
691
|
|
|
@@ -816,7 +816,7 @@ class Tuple(Field[tuple]):
|
|
|
816
816
|
|
|
817
817
|
return tuple(
|
|
818
818
|
field._serialize(each, attr, obj, **kwargs)
|
|
819
|
-
for field, each in zip(self.tuple_fields, value)
|
|
819
|
+
for field, each in zip(self.tuple_fields, value, strict=True)
|
|
820
820
|
)
|
|
821
821
|
|
|
822
822
|
def _deserialize(
|
|
@@ -834,7 +834,7 @@ class Tuple(Field[tuple]):
|
|
|
834
834
|
result = []
|
|
835
835
|
errors = {}
|
|
836
836
|
|
|
837
|
-
for idx, (field, each) in enumerate(zip(self.tuple_fields, value)):
|
|
837
|
+
for idx, (field, each) in enumerate(zip(self.tuple_fields, value, strict=True)):
|
|
838
838
|
try:
|
|
839
839
|
result.append(field.deserialize(each, **kwargs))
|
|
840
840
|
except ValidationError as error:
|
|
@@ -1745,7 +1745,7 @@ class Email(String):
|
|
|
1745
1745
|
self.validators.insert(0, validator)
|
|
1746
1746
|
|
|
1747
1747
|
|
|
1748
|
-
class IP(Field[
|
|
1748
|
+
class IP(Field[ipaddress.IPv4Address | ipaddress.IPv6Address]):
|
|
1749
1749
|
"""A IP address field.
|
|
1750
1750
|
|
|
1751
1751
|
:param exploded: If `True`, serialize ipv6 address in long form, ie. with groups
|
|
@@ -1802,9 +1802,7 @@ class IPv6(IP):
|
|
|
1802
1802
|
DESERIALIZATION_CLASS = ipaddress.IPv6Address
|
|
1803
1803
|
|
|
1804
1804
|
|
|
1805
|
-
class IPInterface(
|
|
1806
|
-
Field[typing.Union[ipaddress.IPv4Interface, ipaddress.IPv6Interface]]
|
|
1807
|
-
):
|
|
1805
|
+
class IPInterface(Field[ipaddress.IPv4Interface | ipaddress.IPv6Interface]):
|
|
1808
1806
|
"""A IPInterface field.
|
|
1809
1807
|
|
|
1810
1808
|
IP interface is the non-strict form of the IPNetwork type where arbitrary host
|
|
@@ -1188,7 +1188,9 @@ class Schema(metaclass=SchemaMeta):
|
|
|
1188
1188
|
pass_original = validator_kwargs.get("pass_original", False)
|
|
1189
1189
|
|
|
1190
1190
|
if many and not pass_collection:
|
|
1191
|
-
for idx, (item, orig) in enumerate(
|
|
1191
|
+
for idx, (item, orig) in enumerate(
|
|
1192
|
+
zip(data, original_data, strict=True)
|
|
1193
|
+
):
|
|
1192
1194
|
self._run_validator(
|
|
1193
1195
|
validator,
|
|
1194
1196
|
item,
|
|
@@ -7,23 +7,16 @@
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
-
try:
|
|
11
|
-
from typing import TypeAlias
|
|
12
|
-
except ImportError: # Remove when dropping Python 3.9
|
|
13
|
-
from typing_extensions import TypeAlias
|
|
14
|
-
|
|
15
10
|
import typing
|
|
16
11
|
|
|
17
12
|
#: A type that can be either a sequence of strings or a set of strings
|
|
18
|
-
StrSequenceOrSet: TypeAlias = typing.
|
|
19
|
-
typing.Sequence[str], typing.AbstractSet[str]
|
|
20
|
-
]
|
|
13
|
+
StrSequenceOrSet: typing.TypeAlias = typing.Sequence[str] | typing.AbstractSet[str]
|
|
21
14
|
|
|
22
15
|
#: Type for validator functions
|
|
23
|
-
Validator: TypeAlias = typing.Callable[[typing.Any], typing.Any]
|
|
16
|
+
Validator: typing.TypeAlias = typing.Callable[[typing.Any], typing.Any]
|
|
24
17
|
|
|
25
18
|
#: A valid option for the ``unknown`` schema option and argument
|
|
26
|
-
UnknownOption: TypeAlias = typing.Literal["exclude", "include", "raise"]
|
|
19
|
+
UnknownOption: typing.TypeAlias = typing.Literal["exclude", "include", "raise"]
|
|
27
20
|
|
|
28
21
|
|
|
29
22
|
class SchemaValidator(typing.Protocol):
|
|
@@ -7,31 +7,25 @@ import inspect
|
|
|
7
7
|
import typing
|
|
8
8
|
from collections.abc import Mapping, Sequence
|
|
9
9
|
|
|
10
|
-
# Remove when we drop Python 3.9
|
|
11
|
-
try:
|
|
12
|
-
from typing import TypeGuard
|
|
13
|
-
except ImportError:
|
|
14
|
-
from typing_extensions import TypeGuard
|
|
15
|
-
|
|
16
10
|
from marshmallow.constants import missing
|
|
17
11
|
|
|
18
12
|
|
|
19
|
-
def is_generator(obj) -> TypeGuard[typing.Generator]:
|
|
13
|
+
def is_generator(obj) -> typing.TypeGuard[typing.Generator]:
|
|
20
14
|
"""Return True if ``obj`` is a generator"""
|
|
21
15
|
return inspect.isgeneratorfunction(obj) or inspect.isgenerator(obj)
|
|
22
16
|
|
|
23
17
|
|
|
24
|
-
def is_iterable_but_not_string(obj) -> TypeGuard[typing.Iterable]:
|
|
18
|
+
def is_iterable_but_not_string(obj) -> typing.TypeGuard[typing.Iterable]:
|
|
25
19
|
"""Return True if ``obj`` is an iterable object that isn't a string."""
|
|
26
20
|
return (hasattr(obj, "__iter__") and not hasattr(obj, "strip")) or is_generator(obj)
|
|
27
21
|
|
|
28
22
|
|
|
29
|
-
def is_sequence_but_not_string(obj) -> TypeGuard[Sequence]:
|
|
23
|
+
def is_sequence_but_not_string(obj) -> typing.TypeGuard[Sequence]:
|
|
30
24
|
"""Return True if ``obj`` is a sequence that isn't a string."""
|
|
31
25
|
return isinstance(obj, Sequence) and not isinstance(obj, (str, bytes))
|
|
32
26
|
|
|
33
27
|
|
|
34
|
-
def is_collection(obj) -> TypeGuard[typing.Iterable]:
|
|
28
|
+
def is_collection(obj) -> typing.TypeGuard[typing.Iterable]:
|
|
35
29
|
"""Return True if ``obj`` is a collection type, e.g list, tuple, queryset."""
|
|
36
30
|
return is_iterable_but_not_string(obj) and not isinstance(obj, Mapping)
|
|
37
31
|
|
|
@@ -187,7 +187,7 @@ class URL(Validator):
|
|
|
187
187
|
self.relative = relative
|
|
188
188
|
self.absolute = absolute
|
|
189
189
|
self.error: str = error or self.default_message
|
|
190
|
-
self.schemes = schemes
|
|
190
|
+
self.schemes = {s.lower() for s in schemes} if schemes else self.default_schemes
|
|
191
191
|
self.require_tld = require_tld
|
|
192
192
|
|
|
193
193
|
def _repr_args(self) -> str:
|
|
@@ -623,8 +623,7 @@ class OneOf(Validator):
|
|
|
623
623
|
:param valuegetter: Can be a callable or a string. In the former case, it must
|
|
624
624
|
be a one-argument callable which returns the value of a
|
|
625
625
|
choice. In the latter case, the string specifies the name
|
|
626
|
-
of an attribute of the choice objects. Defaults to `str()
|
|
627
|
-
or `str()`.
|
|
626
|
+
of an attribute of the choice objects. Defaults to `str()`.
|
|
628
627
|
"""
|
|
629
628
|
valuegetter = valuegetter if callable(valuegetter) else attrgetter(valuegetter)
|
|
630
629
|
pairs = zip_longest(self.choices, self.labels, fillvalue="")
|
|
@@ -165,7 +165,7 @@ class TestPassOriginal:
|
|
|
165
165
|
def post_load(self, data, original, many, **kwargs):
|
|
166
166
|
if many:
|
|
167
167
|
ret = []
|
|
168
|
-
for item, orig_item in zip(data, original):
|
|
168
|
+
for item, orig_item in zip(data, original, strict=True):
|
|
169
169
|
item["_post_load"] = orig_item["sentinel"]
|
|
170
170
|
ret.append(item)
|
|
171
171
|
else:
|
|
@@ -177,7 +177,7 @@ class TestPassOriginal:
|
|
|
177
177
|
def post_dump(self, data, original, many, **kwargs):
|
|
178
178
|
if many:
|
|
179
179
|
ret = []
|
|
180
|
-
for item, orig_item in zip(data, original):
|
|
180
|
+
for item, orig_item in zip(data, original, strict=True):
|
|
181
181
|
item["_post_dump"] = orig_item["sentinel"]
|
|
182
182
|
ret.append(item)
|
|
183
183
|
else:
|
|
@@ -14,6 +14,7 @@ from marshmallow import (
|
|
|
14
14
|
RAISE,
|
|
15
15
|
Schema,
|
|
16
16
|
fields,
|
|
17
|
+
missing,
|
|
17
18
|
validate,
|
|
18
19
|
)
|
|
19
20
|
from marshmallow.exceptions import ValidationError
|
|
@@ -1005,6 +1006,13 @@ class TestFieldDeserialization:
|
|
|
1005
1006
|
field = fields.Function(lambda x: None, deserialize=lambda val: val.upper())
|
|
1006
1007
|
assert field.deserialize("foo") == "FOO"
|
|
1007
1008
|
|
|
1009
|
+
def test_function_field_deserialization_missing_with_length_validator(self):
|
|
1010
|
+
field = fields.Function(
|
|
1011
|
+
deserialize=lambda value: value.get("some-key", missing),
|
|
1012
|
+
validate=validate.Length(min=0),
|
|
1013
|
+
)
|
|
1014
|
+
assert field.deserialize({}) is missing
|
|
1015
|
+
|
|
1008
1016
|
def test_function_field_passed_deserialize_only_is_load_only(self):
|
|
1009
1017
|
field = fields.Function(deserialize=lambda val: val.upper())
|
|
1010
1018
|
assert field.load_only is True
|
|
@@ -1306,7 +1314,7 @@ class TestFieldDeserialization:
|
|
|
1306
1314
|
field = fields.List(fields.DateTime())
|
|
1307
1315
|
result = field.deserialize(dstrings)
|
|
1308
1316
|
assert all(isinstance(each, dt.datetime) for each in result)
|
|
1309
|
-
for actual, expected in zip(result, dtimes):
|
|
1317
|
+
for actual, expected in zip(result, dtimes, strict=True):
|
|
1310
1318
|
assert_date_equal(actual, expected)
|
|
1311
1319
|
|
|
1312
1320
|
def test_list_field_deserialize_invalid_item(self):
|
|
@@ -1347,7 +1355,9 @@ class TestFieldDeserialization:
|
|
|
1347
1355
|
|
|
1348
1356
|
assert isinstance(result, tuple)
|
|
1349
1357
|
assert len(result) == 2
|
|
1350
|
-
for val, type_, true_val in zip(
|
|
1358
|
+
for val, type_, true_val in zip(
|
|
1359
|
+
result, (dt.datetime, int), (dtime, 42), strict=True
|
|
1360
|
+
):
|
|
1351
1361
|
assert isinstance(val, type_)
|
|
1352
1362
|
assert val == true_val
|
|
1353
1363
|
|
|
@@ -2265,7 +2265,7 @@ class TestGetAttribute:
|
|
|
2265
2265
|
]
|
|
2266
2266
|
schema = UserDictSchema(many=True)
|
|
2267
2267
|
results = schema.dump(user_dicts)
|
|
2268
|
-
for result, user_dict in zip(results, user_dicts):
|
|
2268
|
+
for result, user_dict in zip(results, user_dicts, strict=True):
|
|
2269
2269
|
assert result["name"] == user_dict["_name"]
|
|
2270
2270
|
assert result["email"] == user_dict["_email"]
|
|
2271
2271
|
# can't serialize User object
|
|
@@ -124,7 +124,7 @@ def test_from_timestamp_with_negative_value():
|
|
|
124
124
|
|
|
125
125
|
def test_from_timestamp_with_overflow_value():
|
|
126
126
|
value = 9223372036854775
|
|
127
|
-
with pytest.raises(ValueError, match="out of range"):
|
|
127
|
+
with pytest.raises(ValueError, match=r"out of range|year must be in 1\.\.9999"):
|
|
128
128
|
utils.from_timestamp(value)
|
|
129
129
|
|
|
130
130
|
|
|
@@ -205,6 +205,14 @@ def test_url_custom_scheme():
|
|
|
205
205
|
assert validator(url) == url
|
|
206
206
|
|
|
207
207
|
|
|
208
|
+
def test_url_custom_scheme_case_insensitive():
|
|
209
|
+
validator = validate.URL(schemes={"HTTP"})
|
|
210
|
+
assert validator("http://example.com") == "http://example.com"
|
|
211
|
+
|
|
212
|
+
validator = validate.URL(schemes={"HtTp"})
|
|
213
|
+
assert validator("hTtP://example.com") == "hTtP://example.com"
|
|
214
|
+
|
|
215
|
+
|
|
208
216
|
@pytest.mark.parametrize(
|
|
209
217
|
"valid_url",
|
|
210
218
|
(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{marshmallow-4.0.1 → marshmallow-4.1.1}/docs/_static/marshmallow-logo-with-title-for-dark-theme.png
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|