marshmallow 4.0.0__tar.gz → 4.1.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.
Files changed (92) hide show
  1. {marshmallow-4.0.0 → marshmallow-4.1.0}/CHANGELOG.rst +24 -5
  2. {marshmallow-4.0.0 → marshmallow-4.1.0}/CONTRIBUTING.rst +1 -2
  3. {marshmallow-4.0.0 → marshmallow-4.1.0}/PKG-INFO +7 -7
  4. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/code_of_conduct.rst +3 -3
  5. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/conf.py +1 -2
  6. {marshmallow-4.0.0 → marshmallow-4.1.0}/pyproject.toml +8 -7
  7. {marshmallow-4.0.0 → marshmallow-4.1.0}/src/marshmallow/__init__.py +0 -1
  8. {marshmallow-4.0.0 → marshmallow-4.1.0}/src/marshmallow/constants.py +3 -0
  9. {marshmallow-4.0.0 → marshmallow-4.1.0}/src/marshmallow/decorators.py +17 -17
  10. {marshmallow-4.0.0 → marshmallow-4.1.0}/src/marshmallow/exceptions.py +0 -1
  11. {marshmallow-4.0.0 → marshmallow-4.1.0}/src/marshmallow/fields.py +12 -12
  12. {marshmallow-4.0.0 → marshmallow-4.1.0}/src/marshmallow/orderedset.py +1 -1
  13. {marshmallow-4.0.0 → marshmallow-4.1.0}/src/marshmallow/schema.py +7 -5
  14. {marshmallow-4.0.0 → marshmallow-4.1.0}/src/marshmallow/types.py +3 -10
  15. {marshmallow-4.0.0 → marshmallow-4.1.0}/src/marshmallow/utils.py +4 -11
  16. {marshmallow-4.0.0 → marshmallow-4.1.0}/tests/base.py +0 -11
  17. {marshmallow-4.0.0 → marshmallow-4.1.0}/tests/test_decorators.py +3 -3
  18. {marshmallow-4.0.0 → marshmallow-4.1.0}/tests/test_deserialization.py +12 -2
  19. {marshmallow-4.0.0 → marshmallow-4.1.0}/tests/test_registry.py +3 -3
  20. {marshmallow-4.0.0 → marshmallow-4.1.0}/tests/test_schema.py +1 -1
  21. {marshmallow-4.0.0 → marshmallow-4.1.0}/tests/test_serialization.py +2 -2
  22. {marshmallow-4.0.0 → marshmallow-4.1.0}/tests/test_utils.py +1 -1
  23. {marshmallow-4.0.0 → marshmallow-4.1.0}/tox.ini +1 -1
  24. {marshmallow-4.0.0 → marshmallow-4.1.0}/LICENSE +0 -0
  25. {marshmallow-4.0.0 → marshmallow-4.1.0}/NOTICE +0 -0
  26. {marshmallow-4.0.0 → marshmallow-4.1.0}/README.rst +0 -0
  27. {marshmallow-4.0.0 → marshmallow-4.1.0}/SECURITY.md +0 -0
  28. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/.gitignore +0 -0
  29. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/_static/apple-touch-icon.png +0 -0
  30. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/_static/custom.css +0 -0
  31. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/_static/favicon.ico +0 -0
  32. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/_static/marshmallow-logo-200.png +0 -0
  33. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/_static/marshmallow-logo-with-title-for-dark-theme.png +0 -0
  34. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/_static/marshmallow-logo-with-title.png +0 -0
  35. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/_static/marshmallow-logo.png +0 -0
  36. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/api_reference.rst +0 -0
  37. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/authors.rst +0 -0
  38. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/changelog.rst +0 -0
  39. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/contributing.rst +0 -0
  40. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/custom_fields.rst +0 -0
  41. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/dashing.json +0 -0
  42. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/donate.rst +0 -0
  43. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/examples/index.rst +0 -0
  44. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/examples/inflection.rst +0 -0
  45. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/examples/quotes_api.rst +0 -0
  46. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/examples/validating_package_json.rst +0 -0
  47. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/extending/custom_error_handling.rst +0 -0
  48. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/extending/custom_error_messages.rst +0 -0
  49. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/extending/custom_options.rst +0 -0
  50. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/extending/index.rst +0 -0
  51. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/extending/overriding_attribute_access.rst +0 -0
  52. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/extending/pre_and_post_processing_methods.rst +0 -0
  53. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/extending/schema_validation.rst +0 -0
  54. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/extending/using_original_input_data.rst +0 -0
  55. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/index.rst +0 -0
  56. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/install.rst +0 -0
  57. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/kudos.rst +0 -0
  58. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/license.rst +0 -0
  59. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/marshmallow.class_registry.rst +0 -0
  60. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/marshmallow.decorators.rst +0 -0
  61. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/marshmallow.error_store.rst +0 -0
  62. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/marshmallow.exceptions.rst +0 -0
  63. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/marshmallow.experimental.context.rst +0 -0
  64. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/marshmallow.fields.rst +0 -0
  65. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/marshmallow.schema.rst +0 -0
  66. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/marshmallow.types.rst +0 -0
  67. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/marshmallow.utils.rst +0 -0
  68. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/marshmallow.validate.rst +0 -0
  69. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/nesting.rst +0 -0
  70. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/quickstart.rst +0 -0
  71. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/top_level.rst +0 -0
  72. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/upgrading.rst +0 -0
  73. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/whos_using.rst +0 -0
  74. {marshmallow-4.0.0 → marshmallow-4.1.0}/docs/why.rst +0 -0
  75. {marshmallow-4.0.0 → marshmallow-4.1.0}/src/marshmallow/class_registry.py +0 -0
  76. {marshmallow-4.0.0 → marshmallow-4.1.0}/src/marshmallow/error_store.py +0 -0
  77. {marshmallow-4.0.0 → marshmallow-4.1.0}/src/marshmallow/experimental/__init__.py +0 -0
  78. {marshmallow-4.0.0 → marshmallow-4.1.0}/src/marshmallow/experimental/context.py +0 -0
  79. {marshmallow-4.0.0 → marshmallow-4.1.0}/src/marshmallow/py.typed +0 -0
  80. {marshmallow-4.0.0 → marshmallow-4.1.0}/src/marshmallow/validate.py +0 -0
  81. {marshmallow-4.0.0 → marshmallow-4.1.0}/tests/__init__.py +0 -0
  82. {marshmallow-4.0.0 → marshmallow-4.1.0}/tests/conftest.py +0 -0
  83. {marshmallow-4.0.0 → marshmallow-4.1.0}/tests/foo_serializer.py +0 -0
  84. {marshmallow-4.0.0 → marshmallow-4.1.0}/tests/mypy_test_cases/test_class_registry.py +0 -0
  85. {marshmallow-4.0.0 → marshmallow-4.1.0}/tests/mypy_test_cases/test_schema.py +0 -0
  86. {marshmallow-4.0.0 → marshmallow-4.1.0}/tests/mypy_test_cases/test_validation_error.py +0 -0
  87. {marshmallow-4.0.0 → marshmallow-4.1.0}/tests/test_context.py +0 -0
  88. {marshmallow-4.0.0 → marshmallow-4.1.0}/tests/test_error_store.py +0 -0
  89. {marshmallow-4.0.0 → marshmallow-4.1.0}/tests/test_exceptions.py +0 -0
  90. {marshmallow-4.0.0 → marshmallow-4.1.0}/tests/test_fields.py +0 -0
  91. {marshmallow-4.0.0 → marshmallow-4.1.0}/tests/test_options.py +0 -0
  92. {marshmallow-4.0.0 → marshmallow-4.1.0}/tests/test_validate.py +0 -0
@@ -1,6 +1,27 @@
1
1
  Changelog
2
2
  ---------
3
3
 
4
+ 4.1.0 (2025-11-01)
5
+ ++++++++++++++++++
6
+
7
+ Other changes:
8
+
9
+ - Add `__len__` implementation to `missing` so that it can be used with
10
+ `validate.Length <marshmallow.validate.Length>` (:pr:`2861`).
11
+ Thanks :user:`agentgodzilla` for the PR.
12
+ - Drop support for Python 3.9 (:pr:`2363`).
13
+ - Test against Python 3.14.
14
+
15
+
16
+ 4.0.1 (2025-08-28)
17
+ ++++++++++++++++++
18
+
19
+ Bug fixes:
20
+
21
+ - Fix wildcard import of ``from marshmallow import *`` (:pr:`2823`).
22
+ Thanks :user:`Florian-Laport` for the PR.
23
+
24
+
4
25
  4.0.0 (2025-04-16)
5
26
  ******************
6
27
 
@@ -22,16 +43,14 @@ Other changes:
22
43
 
23
44
  - Typing: `Field <marshmallow.fields.Field>` is now a generic type with a type argument for the internal value type.
24
45
  - `marshmallow.fields.UUID` no longer subclasses `marshmallow.fields.String`.
25
- - *Backwards-incompatible*: Use `datetime.date.fromisoformat`, `datetime.time.fromisoformat`, and `datetime.datetime.fromisoformat` from the standard library to deserialize dates, times and datetimes (:pr:`2078`).
26
- - `marshmallow.Schema.load` no longer silently fails to call schema validators when a generator is passed (:issue:`1898`).
46
+ - `marshmallow.Schema.load` no longer silently fails to call schema validators when a generator is passed (:issue:`1898`).
27
47
  The typing of `data` is also updated to be more accurate.
28
48
  Thanks :user:`ziplokk1` for reporting.
29
-
49
+ - *Backwards-incompatible*: Use `datetime.date.fromisoformat`, `datetime.time.fromisoformat`, and `datetime.datetime.fromisoformat` from the standard library to deserialize dates, times and datetimes (:pr:`2078`).
30
50
  As a consequence of this change:
31
51
  - Time with time offsets are now supported.
32
52
  - YYYY-MM-DD is now accepted as a datetime and deserialized as naive 00:00 AM.
33
53
  - `from_iso_date`, `from_iso_time` and `from_iso_datetime` are removed from `marshmallow.utils`.
34
-
35
54
  - Remove `isoformat`, `to_iso_time` and `to_iso_datetime` from `marshmallow.utils` (:pr:`2766`).
36
55
  - Remove `from_rfc`, and `rfcformat` from `marshmallow.utils` (:pr:`2767`).
37
56
  - Remove `is_keyed_tuple` from `marshmallow.utils` (:pr:`2768`).
@@ -152,7 +171,7 @@ Features:
152
171
 
153
172
  Bug fixes:
154
173
 
155
- - Respect ``data_key`` when schema validators raise a `ValidationError <marshmallow.exceptions.ValidationError>`
174
+ - Respect ``data_key`` when schema validators raise a `ValidationError <marshmallow.exceptions.ValidationError>`
156
175
  with a ``field_name`` argument (:issue:`2170`). Thanks :user:`matejsp` for reporting.
157
176
  - Correctly handle multiple `@post_load <marshmallow.post_load>` methods where one method appends to
158
177
  the data and another passes ``pass_original=True`` (:issue:`1755`).
@@ -26,8 +26,7 @@ Ways to contribute
26
26
 
27
27
  - Comment on some of marshmallow's `open issues <https://github.com/marshmallow-code/marshmallow/issues>`_ (especially those `labeled "feedback welcome" <https://github.com/marshmallow-code/marshmallow/issues?q=is%3Aopen+is%3Aissue+label%3A%22feedback+welcome%22>`_). Share a solution or workaround. Make a suggestion for how a feature can be made better. Opinions are welcome!
28
28
  - Improve `the docs <https://marshmallow.readthedocs.io>`_.
29
- For straightforward edits,
30
- click the ReadTheDocs menu button in the bottom-right corner of the page and click "Edit".
29
+ For straightforward edits, click the edit button in the top-right corner of the page.
31
30
  See the :ref:`Documentation <contributing_documentation>` section of this page if you want to build the docs locally.
32
31
  - If you think you've found a bug, `open an issue <https://github.com/marshmallow-code/marshmallow/issues>`_.
33
32
  - Contribute an :ref:`example usage <contributing_examples>` of marshmallow.
@@ -1,20 +1,20 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: marshmallow
3
- Version: 4.0.0
3
+ Version: 4.1.0
4
4
  Summary: A lightweight library for converting complex datatypes to and from native Python datatypes.
5
- Author-email: Steven Loria <sloria1@gmail.com>
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.9
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==2024.8.6 ; extra == "docs"
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.10.0 ; extra == "docs"
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 and young project, we don't yet have a Code of Conduct
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 `Steven Loria
142
- <https://github.com/sloria>`__, via `email <mailto:sloria1@gmail.com>`__
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.
@@ -37,7 +37,6 @@ html_theme_options = {
37
37
  "light_logo": "marshmallow-logo-with-title.png",
38
38
  "dark_logo": "marshmallow-logo-with-title-for-dark-theme.png",
39
39
  "source_repository": "https://github.com/marshmallow-code/marshmallow",
40
- "announcement": 'This is the documentation for the unreleased 4.0 version. The latest v3 docs are <a href="https://marshmallow.readthedocs.io/en/3.x-line/">here</a>.',
41
40
  "source_branch": "dev",
42
41
  "source_directory": "docs/",
43
42
  "sidebar_hide_name": True,
@@ -45,7 +44,7 @@ html_theme_options = {
45
44
  # Serif system font stack: https://systemfontstack.com/
46
45
  "font-stack": "Iowan Old Style, Apple Garamond, Baskerville, Times New Roman, Droid Serif, Times, Source Serif Pro, serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;",
47
46
  },
48
- "top_of_page_buttons": ["view"],
47
+ "top_of_page_buttons": ["view", "edit"],
49
48
  }
50
49
  pygments_dark_style = "lightbulb"
51
50
  html_favicon = "_static/favicon.ico"
@@ -1,12 +1,12 @@
1
1
  [project]
2
2
  name = "marshmallow"
3
- version = "4.0.0"
3
+ version = "4.1.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" }
7
- authors = [{ name = "Steven Loria", email = "sloria1@gmail.com" }]
7
+ authors = [{ name = "Steven Loria", email = "oss@stevenloria.com" }]
8
8
  maintainers = [
9
- { name = "Steven Loria", email = "sloria1@gmail.com" },
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.9"
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==2024.8.6",
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.10.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
@@ -23,7 +23,6 @@ __all__ = [
23
23
  "missing",
24
24
  "post_dump",
25
25
  "post_load",
26
- "pprint",
27
26
  "pre_dump",
28
27
  "pre_load",
29
28
  "validates",
@@ -18,5 +18,8 @@ class _Missing:
18
18
  def __repr__(self):
19
19
  return "<marshmallow.missing>"
20
20
 
21
+ def __len__(self):
22
+ return 0
23
+
21
24
 
22
25
  missing: typing.Final = _Missing()
@@ -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:
@@ -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
  """
@@ -1,4 +1,4 @@
1
- # ruff: noqa: F841, SLF001
1
+ # ruff: noqa: SLF001
2
2
  from __future__ import annotations
3
3
 
4
4
  import abc
@@ -530,7 +530,7 @@ class Nested(Field):
530
530
  else:
531
531
  nested = typing.cast("Schema", self.nested)
532
532
  # defer the import of `marshmallow.schema` to avoid circular imports
533
- from marshmallow.schema import Schema
533
+ from marshmallow.schema import Schema # noqa: PLC0415
534
534
 
535
535
  if isinstance(nested, dict):
536
536
  nested = Schema.from_dict(nested)
@@ -558,7 +558,7 @@ class Nested(Field):
558
558
  f"`Schema`, not {nested.__class__}."
559
559
  )
560
560
  else:
561
- schema_class = class_registry.get_class(nested, all=False)
561
+ schema_class = class_registry.get_class(nested, all=False) # type: ignore[unreachable]
562
562
  self._schema = schema_class(
563
563
  many=self.many,
564
564
  only=self.only,
@@ -591,7 +591,9 @@ class Nested(Field):
591
591
  raise self.make_error("type", input=value, type=value.__class__.__name__)
592
592
 
593
593
  def _load(
594
- self, value: typing.Any, partial: bool | types.StrSequenceOrSet | None = None
594
+ self,
595
+ value: typing.Any,
596
+ partial: bool | types.StrSequenceOrSet | None = None, # noqa: FBT001
595
597
  ):
596
598
  try:
597
599
  valid_data = self.schema.load(value, unknown=self.unknown, partial=partial)
@@ -606,7 +608,7 @@ class Nested(Field):
606
608
  value: typing.Any,
607
609
  attr: str | None,
608
610
  data: typing.Mapping[str, typing.Any] | None,
609
- partial: bool | types.StrSequenceOrSet | None = None,
611
+ partial: bool | types.StrSequenceOrSet | None = None, # noqa: FBT001
610
612
  **kwargs,
611
613
  ):
612
614
  """Same as :meth:`Field._deserialize` with additional ``partial`` argument.
@@ -683,7 +685,7 @@ class Pluck(Nested):
683
685
  return self._load(value, partial=partial)
684
686
 
685
687
 
686
- class List(Field[list[typing.Optional[_InternalT]]]):
688
+ class List(Field[list[_InternalT | None]]):
687
689
  """A list field, composed with another `Field` class or
688
690
  instance.
689
691
 
@@ -814,7 +816,7 @@ class Tuple(Field[tuple]):
814
816
 
815
817
  return tuple(
816
818
  field._serialize(each, attr, obj, **kwargs)
817
- for field, each in zip(self.tuple_fields, value)
819
+ for field, each in zip(self.tuple_fields, value, strict=True)
818
820
  )
819
821
 
820
822
  def _deserialize(
@@ -832,7 +834,7 @@ class Tuple(Field[tuple]):
832
834
  result = []
833
835
  errors = {}
834
836
 
835
- 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)):
836
838
  try:
837
839
  result.append(field.deserialize(each, **kwargs))
838
840
  except ValidationError as error:
@@ -1743,7 +1745,7 @@ class Email(String):
1743
1745
  self.validators.insert(0, validator)
1744
1746
 
1745
1747
 
1746
- class IP(Field[typing.Union[ipaddress.IPv4Address, ipaddress.IPv6Address]]):
1748
+ class IP(Field[ipaddress.IPv4Address | ipaddress.IPv6Address]):
1747
1749
  """A IP address field.
1748
1750
 
1749
1751
  :param exploded: If `True`, serialize ipv6 address in long form, ie. with groups
@@ -1800,9 +1802,7 @@ class IPv6(IP):
1800
1802
  DESERIALIZATION_CLASS = ipaddress.IPv6Address
1801
1803
 
1802
1804
 
1803
- class IPInterface(
1804
- Field[typing.Union[ipaddress.IPv4Interface, ipaddress.IPv6Interface]]
1805
- ):
1805
+ class IPInterface(Field[ipaddress.IPv4Interface | ipaddress.IPv6Interface]):
1806
1806
  """A IPInterface field.
1807
1807
 
1808
1808
  IP interface is the non-strict form of the IPNetwork type where arbitrary host
@@ -23,7 +23,7 @@
23
23
  from collections.abc import MutableSet
24
24
 
25
25
 
26
- class OrderedSet(MutableSet):
26
+ class OrderedSet(MutableSet): # noqa: PLW1641
27
27
  def __init__(self, iterable=None):
28
28
  self.end = end = []
29
29
  end += [None, end, end] # sentinel node for doubly linked list
@@ -550,7 +550,7 @@ class Schema(metaclass=SchemaMeta):
550
550
  :return: Serialized data
551
551
 
552
552
  .. versionchanged:: 3.0.0b7
553
- This method returns the serialized data rather than a ``(data, errors)`` duple.
553
+ This method returns the serialized data rather than a ``(data, errors)`` tuple.
554
554
  A :exc:`ValidationError <marshmallow.exceptions.ValidationError>` is raised
555
555
  if ``obj`` is invalid.
556
556
  .. versionchanged:: 3.0.0rc9
@@ -582,7 +582,7 @@ class Schema(metaclass=SchemaMeta):
582
582
  :return: A ``json`` string
583
583
 
584
584
  .. versionchanged:: 3.0.0b7
585
- This method returns the serialized data rather than a ``(data, errors)`` duple.
585
+ This method returns the serialized data rather than a ``(data, errors)`` tuple.
586
586
  A :exc:`ValidationError <marshmallow.exceptions.ValidationError>` is raised
587
587
  if ``obj`` is invalid.
588
588
  """
@@ -723,7 +723,7 @@ class Schema(metaclass=SchemaMeta):
723
723
  :return: Deserialized data
724
724
 
725
725
  .. versionchanged:: 3.0.0b7
726
- This method returns the deserialized data rather than a ``(data, errors)`` duple.
726
+ This method returns the deserialized data rather than a ``(data, errors)`` tuple.
727
727
  A :exc:`ValidationError <marshmallow.exceptions.ValidationError>` is raised
728
728
  if invalid data are passed.
729
729
  """
@@ -757,7 +757,7 @@ class Schema(metaclass=SchemaMeta):
757
757
  :return: Deserialized data
758
758
 
759
759
  .. versionchanged:: 3.0.0b7
760
- This method returns the deserialized data rather than a ``(data, errors)`` duple.
760
+ This method returns the deserialized data rather than a ``(data, errors)`` tuple.
761
761
  A :exc:`ValidationError <marshmallow.exceptions.ValidationError>` is raised
762
762
  if invalid data are passed.
763
763
  .. versionchanged:: 4.0.0
@@ -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(zip(data, original_data)):
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.Union[
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):
@@ -1,6 +1,5 @@
1
1
  """Utility methods for marshmallow."""
2
2
 
3
- # ruff: noqa: T201, T203
4
3
  from __future__ import annotations
5
4
 
6
5
  import datetime as dt
@@ -8,31 +7,25 @@ import inspect
8
7
  import typing
9
8
  from collections.abc import Mapping, Sequence
10
9
 
11
- # Remove when we drop Python 3.9
12
- try:
13
- from typing import TypeGuard
14
- except ImportError:
15
- from typing_extensions import TypeGuard
16
-
17
10
  from marshmallow.constants import missing
18
11
 
19
12
 
20
- def is_generator(obj) -> TypeGuard[typing.Generator]:
13
+ def is_generator(obj) -> typing.TypeGuard[typing.Generator]:
21
14
  """Return True if ``obj`` is a generator"""
22
15
  return inspect.isgeneratorfunction(obj) or inspect.isgenerator(obj)
23
16
 
24
17
 
25
- def is_iterable_but_not_string(obj) -> TypeGuard[typing.Iterable]:
18
+ def is_iterable_but_not_string(obj) -> typing.TypeGuard[typing.Iterable]:
26
19
  """Return True if ``obj`` is an iterable object that isn't a string."""
27
20
  return (hasattr(obj, "__iter__") and not hasattr(obj, "strip")) or is_generator(obj)
28
21
 
29
22
 
30
- def is_sequence_but_not_string(obj) -> TypeGuard[Sequence]:
23
+ def is_sequence_but_not_string(obj) -> typing.TypeGuard[Sequence]:
31
24
  """Return True if ``obj`` is a sequence that isn't a string."""
32
25
  return isinstance(obj, Sequence) and not isinstance(obj, (str, bytes))
33
26
 
34
27
 
35
- def is_collection(obj) -> TypeGuard[typing.Iterable]:
28
+ def is_collection(obj) -> typing.TypeGuard[typing.Iterable]:
36
29
  """Return True if ``obj`` is a collection type, e.g list, tuple, queryset."""
37
30
  return is_iterable_but_not_string(obj) and not isinstance(obj, Mapping)
38
31
 
@@ -161,17 +161,6 @@ class Blog:
161
161
  return item.name in [each.name for each in self.collaborators]
162
162
 
163
163
 
164
- class DummyModel:
165
- def __init__(self, foo):
166
- self.foo = foo
167
-
168
- def __eq__(self, other):
169
- return self.foo == other.foo
170
-
171
- def __str__(self):
172
- return f"bar {self.foo}"
173
-
174
-
175
164
  ###### Schemas #####
176
165
 
177
166
 
@@ -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:
@@ -234,7 +234,7 @@ def test_decorated_processor_inheritance():
234
234
  item["overridden"] = "overridden"
235
235
  return item
236
236
 
237
- deleted = None
237
+ deleted = None # type: ignore[assignment]
238
238
 
239
239
  parent_dumped = ParentSchema().dump({})
240
240
  assert parent_dumped == {
@@ -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(result, (dt.datetime, int), (dtime, 42)):
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
 
@@ -189,7 +189,7 @@ class FooSerializer(Schema):
189
189
 
190
190
  def test_multiple_classes_with_same_name_raises_error():
191
191
  # Import a class with the same name
192
- from .foo_serializer import FooSerializer as FooSerializer1 # noqa: F401
192
+ from .foo_serializer import FooSerializer as FooSerializer1 # noqa: PLC0415, F401
193
193
 
194
194
  class MySchema(Schema):
195
195
  foo = fields.Nested("FooSerializer")
@@ -204,14 +204,14 @@ def test_multiple_classes_with_same_name_raises_error():
204
204
 
205
205
  def test_multiple_classes_with_all():
206
206
  # Import a class with the same name
207
- from .foo_serializer import FooSerializer as FooSerializer1 # noqa: F401
207
+ from .foo_serializer import FooSerializer as FooSerializer1 # noqa: PLC0415, F401
208
208
 
209
209
  classes = class_registry.get_class("FooSerializer", all=True)
210
210
  assert len(classes) == 2
211
211
 
212
212
 
213
213
  def test_can_use_full_module_path_to_class():
214
- from .foo_serializer import FooSerializer as FooSerializer1 # noqa: F401
214
+ from .foo_serializer import FooSerializer as FooSerializer1 # noqa: PLC0415, F401
215
215
 
216
216
  # Using full paths is ok
217
217
 
@@ -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
@@ -438,7 +438,7 @@ class TestFieldSerialization:
438
438
  )
439
439
 
440
440
  def test_structured_dict_value_serialize(self, user):
441
- user.various_data = {"foo": decimal.Decimal("1")}
441
+ user.various_data = {"foo": decimal.Decimal(1)}
442
442
  field = fields.Dict(values=fields.Decimal)
443
443
  assert field.serialize("various_data", user) == {"foo": 1}
444
444
 
@@ -448,7 +448,7 @@ class TestFieldSerialization:
448
448
  assert field.serialize("various_data", user) == {"1": "bar"}
449
449
 
450
450
  def test_structured_dict_key_value_serialize(self, user):
451
- user.various_data = {1: decimal.Decimal("1")}
451
+ user.various_data = {1: decimal.Decimal(1)}
452
452
  field = fields.Dict(keys=fields.Str, values=fields.Decimal)
453
453
  assert field.serialize("various_data", user) == {"1": 1}
454
454
 
@@ -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
 
@@ -1,5 +1,5 @@
1
1
  [tox]
2
- envlist = lint,mypy,py{39,310,311,312,313},docs
2
+ envlist = lint,mypy,py{310,311,312,313,314},docs
3
3
 
4
4
  [testenv]
5
5
  extras = tests
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