marshmallow 3.21.3__tar.gz → 3.23.0__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-3.21.3 → marshmallow-3.23.0}/CHANGELOG.rst +22 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/PKG-INFO +35 -20
- {marshmallow-3.21.3 → marshmallow-3.23.0}/README.rst +27 -11
- marshmallow-3.23.0/docs/_static/css/sponsor-message.css +15 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/conf.py +1 -1
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/index.rst +22 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/pyproject.toml +9 -9
- {marshmallow-3.21.3 → marshmallow-3.23.0}/src/marshmallow/class_registry.py +1 -1
- {marshmallow-3.21.3 → marshmallow-3.23.0}/src/marshmallow/decorators.py +13 -11
- {marshmallow-3.21.3 → marshmallow-3.23.0}/src/marshmallow/fields.py +14 -10
- {marshmallow-3.21.3 → marshmallow-3.23.0}/src/marshmallow/schema.py +55 -61
- {marshmallow-3.21.3 → marshmallow-3.23.0}/src/marshmallow/types.py +0 -1
- {marshmallow-3.21.3 → marshmallow-3.23.0}/tests/base.py +3 -5
- {marshmallow-3.21.3 → marshmallow-3.23.0}/tests/test_deserialization.py +2 -2
- {marshmallow-3.21.3 → marshmallow-3.23.0}/tests/test_options.py +16 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/tests/test_serialization.py +4 -4
- {marshmallow-3.21.3 → marshmallow-3.23.0}/tests/test_utils.py +4 -4
- {marshmallow-3.21.3 → marshmallow-3.23.0}/tox.ini +1 -1
- {marshmallow-3.21.3 → marshmallow-3.23.0}/CONTRIBUTING.rst +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/LICENSE +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/NOTICE +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/SECURITY.md +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/.gitignore +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/_static/css/versionwarning.css +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/_static/marshmallow-logo.png +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/_templates/donate.html +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/_templates/useful-links.html +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/about.rst.inc +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/api_reference.rst +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/authors.rst +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/changelog.rst +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/code_of_conduct.rst +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/contributing.rst +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/custom_fields.rst +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/dashing.json +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/ecosystem.rst +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/examples.rst +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/extending.rst +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/install.rst +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/kudos.rst +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/license.rst +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/marshmallow.class_registry.rst +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/marshmallow.decorators.rst +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/marshmallow.error_store.rst +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/marshmallow.exceptions.rst +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/marshmallow.fields.rst +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/marshmallow.schema.rst +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/marshmallow.utils.rst +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/marshmallow.validate.rst +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/nesting.rst +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/quickstart.rst +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/upgrading.rst +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/whos_using.rst +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/docs/why.rst +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/src/marshmallow/__init__.py +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/src/marshmallow/base.py +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/src/marshmallow/error_store.py +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/src/marshmallow/exceptions.py +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/src/marshmallow/orderedset.py +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/src/marshmallow/py.typed +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/src/marshmallow/utils.py +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/src/marshmallow/validate.py +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/src/marshmallow/warnings.py +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/tests/__init__.py +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/tests/conftest.py +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/tests/foo_serializer.py +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/tests/mypy_test_cases/test_validation_error.py +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/tests/test_decorators.py +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/tests/test_error_store.py +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/tests/test_exceptions.py +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/tests/test_fields.py +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/tests/test_registry.py +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/tests/test_schema.py +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/tests/test_validate.py +0 -0
- {marshmallow-3.21.3 → marshmallow-3.23.0}/tests/test_version_attributes.py +0 -0
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
Changelog
|
|
2
2
|
---------
|
|
3
3
|
|
|
4
|
+
3.23.0 (2024-10-17)
|
|
5
|
+
*******************
|
|
6
|
+
|
|
7
|
+
Features:
|
|
8
|
+
|
|
9
|
+
- Typing: replace "type" with specific metaclass for ``Schema`` and ``Field``.
|
|
10
|
+
|
|
11
|
+
Other changes:
|
|
12
|
+
|
|
13
|
+
- Officially support Python 3.13 (:pr:`2319`).
|
|
14
|
+
- Drop support for Python 3.8 (:pr:`2318`).
|
|
15
|
+
|
|
16
|
+
3.22.0 (2024-08-20)
|
|
17
|
+
*******************
|
|
18
|
+
|
|
19
|
+
Features:
|
|
20
|
+
|
|
21
|
+
- Add ``many`` Meta option to ``Schema`` so it expects a collection by default (:issue:`2270`).
|
|
22
|
+
Thanks :user:`himalczyk` for reporting and :user:`deckar01` for the PR.
|
|
23
|
+
- Refactor hooks (:pr:`2279`).
|
|
24
|
+
Thanks :user:`deckar01` for the PR.
|
|
25
|
+
|
|
4
26
|
3.21.3 (2024-06-05)
|
|
5
27
|
*******************
|
|
6
28
|
|
|
@@ -1,31 +1,30 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: marshmallow
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.23.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>
|
|
7
|
-
Requires-Python: >=3.
|
|
7
|
+
Requires-Python: >=3.9
|
|
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.8
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.9
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.10
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
18
|
Requires-Dist: packaging>=17.0
|
|
19
19
|
Requires-Dist: marshmallow[tests] ; extra == "dev"
|
|
20
20
|
Requires-Dist: tox ; extra == "dev"
|
|
21
|
-
Requires-Dist: pre-commit
|
|
22
|
-
Requires-Dist: sphinx==
|
|
23
|
-
Requires-Dist: sphinx-issues==
|
|
24
|
-
Requires-Dist: alabaster==0.
|
|
21
|
+
Requires-Dist: pre-commit>=3.5,<5.0 ; extra == "dev"
|
|
22
|
+
Requires-Dist: sphinx==8.1.3 ; extra == "docs"
|
|
23
|
+
Requires-Dist: sphinx-issues==5.0.0 ; 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.
|
|
26
|
+
Requires-Dist: autodocsumm==0.2.13 ; extra == "docs"
|
|
27
27
|
Requires-Dist: pytest ; extra == "tests"
|
|
28
|
-
Requires-Dist: pytz ; extra == "tests"
|
|
29
28
|
Requires-Dist: simplejson ; extra == "tests"
|
|
30
29
|
Project-URL: Changelog, https://marshmallow.readthedocs.io/en/latest/changelog.html
|
|
31
30
|
Project-URL: Funding, https://opencollective.com/marshmallow
|
|
@@ -40,24 +39,39 @@ Provides-Extra: tests
|
|
|
40
39
|
marshmallow: simplified object serialization
|
|
41
40
|
********************************************
|
|
42
41
|
|
|
43
|
-
|
|
42
|
+
|pypi| |build-status| |pre-commit| |docs|
|
|
43
|
+
|
|
44
|
+
.. |pypi| image:: https://badgen.net/pypi/v/marshmallow
|
|
44
45
|
:target: https://pypi.org/project/marshmallow/
|
|
45
46
|
:alt: Latest version
|
|
46
47
|
|
|
47
|
-
.. image:: https://github.com/marshmallow-code/marshmallow/actions/workflows/build-release.yml/badge.svg
|
|
48
|
+
.. |build-status| image:: https://github.com/marshmallow-code/marshmallow/actions/workflows/build-release.yml/badge.svg
|
|
48
49
|
:target: https://github.com/marshmallow-code/marshmallow/actions/workflows/build-release.yml
|
|
49
50
|
:alt: Build status
|
|
50
51
|
|
|
51
|
-
.. image:: https://results.pre-commit.ci/badge/github/marshmallow-code/marshmallow/dev.svg
|
|
52
|
+
.. |pre-commit| image:: https://results.pre-commit.ci/badge/github/marshmallow-code/marshmallow/dev.svg
|
|
52
53
|
:target: https://results.pre-commit.ci/latest/github/marshmallow-code/marshmallow/dev
|
|
53
54
|
:alt: pre-commit.ci status
|
|
54
55
|
|
|
55
|
-
.. image:: https://readthedocs.org/projects/marshmallow/badge/
|
|
56
|
+
.. |docs| image:: https://readthedocs.org/projects/marshmallow/badge/
|
|
56
57
|
:target: https://marshmallow.readthedocs.io/
|
|
57
58
|
:alt: Documentation
|
|
58
59
|
|
|
59
60
|
**marshmallow** is an ORM/ODM/framework-agnostic library for converting complex datatypes, such as objects, to and from native Python datatypes.
|
|
60
61
|
|
|
62
|
+
Sponsor Message
|
|
63
|
+
===============
|
|
64
|
+
|
|
65
|
+
Input an OpenAPI spec to generate API docs that look as good as Stripe's. `Request a preview <https://form.typeform.com/to/bShdJw7z>`_ of your docs on Fern.
|
|
66
|
+
|
|
67
|
+
.. image:: https://github.com/user-attachments/assets/69916225-0d61-4bd7-b3b9-e378557673cb
|
|
68
|
+
:target: https://form.typeform.com/to/bShdJw7z
|
|
69
|
+
:align: center
|
|
70
|
+
:alt: Fern logo
|
|
71
|
+
|
|
72
|
+
Example
|
|
73
|
+
=======
|
|
74
|
+
|
|
61
75
|
.. code-block:: python
|
|
62
76
|
|
|
63
77
|
from datetime import date
|
|
@@ -109,7 +123,7 @@ Full documentation is available at https://marshmallow.readthedocs.io/ .
|
|
|
109
123
|
Requirements
|
|
110
124
|
============
|
|
111
125
|
|
|
112
|
-
- Python >= 3.
|
|
126
|
+
- Python >= 3.9
|
|
113
127
|
|
|
114
128
|
Ecosystem
|
|
115
129
|
=========
|
|
@@ -152,15 +166,16 @@ Thank you to all our backers! [`Become a backer`_]
|
|
|
152
166
|
Sponsors
|
|
153
167
|
--------
|
|
154
168
|
|
|
155
|
-
|
|
156
|
-
Your logo will show up here with a link to your website. [`Become a sponsor`_]
|
|
169
|
+
marshmallow is sponsored by `Route4Me <https://route4me.com>`_.
|
|
157
170
|
|
|
158
|
-
..
|
|
171
|
+
.. image:: https://github.com/user-attachments/assets/018c2e23-032e-4a11-98da-8b6dc25b9054
|
|
172
|
+
:target: https://route4me.com
|
|
173
|
+
:alt: Routing Planner
|
|
159
174
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
:alt: Become a sponsor
|
|
175
|
+
Support this project by becoming a sponsor (or ask your company to support this project by becoming a sponsor).
|
|
176
|
+
Your logo will be displayed here with a link to your website. [`Become a sponsor`_]
|
|
163
177
|
|
|
178
|
+
.. _`Become a sponsor`: https://opencollective.com/marshmallow#sponsor
|
|
164
179
|
|
|
165
180
|
Professional Support
|
|
166
181
|
====================
|
|
@@ -2,24 +2,39 @@
|
|
|
2
2
|
marshmallow: simplified object serialization
|
|
3
3
|
********************************************
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
|pypi| |build-status| |pre-commit| |docs|
|
|
6
|
+
|
|
7
|
+
.. |pypi| image:: https://badgen.net/pypi/v/marshmallow
|
|
6
8
|
:target: https://pypi.org/project/marshmallow/
|
|
7
9
|
:alt: Latest version
|
|
8
10
|
|
|
9
|
-
.. image:: https://github.com/marshmallow-code/marshmallow/actions/workflows/build-release.yml/badge.svg
|
|
11
|
+
.. |build-status| image:: https://github.com/marshmallow-code/marshmallow/actions/workflows/build-release.yml/badge.svg
|
|
10
12
|
:target: https://github.com/marshmallow-code/marshmallow/actions/workflows/build-release.yml
|
|
11
13
|
:alt: Build status
|
|
12
14
|
|
|
13
|
-
.. image:: https://results.pre-commit.ci/badge/github/marshmallow-code/marshmallow/dev.svg
|
|
15
|
+
.. |pre-commit| image:: https://results.pre-commit.ci/badge/github/marshmallow-code/marshmallow/dev.svg
|
|
14
16
|
:target: https://results.pre-commit.ci/latest/github/marshmallow-code/marshmallow/dev
|
|
15
17
|
:alt: pre-commit.ci status
|
|
16
18
|
|
|
17
|
-
.. image:: https://readthedocs.org/projects/marshmallow/badge/
|
|
19
|
+
.. |docs| image:: https://readthedocs.org/projects/marshmallow/badge/
|
|
18
20
|
:target: https://marshmallow.readthedocs.io/
|
|
19
21
|
:alt: Documentation
|
|
20
22
|
|
|
21
23
|
**marshmallow** is an ORM/ODM/framework-agnostic library for converting complex datatypes, such as objects, to and from native Python datatypes.
|
|
22
24
|
|
|
25
|
+
Sponsor Message
|
|
26
|
+
===============
|
|
27
|
+
|
|
28
|
+
Input an OpenAPI spec to generate API docs that look as good as Stripe's. `Request a preview <https://form.typeform.com/to/bShdJw7z>`_ of your docs on Fern.
|
|
29
|
+
|
|
30
|
+
.. image:: https://github.com/user-attachments/assets/69916225-0d61-4bd7-b3b9-e378557673cb
|
|
31
|
+
:target: https://form.typeform.com/to/bShdJw7z
|
|
32
|
+
:align: center
|
|
33
|
+
:alt: Fern logo
|
|
34
|
+
|
|
35
|
+
Example
|
|
36
|
+
=======
|
|
37
|
+
|
|
23
38
|
.. code-block:: python
|
|
24
39
|
|
|
25
40
|
from datetime import date
|
|
@@ -71,7 +86,7 @@ Full documentation is available at https://marshmallow.readthedocs.io/ .
|
|
|
71
86
|
Requirements
|
|
72
87
|
============
|
|
73
88
|
|
|
74
|
-
- Python >= 3.
|
|
89
|
+
- Python >= 3.9
|
|
75
90
|
|
|
76
91
|
Ecosystem
|
|
77
92
|
=========
|
|
@@ -114,15 +129,16 @@ Thank you to all our backers! [`Become a backer`_]
|
|
|
114
129
|
Sponsors
|
|
115
130
|
--------
|
|
116
131
|
|
|
117
|
-
|
|
118
|
-
Your logo will show up here with a link to your website. [`Become a sponsor`_]
|
|
132
|
+
marshmallow is sponsored by `Route4Me <https://route4me.com>`_.
|
|
119
133
|
|
|
120
|
-
..
|
|
134
|
+
.. image:: https://github.com/user-attachments/assets/018c2e23-032e-4a11-98da-8b6dc25b9054
|
|
135
|
+
:target: https://route4me.com
|
|
136
|
+
:alt: Routing Planner
|
|
121
137
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
:alt: Become a sponsor
|
|
138
|
+
Support this project by becoming a sponsor (or ask your company to support this project by becoming a sponsor).
|
|
139
|
+
Your logo will be displayed here with a link to your website. [`Become a sponsor`_]
|
|
125
140
|
|
|
141
|
+
.. _`Become a sponsor`: https://opencollective.com/marshmallow#sponsor
|
|
126
142
|
|
|
127
143
|
Professional Support
|
|
128
144
|
====================
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/* Override alabaster's admonition styling, which we used for sponsored messages */
|
|
2
|
+
|
|
3
|
+
div.admonition-sponsor-message p.admonition-title {
|
|
4
|
+
font-size: 18px;
|
|
5
|
+
font-style: normal;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
p.admonition-title::after {
|
|
9
|
+
content: "";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
div.admonition-sponsor-message p {
|
|
13
|
+
font-size: 14px;
|
|
14
|
+
font-style: italic;
|
|
15
|
+
}
|
|
@@ -36,7 +36,7 @@ exclude_patterns = ["_build"]
|
|
|
36
36
|
html_theme_path = [alabaster.get_path()]
|
|
37
37
|
html_theme = "alabaster"
|
|
38
38
|
html_static_path = ["_static"]
|
|
39
|
-
html_css_files = ["css/versionwarning.css"]
|
|
39
|
+
html_css_files = ["css/versionwarning.css", "css/sponsor-message.css"]
|
|
40
40
|
templates_path = ["_templates"]
|
|
41
41
|
html_show_sourcelink = False
|
|
42
42
|
|
|
@@ -6,6 +6,15 @@ marshmallow: simplified object serialization
|
|
|
6
6
|
|
|
7
7
|
Release v\ |version|. (:doc:`Changelog <changelog>`)
|
|
8
8
|
|
|
9
|
+
.. admonition:: Sponsor Message
|
|
10
|
+
|
|
11
|
+
Input an OpenAPI spec to generate API docs that look as good as Stripe's. `Request a preview <https://form.typeform.com/to/bShdJw7z>`_ of your docs on Fern.
|
|
12
|
+
|
|
13
|
+
.. image:: https://github.com/user-attachments/assets/69916225-0d61-4bd7-b3b9-e378557673cb
|
|
14
|
+
:target: https://form.typeform.com/to/bShdJw7z
|
|
15
|
+
:align: center
|
|
16
|
+
:alt: Fern logo
|
|
17
|
+
|
|
9
18
|
.. include:: about.rst.inc
|
|
10
19
|
|
|
11
20
|
Upgrading from an older version?
|
|
@@ -18,6 +27,19 @@ Why another library?
|
|
|
18
27
|
|
|
19
28
|
See :doc:`this document <why>` to learn about what makes marshmallow unique.
|
|
20
29
|
|
|
30
|
+
Sponsors
|
|
31
|
+
========
|
|
32
|
+
|
|
33
|
+
marshmallow is sponsored by `Route4Me <https://route4me.com>`_.
|
|
34
|
+
|
|
35
|
+
.. image:: https://github.com/user-attachments/assets/018c2e23-032e-4a11-98da-8b6dc25b9054
|
|
36
|
+
:target: https://route4me.com
|
|
37
|
+
:alt: Routing Planner
|
|
38
|
+
|
|
39
|
+
Support this project by becoming a sponsor (or ask your company to support this project by becoming a sponsor).
|
|
40
|
+
Your logo will be displayed here with a link to your website. [`Become a sponsor`_]
|
|
41
|
+
|
|
42
|
+
.. _`Become a sponsor`: https://opencollective.com/marshmallow#sponsor
|
|
21
43
|
|
|
22
44
|
Guide
|
|
23
45
|
=====
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "marshmallow"
|
|
3
|
-
version = "3.
|
|
3
|
+
version = "3.23.0"
|
|
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" }
|
|
@@ -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.8",
|
|
19
18
|
"Programming Language :: Python :: 3.9",
|
|
20
19
|
"Programming Language :: Python :: 3.10",
|
|
21
20
|
"Programming Language :: Python :: 3.11",
|
|
22
21
|
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
23
|
]
|
|
24
|
-
requires-python = ">=3.
|
|
24
|
+
requires-python = ">=3.9"
|
|
25
25
|
dependencies = ["packaging>=17.0"]
|
|
26
26
|
|
|
27
27
|
[project.urls]
|
|
@@ -33,14 +33,14 @@ Tidelift = "https://tidelift.com/subscription/pkg/pypi-marshmallow?utm_source=py
|
|
|
33
33
|
|
|
34
34
|
[project.optional-dependencies]
|
|
35
35
|
docs = [
|
|
36
|
-
"sphinx==
|
|
37
|
-
"sphinx-issues==
|
|
38
|
-
"alabaster==0.
|
|
36
|
+
"sphinx==8.1.3",
|
|
37
|
+
"sphinx-issues==5.0.0",
|
|
38
|
+
"alabaster==1.0.0",
|
|
39
39
|
"sphinx-version-warning==1.1.2",
|
|
40
|
-
"autodocsumm==0.2.
|
|
40
|
+
"autodocsumm==0.2.13",
|
|
41
41
|
]
|
|
42
|
-
tests = ["pytest", "
|
|
43
|
-
dev = ["marshmallow[tests]", "tox", "pre-commit
|
|
42
|
+
tests = ["pytest", "simplejson"]
|
|
43
|
+
dev = ["marshmallow[tests]", "tox", "pre-commit>=3.5,<5.0"]
|
|
44
44
|
|
|
45
45
|
[build-system]
|
|
46
46
|
requires = ["flit_core<4"]
|
|
@@ -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[
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
229
|
+
# __marshmallow_hook__, keyed by <tag>
|
|
228
230
|
if hook_config is not None:
|
|
229
|
-
hook_config[
|
|
231
|
+
hook_config[tag].append((many, kwargs))
|
|
230
232
|
|
|
231
233
|
return fn
|
|
@@ -33,6 +33,10 @@ from marshmallow.utils import (
|
|
|
33
33
|
from marshmallow.validate import And, Length
|
|
34
34
|
from marshmallow.warnings import RemovedInMarshmallow4Warning
|
|
35
35
|
|
|
36
|
+
if typing.TYPE_CHECKING:
|
|
37
|
+
from marshmallow.schema import SchemaMeta
|
|
38
|
+
|
|
39
|
+
|
|
36
40
|
__all__ = [
|
|
37
41
|
"Field",
|
|
38
42
|
"Raw",
|
|
@@ -535,10 +539,10 @@ class Nested(Field):
|
|
|
535
539
|
def __init__(
|
|
536
540
|
self,
|
|
537
541
|
nested: SchemaABC
|
|
538
|
-
|
|
|
542
|
+
| SchemaMeta
|
|
539
543
|
| str
|
|
540
|
-
| dict[str, Field | type]
|
|
541
|
-
| typing.Callable[[], SchemaABC |
|
|
544
|
+
| dict[str, Field | type[Field]]
|
|
545
|
+
| typing.Callable[[], SchemaABC | SchemaMeta | dict[str, Field | type[Field]]],
|
|
542
546
|
*,
|
|
543
547
|
dump_default: typing.Any = missing_,
|
|
544
548
|
default: typing.Any = missing_,
|
|
@@ -700,7 +704,7 @@ class Pluck(Nested):
|
|
|
700
704
|
|
|
701
705
|
def __init__(
|
|
702
706
|
self,
|
|
703
|
-
nested: SchemaABC |
|
|
707
|
+
nested: SchemaABC | SchemaMeta | str | typing.Callable[[], SchemaABC],
|
|
704
708
|
field_name: str,
|
|
705
709
|
**kwargs,
|
|
706
710
|
):
|
|
@@ -751,7 +755,7 @@ class List(Field):
|
|
|
751
755
|
#: Default error messages.
|
|
752
756
|
default_error_messages = {"invalid": "Not a valid list."}
|
|
753
757
|
|
|
754
|
-
def __init__(self, cls_or_instance: Field | type, **kwargs):
|
|
758
|
+
def __init__(self, cls_or_instance: Field | type[Field], **kwargs):
|
|
755
759
|
super().__init__(**kwargs)
|
|
756
760
|
try:
|
|
757
761
|
self.inner = resolve_field_instance(cls_or_instance)
|
|
@@ -1236,7 +1240,7 @@ class DateTime(Field):
|
|
|
1236
1240
|
"rfc822": utils.rfcformat,
|
|
1237
1241
|
"timestamp": utils.timestamp,
|
|
1238
1242
|
"timestamp_ms": utils.timestamp_ms,
|
|
1239
|
-
} # type:
|
|
1243
|
+
} # type: dict[str, typing.Callable[[typing.Any], str | float]]
|
|
1240
1244
|
|
|
1241
1245
|
DESERIALIZATION_FUNCS = {
|
|
1242
1246
|
"iso": utils.from_iso_datetime,
|
|
@@ -1245,7 +1249,7 @@ class DateTime(Field):
|
|
|
1245
1249
|
"rfc822": utils.from_rfc,
|
|
1246
1250
|
"timestamp": utils.from_timestamp,
|
|
1247
1251
|
"timestamp_ms": utils.from_timestamp_ms,
|
|
1248
|
-
} # type:
|
|
1252
|
+
} # type: dict[str, typing.Callable[[str], typing.Any]]
|
|
1249
1253
|
|
|
1250
1254
|
DEFAULT_FORMAT = "iso"
|
|
1251
1255
|
|
|
@@ -1555,8 +1559,8 @@ class Mapping(Field):
|
|
|
1555
1559
|
|
|
1556
1560
|
def __init__(
|
|
1557
1561
|
self,
|
|
1558
|
-
keys: Field | type | None = None,
|
|
1559
|
-
values: Field | type | None = None,
|
|
1562
|
+
keys: Field | type[Field] | None = None,
|
|
1563
|
+
values: Field | type[Field] | None = None,
|
|
1560
1564
|
**kwargs,
|
|
1561
1565
|
):
|
|
1562
1566
|
super().__init__(**kwargs)
|
|
@@ -1878,7 +1882,7 @@ class Enum(Field):
|
|
|
1878
1882
|
self,
|
|
1879
1883
|
enum: type[EnumType],
|
|
1880
1884
|
*,
|
|
1881
|
-
by_value: bool | Field | type = False,
|
|
1885
|
+
by_value: bool | Field | type[Field] = False,
|
|
1882
1886
|
**kwargs,
|
|
1883
1887
|
):
|
|
1884
1888
|
super().__init__(**kwargs)
|
|
@@ -40,8 +40,6 @@ from marshmallow.utils import (
|
|
|
40
40
|
)
|
|
41
41
|
from marshmallow.warnings import RemovedInMarshmallow4Warning
|
|
42
42
|
|
|
43
|
-
_T = typing.TypeVar("_T")
|
|
44
|
-
|
|
45
43
|
|
|
46
44
|
def _get_fields(attrs):
|
|
47
45
|
"""Get fields from a class
|
|
@@ -57,7 +55,7 @@ def _get_fields(attrs):
|
|
|
57
55
|
|
|
58
56
|
# This function allows Schemas to inherit from non-Schema classes and ensures
|
|
59
57
|
# inheritance according to the MRO
|
|
60
|
-
def _get_fields_by_mro(klass):
|
|
58
|
+
def _get_fields_by_mro(klass: SchemaMeta):
|
|
61
59
|
"""Collect fields from a class, following its method resolution order. The
|
|
62
60
|
class itself is excluded from the search; only its parents are checked. Get
|
|
63
61
|
fields from ``_declared_fields`` if available, else use ``__dict__``.
|
|
@@ -125,10 +123,10 @@ class SchemaMeta(ABCMeta):
|
|
|
125
123
|
@classmethod
|
|
126
124
|
def get_declared_fields(
|
|
127
125
|
mcs,
|
|
128
|
-
klass:
|
|
126
|
+
klass: SchemaMeta,
|
|
129
127
|
cls_fields: list,
|
|
130
128
|
inherited_fields: list,
|
|
131
|
-
dict_cls: type = dict,
|
|
129
|
+
dict_cls: type[dict] = dict,
|
|
132
130
|
):
|
|
133
131
|
"""Returns a dictionary of field_name => `Field` pairs declared on the class.
|
|
134
132
|
This is exposed mainly so that plugins can add additional fields, e.g. fields
|
|
@@ -148,7 +146,7 @@ class SchemaMeta(ABCMeta):
|
|
|
148
146
|
class_registry.register(name, cls)
|
|
149
147
|
cls._hooks = cls.resolve_hooks()
|
|
150
148
|
|
|
151
|
-
def resolve_hooks(cls) -> dict[
|
|
149
|
+
def resolve_hooks(cls) -> dict[str, list[tuple[str, bool, dict]]]:
|
|
152
150
|
"""Add in the decorated processors
|
|
153
151
|
|
|
154
152
|
By doing this after constructing the class, we let standard inheritance
|
|
@@ -156,7 +154,7 @@ class SchemaMeta(ABCMeta):
|
|
|
156
154
|
"""
|
|
157
155
|
mro = inspect.getmro(cls)
|
|
158
156
|
|
|
159
|
-
hooks = defaultdict(list) # type:
|
|
157
|
+
hooks = defaultdict(list) # type: dict[str, list[tuple[str, bool, dict]]]
|
|
160
158
|
|
|
161
159
|
for attr_name in dir(cls):
|
|
162
160
|
# Need to look up the actual descriptor, not whatever might be
|
|
@@ -176,14 +174,16 @@ class SchemaMeta(ABCMeta):
|
|
|
176
174
|
continue
|
|
177
175
|
|
|
178
176
|
try:
|
|
179
|
-
hook_config = attr.__marshmallow_hook__
|
|
177
|
+
hook_config = attr.__marshmallow_hook__ # type: dict[str, list[tuple[bool, dict]]]
|
|
180
178
|
except AttributeError:
|
|
181
179
|
pass
|
|
182
180
|
else:
|
|
183
|
-
for
|
|
181
|
+
for tag, config in hook_config.items():
|
|
184
182
|
# Use name here so we can get the bound method later, in
|
|
185
183
|
# case the processor was a descriptor or something.
|
|
186
|
-
hooks[
|
|
184
|
+
hooks[tag].extend(
|
|
185
|
+
(attr_name, many, kwargs) for many, kwargs in config
|
|
186
|
+
)
|
|
187
187
|
|
|
188
188
|
return hooks
|
|
189
189
|
|
|
@@ -226,6 +226,7 @@ class SchemaOpts:
|
|
|
226
226
|
self.dump_only = getattr(meta, "dump_only", ())
|
|
227
227
|
self.unknown = validate_unknown_parameter_value(getattr(meta, "unknown", RAISE))
|
|
228
228
|
self.register = getattr(meta, "register", True)
|
|
229
|
+
self.many = getattr(meta, "many", False)
|
|
229
230
|
|
|
230
231
|
|
|
231
232
|
class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
@@ -303,14 +304,14 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
303
304
|
dt.date: ma_fields.Date,
|
|
304
305
|
dt.timedelta: ma_fields.TimeDelta,
|
|
305
306
|
decimal.Decimal: ma_fields.Decimal,
|
|
306
|
-
} # type:
|
|
307
|
+
} # type: dict[type, typing.Type[ma_fields.Field]]
|
|
307
308
|
#: Overrides for default schema-level error messages
|
|
308
|
-
error_messages = {} # type:
|
|
309
|
+
error_messages = {} # type: dict[str, str]
|
|
309
310
|
|
|
310
311
|
_default_error_messages = {
|
|
311
312
|
"type": "Invalid input type.",
|
|
312
313
|
"unknown": "Unknown field.",
|
|
313
|
-
} # type:
|
|
314
|
+
} # type: dict[str, str]
|
|
314
315
|
|
|
315
316
|
OPTIONS_CLASS = SchemaOpts # type: type
|
|
316
317
|
|
|
@@ -318,8 +319,8 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
318
319
|
|
|
319
320
|
# These get set by SchemaMeta
|
|
320
321
|
opts = None # type: SchemaOpts
|
|
321
|
-
_declared_fields = {} # type:
|
|
322
|
-
_hooks = {} # type:
|
|
322
|
+
_declared_fields = {} # type: dict[str, ma_fields.Field]
|
|
323
|
+
_hooks = {} # type: dict[str, list[tuple[str, bool, dict]]]
|
|
323
324
|
|
|
324
325
|
class Meta:
|
|
325
326
|
"""Options object for a Schema.
|
|
@@ -342,6 +343,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
342
343
|
`OrderedDict`.
|
|
343
344
|
- ``exclude``: Tuple or list of fields to exclude in the serialized result.
|
|
344
345
|
Nested fields can be represented with dot delimiters.
|
|
346
|
+
- ``many``: Whether the data is a collection by default.
|
|
345
347
|
- ``dateformat``: Default format for `Date <fields.Date>` fields.
|
|
346
348
|
- ``datetimeformat``: Default format for `DateTime <fields.DateTime>` fields.
|
|
347
349
|
- ``timeformat``: Default format for `Time <fields.Time>` fields.
|
|
@@ -365,7 +367,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
365
367
|
*,
|
|
366
368
|
only: types.StrSequenceOrSet | None = None,
|
|
367
369
|
exclude: types.StrSequenceOrSet = (),
|
|
368
|
-
many: bool =
|
|
370
|
+
many: bool | None = None,
|
|
369
371
|
context: dict | None = None,
|
|
370
372
|
load_only: types.StrSequenceOrSet = (),
|
|
371
373
|
dump_only: types.StrSequenceOrSet = (),
|
|
@@ -379,7 +381,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
379
381
|
raise StringNotCollectionError('"exclude" should be a list of strings')
|
|
380
382
|
# copy declared fields from metaclass
|
|
381
383
|
self.declared_fields = copy.deepcopy(self._declared_fields)
|
|
382
|
-
self.many = many
|
|
384
|
+
self.many = self.opts.many if many is None else many
|
|
383
385
|
self.only = only
|
|
384
386
|
self.exclude: set[typing.Any] | typing.MutableSet[typing.Any] = set(
|
|
385
387
|
self.opts.exclude
|
|
@@ -396,9 +398,9 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
396
398
|
self.context = context or {}
|
|
397
399
|
self._normalize_nested_options()
|
|
398
400
|
#: Dictionary mapping field_names -> :class:`Field` objects
|
|
399
|
-
self.fields = {} # type:
|
|
400
|
-
self.load_fields = {} # type:
|
|
401
|
-
self.dump_fields = {} # type:
|
|
401
|
+
self.fields = {} # type: dict[str, ma_fields.Field]
|
|
402
|
+
self.load_fields = {} # type: dict[str, ma_fields.Field]
|
|
403
|
+
self.dump_fields = {} # type: dict[str, ma_fields.Field]
|
|
402
404
|
self._init_fields()
|
|
403
405
|
messages = {}
|
|
404
406
|
messages.update(self._default_error_messages)
|
|
@@ -411,16 +413,19 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
411
413
|
return f"<{self.__class__.__name__}(many={self.many})>"
|
|
412
414
|
|
|
413
415
|
@property
|
|
414
|
-
def dict_class(self) -> type:
|
|
415
|
-
|
|
416
|
+
def dict_class(self) -> type[dict]:
|
|
417
|
+
if self.ordered:
|
|
418
|
+
return OrderedDict
|
|
419
|
+
else:
|
|
420
|
+
return dict
|
|
416
421
|
|
|
417
422
|
@classmethod
|
|
418
423
|
def from_dict(
|
|
419
424
|
cls,
|
|
420
|
-
fields: dict[str, ma_fields.Field | type],
|
|
425
|
+
fields: dict[str, ma_fields.Field | type[ma_fields.Field]],
|
|
421
426
|
*,
|
|
422
427
|
name: str = "GeneratedSchema",
|
|
423
|
-
) -> type:
|
|
428
|
+
) -> type[Schema]:
|
|
424
429
|
"""Generate a `Schema` class given a dictionary of fields.
|
|
425
430
|
|
|
426
431
|
.. code-block:: python
|
|
@@ -497,7 +502,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
497
502
|
return error.valid_data or missing
|
|
498
503
|
return value
|
|
499
504
|
|
|
500
|
-
def _serialize(self, obj:
|
|
505
|
+
def _serialize(self, obj: typing.Any, *, many: bool = False):
|
|
501
506
|
"""Serialize ``obj``.
|
|
502
507
|
|
|
503
508
|
:param obj: The object(s) to serialize.
|
|
@@ -508,10 +513,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
508
513
|
Renamed from ``marshal``.
|
|
509
514
|
"""
|
|
510
515
|
if many and obj is not None:
|
|
511
|
-
return [
|
|
512
|
-
self._serialize(d, many=False)
|
|
513
|
-
for d in typing.cast(typing.Iterable[_T], obj)
|
|
514
|
-
]
|
|
516
|
+
return [self._serialize(d, many=False) for d in obj]
|
|
515
517
|
ret = self.dict_class()
|
|
516
518
|
for attr_name, field_obj in self.dump_fields.items():
|
|
517
519
|
value = field_obj.serialize(attr_name, obj, accessor=self.get_attribute)
|
|
@@ -539,7 +541,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
539
541
|
Validation no longer occurs upon serialization.
|
|
540
542
|
"""
|
|
541
543
|
many = self.many if many is None else bool(many)
|
|
542
|
-
if self.
|
|
544
|
+
if self._hooks[PRE_DUMP]:
|
|
543
545
|
processed_obj = self._invoke_dump_processors(
|
|
544
546
|
PRE_DUMP, obj, many=many, original_data=obj
|
|
545
547
|
)
|
|
@@ -548,7 +550,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
548
550
|
|
|
549
551
|
result = self._serialize(processed_obj, many=many)
|
|
550
552
|
|
|
551
|
-
if self.
|
|
553
|
+
if self._hooks[POST_DUMP]:
|
|
552
554
|
result = self._invoke_dump_processors(
|
|
553
555
|
POST_DUMP, result, many=many, original_data=obj
|
|
554
556
|
)
|
|
@@ -584,7 +586,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
584
586
|
partial=None,
|
|
585
587
|
unknown=RAISE,
|
|
586
588
|
index=None,
|
|
587
|
-
) ->
|
|
589
|
+
) -> typing.Any | list[typing.Any]:
|
|
588
590
|
"""Deserialize ``data``.
|
|
589
591
|
|
|
590
592
|
:param dict data: The data to deserialize.
|
|
@@ -598,26 +600,24 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
598
600
|
fields in the data. Use `EXCLUDE`, `INCLUDE` or `RAISE`.
|
|
599
601
|
:param int index: Index of the item being serialized (for storing errors) if
|
|
600
602
|
serializing a collection, otherwise `None`.
|
|
601
|
-
:return:
|
|
603
|
+
:return: The deserialized data as `dict_class` instance or list of `dict_class`
|
|
604
|
+
instances if `many` is `True`.
|
|
602
605
|
"""
|
|
603
606
|
index_errors = self.opts.index_errors
|
|
604
607
|
index = index if index_errors else None
|
|
605
608
|
if many:
|
|
606
609
|
if not is_collection(data):
|
|
607
610
|
error_store.store_error([self.error_messages["type"]], index=index)
|
|
608
|
-
ret_l = []
|
|
611
|
+
ret_l = []
|
|
609
612
|
else:
|
|
610
613
|
ret_l = [
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
unknown=unknown,
|
|
619
|
-
index=idx,
|
|
620
|
-
),
|
|
614
|
+
self._deserialize(
|
|
615
|
+
typing.cast(dict, d),
|
|
616
|
+
error_store=error_store,
|
|
617
|
+
many=False,
|
|
618
|
+
partial=partial,
|
|
619
|
+
unknown=unknown,
|
|
620
|
+
index=idx,
|
|
621
621
|
)
|
|
622
622
|
for idx, d in enumerate(data)
|
|
623
623
|
]
|
|
@@ -802,7 +802,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
802
802
|
try:
|
|
803
803
|
self._do_load(data, many=many, partial=partial, postprocess=False)
|
|
804
804
|
except ValidationError as exc:
|
|
805
|
-
return typing.cast(
|
|
805
|
+
return typing.cast(dict[str, list[str]], exc.messages)
|
|
806
806
|
return {}
|
|
807
807
|
|
|
808
808
|
##### Private Helpers #####
|
|
@@ -846,7 +846,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
846
846
|
if partial is None:
|
|
847
847
|
partial = self.partial
|
|
848
848
|
# Run preprocessors
|
|
849
|
-
if self.
|
|
849
|
+
if self._hooks[PRE_LOAD]:
|
|
850
850
|
try:
|
|
851
851
|
processed_data = self._invoke_load_processors(
|
|
852
852
|
PRE_LOAD, data, many=many, original_data=data, partial=partial
|
|
@@ -870,7 +870,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
870
870
|
error_store=error_store, data=result, many=many
|
|
871
871
|
)
|
|
872
872
|
# Run schema-level validation
|
|
873
|
-
if self.
|
|
873
|
+
if self._hooks[VALIDATES_SCHEMA]:
|
|
874
874
|
field_errors = bool(error_store.errors)
|
|
875
875
|
self._invoke_schema_validators(
|
|
876
876
|
error_store=error_store,
|
|
@@ -892,7 +892,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
892
892
|
)
|
|
893
893
|
errors = error_store.errors
|
|
894
894
|
# Run post processors
|
|
895
|
-
if not errors and postprocess and self.
|
|
895
|
+
if not errors and postprocess and self._hooks[POST_LOAD]:
|
|
896
896
|
try:
|
|
897
897
|
result = self._invoke_load_processors(
|
|
898
898
|
POST_LOAD,
|
|
@@ -1055,9 +1055,6 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
1055
1055
|
raise error
|
|
1056
1056
|
self.on_bind_field(field_name, field_obj)
|
|
1057
1057
|
|
|
1058
|
-
def _has_processors(self, tag) -> bool:
|
|
1059
|
-
return bool(self._hooks[(tag, True)] or self._hooks[(tag, False)])
|
|
1060
|
-
|
|
1061
1058
|
def _invoke_dump_processors(
|
|
1062
1059
|
self, tag: str, data, *, many: bool, original_data=None
|
|
1063
1060
|
):
|
|
@@ -1102,9 +1099,8 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
1102
1099
|
return data
|
|
1103
1100
|
|
|
1104
1101
|
def _invoke_field_validators(self, *, error_store: ErrorStore, data, many: bool):
|
|
1105
|
-
for attr_name in self._hooks[VALIDATES]:
|
|
1102
|
+
for attr_name, _, validator_kwargs in self._hooks[VALIDATES]:
|
|
1106
1103
|
validator = getattr(self, attr_name)
|
|
1107
|
-
validator_kwargs = validator.__marshmallow_hook__[VALIDATES]
|
|
1108
1104
|
field_name = validator_kwargs["field_name"]
|
|
1109
1105
|
|
|
1110
1106
|
try:
|
|
@@ -1159,11 +1155,10 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
1159
1155
|
partial: bool | types.StrSequenceOrSet | None,
|
|
1160
1156
|
field_errors: bool = False,
|
|
1161
1157
|
):
|
|
1162
|
-
for attr_name in self._hooks[
|
|
1158
|
+
for attr_name, hook_many, validator_kwargs in self._hooks[VALIDATES_SCHEMA]:
|
|
1159
|
+
if hook_many != pass_many:
|
|
1160
|
+
continue
|
|
1163
1161
|
validator = getattr(self, attr_name)
|
|
1164
|
-
validator_kwargs = validator.__marshmallow_hook__[
|
|
1165
|
-
(VALIDATES_SCHEMA, pass_many)
|
|
1166
|
-
]
|
|
1167
1162
|
if field_errors and validator_kwargs["skip_on_field_errors"]:
|
|
1168
1163
|
continue
|
|
1169
1164
|
pass_original = validator_kwargs.get("pass_original", False)
|
|
@@ -1201,12 +1196,11 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
|
|
|
1201
1196
|
original_data=None,
|
|
1202
1197
|
**kwargs,
|
|
1203
1198
|
):
|
|
1204
|
-
|
|
1205
|
-
|
|
1199
|
+
for attr_name, hook_many, processor_kwargs in self._hooks[tag]:
|
|
1200
|
+
if hook_many != pass_many:
|
|
1201
|
+
continue
|
|
1206
1202
|
# This will be a bound method.
|
|
1207
1203
|
processor = getattr(self, attr_name)
|
|
1208
|
-
|
|
1209
|
-
processor_kwargs = processor.__marshmallow_hook__[key]
|
|
1210
1204
|
pass_original = processor_kwargs.get("pass_original", False)
|
|
1211
1205
|
|
|
1212
1206
|
if many and not pass_many:
|
|
@@ -4,14 +4,14 @@ import datetime as dt
|
|
|
4
4
|
import functools
|
|
5
5
|
import uuid
|
|
6
6
|
from enum import Enum, IntEnum
|
|
7
|
+
from zoneinfo import ZoneInfo
|
|
7
8
|
|
|
8
|
-
import pytz
|
|
9
9
|
import simplejson
|
|
10
10
|
|
|
11
11
|
from marshmallow import Schema, fields, missing, post_load, validate
|
|
12
12
|
from marshmallow.exceptions import ValidationError
|
|
13
13
|
|
|
14
|
-
central =
|
|
14
|
+
central = ZoneInfo("America/Chicago")
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class GenderEnum(IntEnum):
|
|
@@ -104,9 +104,7 @@ class User:
|
|
|
104
104
|
# A naive datetime
|
|
105
105
|
self.created = dt.datetime(2013, 11, 10, 14, 20, 58)
|
|
106
106
|
# A TZ-aware datetime
|
|
107
|
-
self.updated =
|
|
108
|
-
dt.datetime(2013, 11, 10, 14, 20, 58), is_dst=False
|
|
109
|
-
)
|
|
107
|
+
self.updated = dt.datetime(2013, 11, 10, 14, 20, 58, tzinfo=central)
|
|
110
108
|
self.id = id_
|
|
111
109
|
self.homepage = homepage
|
|
112
110
|
self.email = email
|
|
@@ -477,7 +477,7 @@ class TestFieldDeserialization:
|
|
|
477
477
|
),
|
|
478
478
|
(
|
|
479
479
|
"Sun, 10 Nov 2013 01:23:45 -0600",
|
|
480
|
-
|
|
480
|
+
dt.datetime(2013, 11, 10, 1, 23, 45, tzinfo=central),
|
|
481
481
|
True,
|
|
482
482
|
),
|
|
483
483
|
],
|
|
@@ -521,7 +521,7 @@ class TestFieldDeserialization:
|
|
|
521
521
|
),
|
|
522
522
|
(
|
|
523
523
|
"2013-11-10T01:23:45-06:00",
|
|
524
|
-
|
|
524
|
+
dt.datetime(2013, 11, 10, 1, 23, 45, tzinfo=central),
|
|
525
525
|
True,
|
|
526
526
|
),
|
|
527
527
|
],
|
|
@@ -262,3 +262,19 @@ class TestIncludeOption:
|
|
|
262
262
|
assert "email" in s._declared_fields.keys()
|
|
263
263
|
assert "from" in s._declared_fields.keys()
|
|
264
264
|
assert isinstance(s._declared_fields["from"], fields.Str)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
class TestManyOption:
|
|
268
|
+
class ManySchema(Schema):
|
|
269
|
+
foo = fields.Str()
|
|
270
|
+
|
|
271
|
+
class Meta:
|
|
272
|
+
many = True
|
|
273
|
+
|
|
274
|
+
def test_many_by_default(self):
|
|
275
|
+
test = self.ManySchema()
|
|
276
|
+
assert test.load([{"foo": "bar"}]) == [{"foo": "bar"}]
|
|
277
|
+
|
|
278
|
+
def test_explicit_single(self):
|
|
279
|
+
test = self.ManySchema(many=False)
|
|
280
|
+
assert test.load({"foo": "bar"}) == {"foo": "bar"}
|
|
@@ -565,7 +565,7 @@ class TestFieldSerialization:
|
|
|
565
565
|
"Sun, 10 Nov 2013 01:23:45 +0000",
|
|
566
566
|
),
|
|
567
567
|
(
|
|
568
|
-
|
|
568
|
+
dt.datetime(2013, 11, 10, 1, 23, 45, tzinfo=central),
|
|
569
569
|
"Sun, 10 Nov 2013 01:23:45 -0600",
|
|
570
570
|
),
|
|
571
571
|
],
|
|
@@ -586,7 +586,7 @@ class TestFieldSerialization:
|
|
|
586
586
|
),
|
|
587
587
|
(
|
|
588
588
|
"timestamp",
|
|
589
|
-
|
|
589
|
+
dt.datetime(2013, 11, 10, 0, 23, 45, tzinfo=central),
|
|
590
590
|
1384064625,
|
|
591
591
|
),
|
|
592
592
|
("timestamp_ms", dt.datetime(2013, 11, 10, 0, 23, 45), 1384043025000),
|
|
@@ -597,7 +597,7 @@ class TestFieldSerialization:
|
|
|
597
597
|
),
|
|
598
598
|
(
|
|
599
599
|
"timestamp_ms",
|
|
600
|
-
|
|
600
|
+
dt.datetime(2013, 11, 10, 0, 23, 45, tzinfo=central),
|
|
601
601
|
1384064625000,
|
|
602
602
|
),
|
|
603
603
|
],
|
|
@@ -620,7 +620,7 @@ class TestFieldSerialization:
|
|
|
620
620
|
"2013-11-10T01:23:45+00:00",
|
|
621
621
|
),
|
|
622
622
|
(
|
|
623
|
-
|
|
623
|
+
dt.datetime(2013, 11, 10, 1, 23, 45, tzinfo=central),
|
|
624
624
|
"2013-11-10T01:23:45-06:00",
|
|
625
625
|
),
|
|
626
626
|
],
|
|
@@ -124,7 +124,7 @@ def test_is_collection():
|
|
|
124
124
|
"Sun, 10 Nov 2013 01:23:45 +0000",
|
|
125
125
|
),
|
|
126
126
|
(
|
|
127
|
-
|
|
127
|
+
dt.datetime(2013, 11, 10, 1, 23, 45, tzinfo=central),
|
|
128
128
|
"Sun, 10 Nov 2013 01:23:45 -0600",
|
|
129
129
|
),
|
|
130
130
|
],
|
|
@@ -146,7 +146,7 @@ def test_rfc_format(value, expected):
|
|
|
146
146
|
"2013-11-10T01:23:45+00:00",
|
|
147
147
|
),
|
|
148
148
|
(
|
|
149
|
-
|
|
149
|
+
dt.datetime(2013, 11, 10, 1, 23, 45, tzinfo=central),
|
|
150
150
|
"2013-11-10T01:23:45-06:00",
|
|
151
151
|
),
|
|
152
152
|
],
|
|
@@ -165,7 +165,7 @@ def test_isoformat(value, expected):
|
|
|
165
165
|
),
|
|
166
166
|
(
|
|
167
167
|
"Sun, 10 Nov 2013 01:23:45 -0600",
|
|
168
|
-
|
|
168
|
+
dt.datetime(2013, 11, 10, 1, 23, 45, tzinfo=central),
|
|
169
169
|
),
|
|
170
170
|
],
|
|
171
171
|
)
|
|
@@ -194,7 +194,7 @@ def test_from_rfc(value, expected):
|
|
|
194
194
|
),
|
|
195
195
|
(
|
|
196
196
|
"2013-11-10T01:23:45-06:00",
|
|
197
|
-
|
|
197
|
+
dt.datetime(2013, 11, 10, 1, 23, 45, tzinfo=central),
|
|
198
198
|
),
|
|
199
199
|
],
|
|
200
200
|
)
|
|
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
|