From 19934b2b0cc83eef0830b9e2adb6c5301a7201fc Mon Sep 17 00:00:00 2001 From: Benjamin Schubert Date: Sat, 18 Nov 2023 09:07:00 +0000 Subject: [PATCH] Merge the AlwaysDispathPrettyPrinter into the now vendored PrettyPrinter We don't need to keep the separation anymore, and this will make it easier to extend --- src/_pytest/_io/pprint.py | 47 ++++++++++++++++----------------- src/_pytest/_io/saferepr.py | 49 ----------------------------------- src/_pytest/assertion/util.py | 7 ++--- testing/io/test_pprint.py | 8 ++++++ testing/io/test_saferepr.py | 7 ----- 5 files changed, 34 insertions(+), 84 deletions(-) create mode 100644 testing/io/test_pprint.py diff --git a/src/_pytest/_io/pprint.py b/src/_pytest/_io/pprint.py index 04f7edbbe..9923d0a62 100644 --- a/src/_pytest/_io/pprint.py +++ b/src/_pytest/_io/pprint.py @@ -121,31 +121,28 @@ class PrettyPrinter: self._recursive = True self._readable = False return - rep = self._repr(object, context, level) - max_width = self._width - indent - allowance - if len(rep) > max_width: - p = self._dispatch.get(type(object).__repr__, None) - if p is not None: - context[objid] = 1 - p(self, object, stream, indent, allowance, context, level + 1) - del context[objid] - return - elif ( - _dataclasses.is_dataclass(object) - and not isinstance(object, type) - and object.__dataclass_params__.repr - and - # Check dataclass has generated repr method. - hasattr(object.__repr__, "__wrapped__") - and "__create_fn__" in object.__repr__.__wrapped__.__qualname__ - ): - context[objid] = 1 - self._pprint_dataclass( - object, stream, indent, allowance, context, level + 1 - ) - del context[objid] - return - stream.write(rep) + + p = self._dispatch.get(type(object).__repr__, None) + if p is not None: + context[objid] = 1 + p(self, object, stream, indent, allowance, context, level + 1) + del context[objid] + elif ( + _dataclasses.is_dataclass(object) + and not isinstance(object, type) + and object.__dataclass_params__.repr + and + # Check dataclass has generated repr method. + hasattr(object.__repr__, "__wrapped__") + and "__create_fn__" in object.__repr__.__wrapped__.__qualname__ + ): + context[objid] = 1 + self._pprint_dataclass( + object, stream, indent, allowance, context, level + 1 + ) + del context[objid] + else: + stream.write(self._repr(object, context, level)) def _pprint_dataclass(self, object, stream, indent, allowance, context, level): cls_name = object.__class__.__name__ diff --git a/src/_pytest/_io/saferepr.py b/src/_pytest/_io/saferepr.py index ba8ea4302..c51578ed4 100644 --- a/src/_pytest/_io/saferepr.py +++ b/src/_pytest/_io/saferepr.py @@ -1,12 +1,7 @@ import pprint import reprlib -from typing import Any -from typing import Dict -from typing import IO from typing import Optional -from .pprint import PrettyPrinter - def _try_repr_or_str(obj: object) -> str: try: @@ -134,47 +129,3 @@ def saferepr_unlimited(obj: object, use_ascii: bool = True) -> str: return repr(obj) except Exception as exc: return _format_repr_exception(exc, obj) - - -class AlwaysDispatchingPrettyPrinter(PrettyPrinter): - """PrettyPrinter that always dispatches (regardless of width).""" - - def _format( - self, - object: object, - stream: IO[str], - indent: int, - allowance: int, - context: Dict[int, Any], - level: int, - ) -> None: - p = self._dispatch.get(type(object).__repr__, None) - - objid = id(object) - if objid in context or p is None: - super()._format( - object, - stream, - indent, - allowance, - context, - level, - ) - return - - context[objid] = 1 - p(self, object, stream, indent, allowance, context, level + 1) - del context[objid] - - -def _pformat_dispatch( - object: object, - indent: int = 1, - width: int = 80, - depth: Optional[int] = None, - *, - compact: bool = False, -) -> str: - return AlwaysDispatchingPrettyPrinter( - indent=indent, width=width, depth=depth, compact=compact - ).pformat(object) diff --git a/src/_pytest/assertion/util.py b/src/_pytest/assertion/util.py index 65abe8d23..4d9fd114b 100644 --- a/src/_pytest/assertion/util.py +++ b/src/_pytest/assertion/util.py @@ -16,7 +16,7 @@ from unicodedata import normalize import _pytest._code from _pytest import outcomes -from _pytest._io.saferepr import _pformat_dispatch +from _pytest._io.pprint import PrettyPrinter from _pytest._io.saferepr import saferepr from _pytest._io.saferepr import saferepr_unlimited from _pytest.config import Config @@ -348,8 +348,9 @@ def _compare_eq_iterable( lines_left = len(left_formatting) lines_right = len(right_formatting) if lines_left != lines_right: - left_formatting = _pformat_dispatch(left).splitlines() - right_formatting = _pformat_dispatch(right).splitlines() + printer = PrettyPrinter() + left_formatting = printer.pformat(left).splitlines() + right_formatting = printer.pformat(right).splitlines() if lines_left > 1 or lines_right > 1: _surrounding_parens_on_own_lines(left_formatting) diff --git a/testing/io/test_pprint.py b/testing/io/test_pprint.py new file mode 100644 index 000000000..900ccbbfa --- /dev/null +++ b/testing/io/test_pprint.py @@ -0,0 +1,8 @@ +from _pytest._io.pprint import PrettyPrinter + + +def test_pformat_dispatch(): + printer = PrettyPrinter(width=5) + assert printer.pformat("a") == "'a'" + assert printer.pformat("a" * 10) == "'aaaaaaaaaa'" + assert printer.pformat("foo bar") == "('foo '\n 'bar')" diff --git a/testing/io/test_saferepr.py b/testing/io/test_saferepr.py index 24746bc22..d94faa4f1 100644 --- a/testing/io/test_saferepr.py +++ b/testing/io/test_saferepr.py @@ -1,5 +1,4 @@ import pytest -from _pytest._io.saferepr import _pformat_dispatch from _pytest._io.saferepr import DEFAULT_REPR_MAX_SIZE from _pytest._io.saferepr import saferepr from _pytest._io.saferepr import saferepr_unlimited @@ -159,12 +158,6 @@ def test_unicode(): assert saferepr(val) == reprval -def test_pformat_dispatch(): - assert _pformat_dispatch("a") == "'a'" - assert _pformat_dispatch("a" * 10, width=5) == "'aaaaaaaaaa'" - assert _pformat_dispatch("foo bar", width=5) == "('foo '\n 'bar')" - - def test_broken_getattribute(): """saferepr() can create proper representations of classes with broken __getattribute__ (#7145)