From 5289308a8254986e83b6d60b69d90eade31f1c0b Mon Sep 17 00:00:00 2001 From: Matt Clay Date: Tue, 1 Oct 2024 15:18:27 -0700 Subject: [PATCH 1/3] Fix pickle/copy support for the `missing` singleton * Fix pickle support for the `missing` singleton. * Add unit test for pickling the `missing` singleton. * Add unit test for copying the `missing` singleton. --- CHANGES.rst | 2 ++ src/jinja2/utils.py | 3 ++- tests/test_utils.py | 12 ++++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index f23b6c96f..0dfdfec5c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,6 +6,8 @@ Version 3.2.0 Unreleased - Drop support for Python 3.7. +- Fix pickle/copy support for the ``missing`` singleton. + :pr:`2029` - Use modern packaging metadata with ``pyproject.toml`` instead of ``setup.cfg``. :pr:`1793` - Use ``flit_core`` instead of ``setuptools`` as build backend. diff --git a/src/jinja2/utils.py b/src/jinja2/utils.py index 5c1ff5d7b..b16870554 100644 --- a/src/jinja2/utils.py +++ b/src/jinja2/utils.py @@ -19,7 +19,8 @@ F = t.TypeVar("F", bound=t.Callable[..., t.Any]) # special singleton representing missing values for the runtime -missing: t.Any = type("MissingType", (), {"__repr__": lambda x: "missing"})() +missing: t.Any = type("MissingType", (), {"__repr__": lambda x: "missing", + "__reduce__": lambda x: "missing"})() internal_code: t.MutableSet[CodeType] = set() diff --git a/tests/test_utils.py b/tests/test_utils.py index 7b58af144..86e0f0420 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,3 +1,4 @@ +import copy import pickle import random from collections import deque @@ -183,3 +184,14 @@ def test_consume(): consume(x) with pytest.raises(StopIteration): next(x) + + +@pytest.mark.parametrize("protocol", range(pickle.HIGHEST_PROTOCOL + 1)) +def test_pickle_missing(protocol: int) -> None: + """Test that missing can be pickled while remaining a singleton.""" + assert pickle.loads(pickle.dumps(missing, protocol)) is missing + + +def test_copy_missing() -> None: + """Test that missing can be copied while remaining a singleton.""" + assert copy.copy(missing) is missing From 0a87e81d37fb8bd1eb1b69ed9e80ac2216ce67b9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 22:29:03 +0000 Subject: [PATCH 2/3] [pre-commit.ci lite] apply automatic fixes --- src/jinja2/utils.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/jinja2/utils.py b/src/jinja2/utils.py index b16870554..3d0f2421d 100644 --- a/src/jinja2/utils.py +++ b/src/jinja2/utils.py @@ -19,8 +19,11 @@ F = t.TypeVar("F", bound=t.Callable[..., t.Any]) # special singleton representing missing values for the runtime -missing: t.Any = type("MissingType", (), {"__repr__": lambda x: "missing", - "__reduce__": lambda x: "missing"})() +missing: t.Any = type( + "MissingType", + (), + {"__repr__": lambda x: "missing", "__reduce__": lambda x: "missing"}, +)() internal_code: t.MutableSet[CodeType] = set() From d3dd9f22aa3d9374b9cb894d2176a83057453994 Mon Sep 17 00:00:00 2001 From: Matt Clay Date: Tue, 1 Oct 2024 17:01:52 -0700 Subject: [PATCH 3/3] Define `missing` using an actual class --- src/jinja2/utils.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/jinja2/utils.py b/src/jinja2/utils.py index 3d0f2421d..7b52fc03e 100644 --- a/src/jinja2/utils.py +++ b/src/jinja2/utils.py @@ -18,12 +18,17 @@ F = t.TypeVar("F", bound=t.Callable[..., t.Any]) -# special singleton representing missing values for the runtime -missing: t.Any = type( - "MissingType", - (), - {"__repr__": lambda x: "missing", "__reduce__": lambda x: "missing"}, -)() + +class _MissingType: + def __repr__(self) -> str: + return "missing" + + def __reduce__(self) -> str: + return "missing" + + +missing: t.Any = _MissingType() +"""Special singleton representing missing values for the runtime.""" internal_code: t.MutableSet[CodeType] = set()