marshmallow 4.1.0__tar.gz → 4.1.2__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.1.0 → marshmallow-4.1.2}/CHANGELOG.rst +17 -2
- {marshmallow-4.1.0 → marshmallow-4.1.2}/PKG-INFO +1 -1
- {marshmallow-4.1.0 → marshmallow-4.1.2}/pyproject.toml +1 -1
- {marshmallow-4.1.0 → marshmallow-4.1.2}/src/marshmallow/error_store.py +23 -12
- {marshmallow-4.1.0 → marshmallow-4.1.2}/src/marshmallow/validate.py +2 -3
- {marshmallow-4.1.0 → marshmallow-4.1.2}/tests/test_error_store.py +17 -1
- {marshmallow-4.1.0 → marshmallow-4.1.2}/tests/test_validate.py +8 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/CONTRIBUTING.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/LICENSE +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/NOTICE +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/README.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/SECURITY.md +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/.gitignore +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/_static/apple-touch-icon.png +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/_static/custom.css +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/_static/favicon.ico +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/_static/marshmallow-logo-200.png +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/_static/marshmallow-logo-with-title-for-dark-theme.png +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/_static/marshmallow-logo-with-title.png +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/_static/marshmallow-logo.png +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/api_reference.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/authors.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/changelog.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/code_of_conduct.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/conf.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/contributing.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/custom_fields.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/dashing.json +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/donate.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/examples/index.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/examples/inflection.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/examples/quotes_api.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/examples/validating_package_json.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/extending/custom_error_handling.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/extending/custom_error_messages.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/extending/custom_options.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/extending/index.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/extending/overriding_attribute_access.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/extending/pre_and_post_processing_methods.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/extending/schema_validation.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/extending/using_original_input_data.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/index.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/install.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/kudos.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/license.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/marshmallow.class_registry.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/marshmallow.decorators.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/marshmallow.error_store.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/marshmallow.exceptions.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/marshmallow.experimental.context.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/marshmallow.fields.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/marshmallow.schema.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/marshmallow.types.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/marshmallow.utils.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/marshmallow.validate.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/nesting.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/quickstart.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/top_level.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/upgrading.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/whos_using.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/docs/why.rst +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/src/marshmallow/__init__.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/src/marshmallow/class_registry.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/src/marshmallow/constants.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/src/marshmallow/decorators.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/src/marshmallow/exceptions.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/src/marshmallow/experimental/__init__.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/src/marshmallow/experimental/context.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/src/marshmallow/fields.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/src/marshmallow/orderedset.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/src/marshmallow/py.typed +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/src/marshmallow/schema.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/src/marshmallow/types.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/src/marshmallow/utils.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/tests/__init__.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/tests/base.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/tests/conftest.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/tests/foo_serializer.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/tests/mypy_test_cases/test_class_registry.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/tests/mypy_test_cases/test_schema.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/tests/mypy_test_cases/test_validation_error.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/tests/test_context.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/tests/test_decorators.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/tests/test_deserialization.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/tests/test_exceptions.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/tests/test_fields.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/tests/test_options.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/tests/test_registry.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/tests/test_schema.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/tests/test_serialization.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/tests/test_utils.py +0 -0
- {marshmallow-4.1.0 → marshmallow-4.1.2}/tox.ini +0 -0
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
Changelog
|
|
2
2
|
---------
|
|
3
3
|
|
|
4
|
+
4.1.2 (2025-12-19)
|
|
5
|
+
++++++++++++++++++
|
|
6
|
+
|
|
7
|
+
Bug fixes:
|
|
8
|
+
|
|
9
|
+
- :cve:`CVE-2025-68480`: Merge error store messages without rebuilding collections.
|
|
10
|
+
Thanks 카푸치노 for reporting and :user:`deckar01` for the fix.
|
|
11
|
+
|
|
12
|
+
4.1.1 (2025-11-05)
|
|
13
|
+
++++++++++++++++++
|
|
14
|
+
|
|
15
|
+
Bug fixes:
|
|
16
|
+
|
|
17
|
+
- Ensure ``URL`` validator is case-insensitive when using custom schemes (:pr:`2874`).
|
|
18
|
+
Thanks :user:`T90REAL` for the PR.
|
|
19
|
+
|
|
4
20
|
4.1.0 (2025-11-01)
|
|
5
21
|
++++++++++++++++++
|
|
6
22
|
|
|
@@ -10,8 +26,7 @@ Other changes:
|
|
|
10
26
|
`validate.Length <marshmallow.validate.Length>` (:pr:`2861`).
|
|
11
27
|
Thanks :user:`agentgodzilla` for the PR.
|
|
12
28
|
- Drop support for Python 3.9 (:pr:`2363`).
|
|
13
|
-
- Test against Python 3.14.
|
|
14
|
-
|
|
29
|
+
- Test against Python 3.14 (:pr:`2864`).
|
|
15
30
|
|
|
16
31
|
4.0.1 (2025-08-28)
|
|
17
32
|
++++++++++++++++++
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: marshmallow
|
|
3
|
-
Version: 4.1.
|
|
3
|
+
Version: 4.1.2
|
|
4
4
|
Summary: A lightweight library for converting complex datatypes to and from native Python datatypes.
|
|
5
5
|
Author-email: Steven Loria <oss@stevenloria.com>
|
|
6
6
|
Maintainer-email: Steven Loria <oss@stevenloria.com>, Jérôme Lafréchoux <jerome@jolimont.fr>, Jared Deckard <jared@shademaps.com>
|
|
@@ -18,6 +18,7 @@ class ErrorStore:
|
|
|
18
18
|
# field error -> store/merge error messages under field name key
|
|
19
19
|
# schema error -> if string or list, store/merge under _schema key
|
|
20
20
|
# -> if dict, store/merge with other top-level keys
|
|
21
|
+
messages = copy_containers(messages)
|
|
21
22
|
if field_name != SCHEMA or not isinstance(messages, dict):
|
|
22
23
|
messages = {field_name: messages}
|
|
23
24
|
if index is not None:
|
|
@@ -25,6 +26,14 @@ class ErrorStore:
|
|
|
25
26
|
self.errors = merge_errors(self.errors, messages)
|
|
26
27
|
|
|
27
28
|
|
|
29
|
+
def copy_containers(errors):
|
|
30
|
+
if isinstance(errors, list):
|
|
31
|
+
return [copy_containers(val) for val in errors]
|
|
32
|
+
if isinstance(errors, dict):
|
|
33
|
+
return {key: copy_containers(val) for key, val in errors.items()}
|
|
34
|
+
return errors
|
|
35
|
+
|
|
36
|
+
|
|
28
37
|
def merge_errors(errors1, errors2): # noqa: PLR0911
|
|
29
38
|
"""Deeply merge two error messages.
|
|
30
39
|
|
|
@@ -37,24 +46,26 @@ def merge_errors(errors1, errors2): # noqa: PLR0911
|
|
|
37
46
|
return errors1
|
|
38
47
|
if isinstance(errors1, list):
|
|
39
48
|
if isinstance(errors2, list):
|
|
40
|
-
|
|
49
|
+
errors1.extend(errors2)
|
|
50
|
+
return errors1
|
|
41
51
|
if isinstance(errors2, dict):
|
|
42
|
-
|
|
43
|
-
|
|
52
|
+
errors2[SCHEMA] = merge_errors(errors1, errors2.get(SCHEMA))
|
|
53
|
+
return errors2
|
|
54
|
+
errors1.append(errors2)
|
|
55
|
+
return errors1
|
|
44
56
|
if isinstance(errors1, dict):
|
|
45
|
-
if isinstance(errors2, list):
|
|
46
|
-
return dict(errors1, **{SCHEMA: merge_errors(errors1.get(SCHEMA), errors2)})
|
|
47
57
|
if isinstance(errors2, dict):
|
|
48
|
-
errors = dict(errors1)
|
|
49
58
|
for key, val in errors2.items():
|
|
50
|
-
if key in
|
|
51
|
-
|
|
59
|
+
if key in errors1:
|
|
60
|
+
errors1[key] = merge_errors(errors1[key], val)
|
|
52
61
|
else:
|
|
53
|
-
|
|
54
|
-
return
|
|
55
|
-
|
|
62
|
+
errors1[key] = val
|
|
63
|
+
return errors1
|
|
64
|
+
errors1[SCHEMA] = merge_errors(errors1.get(SCHEMA), errors2)
|
|
65
|
+
return errors1
|
|
56
66
|
if isinstance(errors2, list):
|
|
57
67
|
return [errors1, *errors2]
|
|
58
68
|
if isinstance(errors2, dict):
|
|
59
|
-
|
|
69
|
+
errors2[SCHEMA] = merge_errors(errors1, errors2.get(SCHEMA))
|
|
70
|
+
return errors2
|
|
60
71
|
return [errors1, errors2]
|
|
@@ -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="")
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import NamedTuple
|
|
2
2
|
|
|
3
3
|
from marshmallow import missing
|
|
4
|
-
from marshmallow.error_store import merge_errors
|
|
4
|
+
from marshmallow.error_store import ErrorStore, merge_errors
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
def test_missing_is_falsy():
|
|
@@ -149,3 +149,19 @@ class TestMergeErrors:
|
|
|
149
149
|
assert merge_errors(
|
|
150
150
|
{"field1": {"field2": "error1"}}, {"field1": {"field2": "error2"}}
|
|
151
151
|
) == {"field1": {"field2": ["error1", "error2"]}}
|
|
152
|
+
|
|
153
|
+
def test_list_not_changed(self):
|
|
154
|
+
store = ErrorStore()
|
|
155
|
+
message = ["foo"]
|
|
156
|
+
store.store_error(message)
|
|
157
|
+
store.store_error(message)
|
|
158
|
+
assert message == ["foo"]
|
|
159
|
+
assert store.errors == {"_schema": ["foo", "foo"]}
|
|
160
|
+
|
|
161
|
+
def test_dict_not_changed(self):
|
|
162
|
+
store = ErrorStore()
|
|
163
|
+
message = {"foo": ["bar"]}
|
|
164
|
+
store.store_error(message)
|
|
165
|
+
store.store_error(message)
|
|
166
|
+
assert message == {"foo": ["bar"]}
|
|
167
|
+
assert store.errors == {"foo": ["bar", "bar"]}
|
|
@@ -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.1.0 → marshmallow-4.1.2}/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
|
|
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
|