Repository: marshmallow-code/marshmallow
Pull request #1968 (2022-03-28 19:15:00)

Bump sphinx from 4.4.0 to 4.5.0
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 4.4.0 to 4.5.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/sphinx-doc/sphinx/releases">sphinx's releases</a>.</em></p>
<blockquote>
<h2>v4.5.0</h2>
<p>Changelog: <a href="https://www.sphinx-doc.org/en/master/changes.html">https://www.sphinx-doc.org/en/master/changes.html</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/sphinx-doc/sphinx/blob/4.x/CHANGES">sphinx's changelog</a>.</em></p>
<blockquote>
<h1>Release 4.5.0 (released Mar 28, 2022)</h1>
<h2>Incompatible changes</h2>
<ul>
<li><a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/10112">#10112</a>: extlinks: Disable hardcoded links detector by default</li>
<li><a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/9993">#9993</a>, <a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/10177">#10177</a>: std domain: Disallow to refer an inline target via
:rst:role:<code>ref</code> role</li>
</ul>
<h2>Deprecated</h2>
<ul>
<li><code>sphinx.ext.napoleon.docstring.GoogleDocstring._qualify_name()</code></li>
</ul>
<h2>Features added</h2>
<ul>
<li><a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/10260">#10260</a>: Enable <code>FORCE_COLOR</code> and <code>NO_COLOR</code> for terminal colouring</li>
<li><a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/10234">#10234</a>: autosummary: Add &quot;autosummary&quot; CSS class to summary tables</li>
<li><a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/10125">#10125</a>: extlinks: Improve suggestion message for a reference having title</li>
<li><a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/10112">#10112</a>: extlinks: Add :confval:<code>extlinks_detect_hardcoded_links</code> to enable
hardcoded links detector feature</li>
<li><a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/9494">#9494</a>, <a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/9456">#9456</a>: html search: Add a config variable
:confval:<code>html_show_search_summary</code> to enable/disable the search summaries</li>
<li><a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/9337">#9337</a>: HTML theme, add option <code>enable_search_shortcuts</code> that enables :kbd:'/' as
a Quick search shortcut and :kbd:<code>Esc</code> shortcut that
removes search highlighting.</li>
<li><a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/10107">#10107</a>: i18n: Allow to suppress translation warnings by adding <code>#noqa</code>
comment to the tail of each translation message</li>
<li><a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/10252">#10252</a>: C++, support attributes on classes, unions, and enums.</li>
<li><a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/10253">#10253</a>: :rst:dir:<code>pep</code> role now generates URLs based on peps.python.org</li>
</ul>
<h2>Bugs fixed</h2>
<ul>
<li><a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/9876">#9876</a>: autodoc: Failed to document an imported class that is built from native
binary module</li>
<li><a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/10133">#10133</a>: autodoc: Crashed when mocked module is used for type annotation</li>
<li><a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/10146">#10146</a>: autodoc: :confval:<code>autodoc_default_options</code> does not support
<code>no-value</code> option</li>
<li><a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/9971">#9971</a>: autodoc: TypeError is raised when the target object is annotated by
unhashable object</li>
<li><a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/10205">#10205</a>: extlinks: Failed to compile regexp on checking hardcoded links</li>
<li><a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/10277">#10277</a>: html search: Could not search short words (ex. &quot;use&quot;)</li>
<li><a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/9529">#9529</a>: LaTeX: named auto numbered footnote (ex. <code>[#named]</code>) that is referred
multiple times was rendered to a question mark</li>
<li><a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/9924">#9924</a>: LaTeX: multi-line :rst:dir:<code>cpp:function</code> directive has big vertical
spacing in Latexpdf</li>
<li><a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/10158">#10158</a>: LaTeX: excessive whitespace since v4.4.0 for undocumented</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/sphinx-doc/sphinx/commit/2329fdef8c20c6c75194f5d842b8f62ebad5c79d"><code>2329fde</code></a> Bump to 4.5.0 final</li>
<li><a href="https://github.com/sphinx-doc/sphinx/commit/c34444149d7703ae2bf415adbec97324a5842632"><code>c344441</code></a> Update CHANGES for PR <a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/10263">#10263</a></li>
<li><a href="https://github.com/sphinx-doc/sphinx/commit/746df6145239fd525056907deccb431f89bf8e41"><code>746df61</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/10263">#10263</a> from nicoa/escape_base_uri_in_extlinks</li>
<li><a href="https://github.com/sphinx-doc/sphinx/commit/81830cc77047ce39eab056a70b44a2d97848550c"><code>81830cc</code></a> Fix a flake8 warning</li>
<li><a href="https://github.com/sphinx-doc/sphinx/commit/aee4e42b81d56c57e1311176ce175ba3374baa0a"><code>aee4e42</code></a> extlink: Strip a leading backslash on compiling pattern</li>
<li><a href="https://github.com/sphinx-doc/sphinx/commit/8a1830ca36ddea80f8bcbc20c1090280a0a5197a"><code>8a1830c</code></a> Update CHANGES for PR <a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/10178">#10178</a></li>
<li><a href="https://github.com/sphinx-doc/sphinx/commit/4a496bfc98feced56e9e84eb6cf96264982d0e7a"><code>4a496bf</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/10178">#10178</a> from stephenfin/issue-10177</li>
<li><a href="https://github.com/sphinx-doc/sphinx/commit/c93b95d685aea6c8e4392d624e4668587b9e5726"><code>c93b95d</code></a> Merge CHANGES entry for 4.4.1 to 4.5.0</li>
<li><a href="https://github.com/sphinx-doc/sphinx/commit/a001bf47d66ae804a9a6e5d754de9b5eda4d0eb9"><code>a001bf4</code></a> Update CHANGES for PR <a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/10107">#10107</a></li>
<li><a href="https://github.com/sphinx-doc/sphinx/commit/b20e04968e73234da9fff7d19b12dfbeebebe944"><code>b20e049</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/sphinx-doc/sphinx/issues/10107">#10107</a> from Jean-Abou-Samra/intl-warnings</li>
<li>Additional commits viewable in <a href="https://github.com/sphinx-doc/sphinx/compare/v4.4.0...v4.5.0">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=sphinx&package-manager=pip&previous-version=4.4.0&new-version=4.5.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 4dac9da7..af119c26 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -15,7 +15,7 @@ repos:
   - id: flake8
     additional_dependencies: [flake8-bugbear==22.1.11]
 - repo: https://github.com/pre-commit/mirrors-mypy
-  rev: v0.942
+  rev: v0.960
   hooks:
   - id: mypy
     additional_dependencies: [types-simplejson, types-pytz, packaging]
diff --git a/AUTHORS.rst b/AUTHORS.rst
index a877bc46..7f557b7f 100644
--- a/AUTHORS.rst
+++ b/AUTHORS.rst
@@ -168,3 +168,4 @@ Contributors (chronological)
 - Ben Windsor `@bwindsor <https://github.com/bwindsor>`_
 - Kevin Kirsche `@kkirsche <https://github.com/kkirsche>`_
 - Isira Seneviratne `@Isira-Seneviratne <https://github.com/Isira-Seneviratne>`_
+- Karthikeyan Singaravelan `@tirkarthi  <https://github.com/tirkarthi>`_
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 7c8215b2..5fb2ba6d 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,6 +1,21 @@
 Changelog
 ---------
 
+(unreleased)
+************
+
+Features:
+
+- Raise `ValueError` if an invalid value is passed to the ``unknown`` argument (:issue:`1721`, :issue:`1732`).
+  Thanks :user:`sirosen` for the PR.
+
+Other changes:
+
+- Set lower bound for `packaging` requirement (:issue:`1957`).
+  Thanks :user:`MatthewNicolTR` for reporting and thanks :user:`sirosen` for the PR.
+- Improve warning messages by passing `stacklevel` (:pr:`1986`).
+  Thanks :user:`tirkarthi` for the PR.
+
 3.15.0 (2022-03-12)
 *******************
 
diff --git a/examples/peewee_example.py b/examples/peewee_example.py
index 408215b5..7e2bc55e 100644
--- a/examples/peewee_example.py
+++ b/examples/peewee_example.py
@@ -38,9 +38,6 @@ class Todo(BaseModel):
     user = pw.ForeignKeyField(User)
     posted_on = pw.DateTimeField()
 
-    class Meta:
-        order_by = ("-posted_on",)
-
 
 def create_tables():
     db.connect()
diff --git a/setup.py b/setup.py
index 96d83a85..ea448cf6 100644
--- a/setup.py
+++ b/setup.py
@@ -4,13 +4,13 @@ from setuptools import setup, find_packages
 EXTRAS_REQUIRE = {
     "tests": ["pytest", "pytz", "simplejson"],
     "lint": [
-        "mypy==0.942",
+        "mypy==0.960",
         "flake8==4.0.1",
-        "flake8-bugbear==22.3.23",
+        "flake8-bugbear==22.4.25",
         "pre-commit~=2.4",
     ],
     "docs": [
-        "sphinx==4.4.0",
+        "sphinx==4.5.0",
         "sphinx-issues==3.0.1",
         "alabaster==0.7.12",
         "sphinx-version-warning==1.1.2",
@@ -58,7 +58,7 @@ setup(
     package_dir={"": "src"},
     package_data={"marshmallow": ["py.typed"]},
     include_package_data=True,
-    install_requires=["packaging"],
+    install_requires=["packaging>=17.0"],
     extras_require=EXTRAS_REQUIRE,
     license="MIT",
     zip_safe=False,
diff --git a/src/marshmallow/fields.py b/src/marshmallow/fields.py
index 09df0d99..6b26126e 100644
--- a/src/marshmallow/fields.py
+++ b/src/marshmallow/fields.py
@@ -174,6 +174,7 @@ class Field(FieldABC):
                 "The 'default' argument to fields is deprecated. "
                 "Use 'dump_default' instead.",
                 RemovedInMarshmallow4Warning,
+                stacklevel=2,
             )
             if dump_default is missing_:
                 dump_default = default
@@ -182,6 +183,7 @@ class Field(FieldABC):
                 "The 'missing' argument to fields is deprecated. "
                 "Use 'load_default' instead.",
                 RemovedInMarshmallow4Warning,
+                stacklevel=2,
             )
             if load_default is missing_:
                 load_default = missing
@@ -220,6 +222,7 @@ class Field(FieldABC):
                 "explicit `metadata=...` argument instead. "
                 f"Additional metadata: {additional_metadata}",
                 RemovedInMarshmallow4Warning,
+                stacklevel=2,
             )
 
         self._creation_index = Field._creation_index
@@ -298,6 +301,7 @@ class Field(FieldABC):
                 key
             ),
             RemovedInMarshmallow4Warning,
+            stacklevel=2,
         )
         raise self.make_error(key=key, **kwargs)
 
@@ -439,6 +443,7 @@ class Field(FieldABC):
             "The 'default' attribute of fields is deprecated. "
             "Use 'dump_default' instead.",
             RemovedInMarshmallow4Warning,
+            stacklevel=2,
         )
         return self.dump_default
 
@@ -448,6 +453,7 @@ class Field(FieldABC):
             "The 'default' attribute of fields is deprecated. "
             "Use 'dump_default' instead.",
             RemovedInMarshmallow4Warning,
+            stacklevel=2,
         )
         self.dump_default = value
 
@@ -457,6 +463,7 @@ class Field(FieldABC):
             "The 'missing' attribute of fields is deprecated. "
             "Use 'load_default' instead.",
             RemovedInMarshmallow4Warning,
+            stacklevel=2,
         )
         return self.load_default
 
@@ -466,6 +473,7 @@ class Field(FieldABC):
             "The 'missing' attribute of fields is deprecated. "
             "Use 'load_default' instead.",
             RemovedInMarshmallow4Warning,
+            stacklevel=2,
         )
         self.load_default = value
 
@@ -550,6 +558,7 @@ class Nested(Field):
                 "Passing 'self' to `Nested` is deprecated. "
                 "Use `Nested(lambda: MySchema(...))` instead.",
                 RemovedInMarshmallow4Warning,
+                stacklevel=2,
             )
         self.nested = nested
         self.only = only
diff --git a/src/marshmallow/schema.py b/src/marshmallow/schema.py
index ff119a46..00e697e1 100644
--- a/src/marshmallow/schema.py
+++ b/src/marshmallow/schema.py
@@ -34,6 +34,7 @@ from marshmallow.utils import (
     get_value,
     is_collection,
     is_instance_or_subclass,
+    validate_unknown_parameter_value,
 )
 from marshmallow.warnings import RemovedInMarshmallow4Warning
 
@@ -227,7 +228,7 @@ class SchemaOpts:
         self.include = getattr(meta, "include", {})
         self.load_only = getattr(meta, "load_only", ())
         self.dump_only = getattr(meta, "dump_only", ())
-        self.unknown = getattr(meta, "unknown", RAISE)
+        self.unknown = validate_unknown_parameter_value(getattr(meta, "unknown", RAISE))
         self.register = getattr(meta, "register", True)
 
 
@@ -389,7 +390,11 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
         self.load_only = set(load_only) or set(self.opts.load_only)
         self.dump_only = set(dump_only) or set(self.opts.dump_only)
         self.partial = partial
-        self.unknown = unknown or self.opts.unknown
+        self.unknown = (
+            self.opts.unknown
+            if unknown is None
+            else validate_unknown_parameter_value(unknown)
+        )
         self.context = context or {}
         self._normalize_nested_options()
         #: Dictionary mapping field_names -> :class:`Field` objects
@@ -833,7 +838,11 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
         error_store = ErrorStore()
         errors = {}  # type: dict[str, list[str]]
         many = self.many if many is None else bool(many)
-        unknown = unknown or self.unknown
+        unknown = (
+            self.unknown
+            if unknown is None
+            else validate_unknown_parameter_value(unknown)
+        )
         if partial is None:
             partial = self.partial
         # Run preprocessors
diff --git a/src/marshmallow/utils.py b/src/marshmallow/utils.py
index 2aa6dfe1..fd651335 100644
--- a/src/marshmallow/utils.py
+++ b/src/marshmallow/utils.py
@@ -20,6 +20,7 @@ from marshmallow.warnings import RemovedInMarshmallow4Warning
 EXCLUDE = "exclude"
 INCLUDE = "include"
 RAISE = "raise"
+_UNKNOWN_VALUES = {EXCLUDE, INCLUDE, RAISE}
 
 
 class _Missing:
@@ -83,6 +84,7 @@ def pprint(obj, *args, **kwargs) -> None:
     warnings.warn(
         "marshmallow's pprint function is deprecated and will be removed in marshmallow 4.",
         RemovedInMarshmallow4Warning,
+        stacklevel=2,
     )
     if isinstance(obj, collections.OrderedDict):
         print(json.dumps(obj, *args, **kwargs))
@@ -333,3 +335,11 @@ def timedelta_to_microseconds(value: dt.timedelta) -> int:
     https://github.com/python/cpython/blob/bb3e0c240bc60fe08d332ff5955d54197f79751c/Lib/datetime.py#L665-L667  # noqa: B950
     """
     return (value.days * (24 * 3600) + value.seconds) * 1000000 + value.microseconds
+
+
+def validate_unknown_parameter_value(obj: typing.Any) -> str:
+    if obj not in _UNKNOWN_VALUES:
+        raise ValueError(
+            f"Object {obj!r} is not a valid value for the 'unknown' parameter"
+        )
+    return obj


Test patch:
diff --git a/tests/test_schema.py b/tests/test_schema.py
index c00189df..4943fbd8 100644
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -1,50 +1,47 @@
 import datetime as dt
 import decimal
-import random
 import math
-from collections import namedtuple, OrderedDict
-
-import simplejson as json
+import random
+from collections import OrderedDict, namedtuple
 
 import pytest
-
+import simplejson as json
 from marshmallow import (
+    EXCLUDE,
+    INCLUDE,
+    RAISE,
     Schema,
+    class_registry,
     fields,
     utils,
     validates,
     validates_schema,
-    EXCLUDE,
-    INCLUDE,
-    RAISE,
-    class_registry,
 )
 from marshmallow.exceptions import (
-    ValidationError,
-    StringNotCollectionError,
     RegistryError,
+    StringNotCollectionError,
+    ValidationError,
 )
 
 from tests.base import (
-    UserSchema,
-    UserMetaSchema,
-    UserRelativeUrlSchema,
-    ExtendedUserSchema,
-    UserIntSchema,
-    UserFloatStringSchema,
-    BlogSchema,
-    BlogUserMetaSchema,
+    Blog,
     BlogOnlySchema,
-    UserExcludeSchema,
-    UserAdditionalSchema,
+    BlogSchema,
     BlogSchemaExclude,
     BlogSchemaMeta,
+    BlogUserMetaSchema,
+    ExtendedUserSchema,
     User,
+    UserAdditionalSchema,
+    UserExcludeSchema,
+    UserFloatStringSchema,
+    UserIntSchema,
+    UserMetaSchema,
+    UserRelativeUrlSchema,
+    UserSchema,
     mockjson,
-    Blog,
 )
 
-
 random.seed(1)
 
 
@@ -2912,3 +2909,27 @@ def test_class_registry_returns_schema_type():
 
     SchemaClass = class_registry.get_class(DefinitelyUniqueSchema.__name__)
     assert SchemaClass is DefinitelyUniqueSchema
+
+
+@pytest.mark.parametrize("usage_location", ["meta", "init", "load"])
+def test_unknown_parameter_value_is_validated(usage_location):
+    class MySchema(Schema):
+        foo = fields.String()
+
+    with pytest.raises(
+        ValueError,
+        match="Object 'badval' is not a valid value for the 'unknown' parameter",
+    ):
+        # Meta.unknown setting gets caught at class creation time, since that's when
+        # metaclass __new__ runs
+        if usage_location == "meta":
+
+            class SubSchema(MySchema):
+                class Meta:
+                    unknown = "badval"
+
+        # usages in init and load are caught at call time, as expected
+        elif usage_location == "init":
+            MySchema(unknown="badval")
+        else:
+            MySchema().load({"foo": "bar"}, unknown="badval")