From c6f04046e458c22bfc7d958a2e7616bcf3ed7cb4 Mon Sep 17 00:00:00 2001 From: Toluwalase Akinyemi Date: Tue, 13 Oct 2026 12:49:40 +0100 Subject: [PATCH] Pretty-print builtin frozendict in assertion diffs On Python 3.15+, register frozendict in PrettyPrinter._dispatch, reusing the OrderedDict printer since both repr as Name({...}). Previously it fell back to a single-line repr, making diffs hard to read. Closes #15172 --- AUTHORS | 1 + changelog/15172.bugfix.rst | 1 + src/_pytest/_io/pprint.py | 5 +++++ testing/io/test_pprint.py | 27 +++++++++++++++++++++++++++ 4 files changed, 34 insertions(+) create mode 100644 changelog/15172.bugfix.rst diff --git a/AUTHORS b/AUTHORS index bd1e27cb37d..4627a7d2c18 100644 --- a/AUTHORS +++ b/AUTHORS @@ -497,6 +497,7 @@ Tim Strazny TJ Bruno Tobias Diez Tobias Petersen +Toluwalase Akinyemi Tom Dalton Tom Most Tom Viner diff --git a/changelog/15172.bugfix.rst b/changelog/15172.bugfix.rst new file mode 100644 index 00000000000..2f97f57ffdb --- /dev/null +++ b/changelog/15172.bugfix.rst @@ -0,0 +1 @@ +Assertion diffs now pretty-print the built-in :class:`frozendict` (Python 3.15+) across multiple lines, the same way :class:`dict` is displayed. diff --git a/src/_pytest/_io/pprint.py b/src/_pytest/_io/pprint.py index a550b65b0c8..14fd6906ed7 100644 --- a/src/_pytest/_io/pprint.py +++ b/src/_pytest/_io/pprint.py @@ -29,6 +29,7 @@ from collections.abc import Iterator import dataclasses as _dataclasses import re +import sys import types as _types from typing import Any @@ -229,6 +230,10 @@ def _pprint_ordered_dict( _dispatch[_collections.OrderedDict.__repr__] = _pprint_ordered_dict + if sys.version_info >= (3, 15): + # frozendict's repr has the same shape as OrderedDict's: Name({...}) + _dispatch[frozendict.__repr__] = _pprint_ordered_dict # noqa: F821 + def _pprint_list( self, object: Any, diff --git a/testing/io/test_pprint.py b/testing/io/test_pprint.py index f7dbf748664..ca89368fbae 100644 --- a/testing/io/test_pprint.py +++ b/testing/io/test_pprint.py @@ -9,6 +9,7 @@ from collections import UserList from collections import UserString from dataclasses import dataclass +import sys import textwrap from types import MappingProxyType from types import SimpleNamespace @@ -651,6 +652,32 @@ class MyNamespace(SimpleNamespace): assert pp.pformat(MyNamespace(one=1)) == "MyNamespace(\n one=1,\n)" +@pytest.mark.skipif( + sys.version_info < (3, 15), reason="frozendict is a builtin since Python 3.15" +) +def test_frozendict() -> None: + if sys.version_info >= (3, 15): # for mypy, which checks against Python 3.10 + pp = PrettyPrinter() + assert pp.pformat(frozendict()) == "frozendict()" # noqa: F821 + assert pp.pformat(frozendict({"one": 1, "two": 2})) == textwrap.dedent( # noqa: F821 + """\ + frozendict({ + 'one': 1, + 'two': 2, + })""" + ) + + class MyFrozenDict(frozendict): # noqa: F821 + pass + + assert pp.pformat(MyFrozenDict({"one": 1})) == textwrap.dedent( + """\ + MyFrozenDict({ + 'one': 1, + })""" + ) + + def test_safe_tuple_sorts_unorderable_pairs() -> None: # ``_safe_tuple`` wraps each element of a 2-tuple in ``_safe_key`` so a # list of pairs with unorderable elements can be sorted without raising.