diff --git a/AUTHORS b/AUTHORS index 347efad57b..4d34d2ba9b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -149,6 +149,7 @@ Evgeny Seliverstov Fabian Sturm Fabien Zarifian Fabio Zadrozny +Farbod Ahmadian faph Felix Hofstätter Felix Nieuwenhuizen diff --git a/changelog/389.improvement.rst b/changelog/389.improvement.rst new file mode 100644 index 0000000000..f8e2c19fde --- /dev/null +++ b/changelog/389.improvement.rst @@ -0,0 +1,38 @@ +The readability of assertion introspection of bound methods has been enhanced +-- by :user:`farbodahm`, :user:`webknjaz`, :user:`obestwalter`, :user:`flub` +and :user:`glyphack`. + +Earlier, it was like: + +.. code-block:: console + + =================================== FAILURES =================================== + _____________________________________ test _____________________________________ + + def test(): + > assert Help().fun() == 2 + E assert 1 == 2 + E + where 1 = >() + E + where > = .fun + E + where = Help() + + example.py:7: AssertionError + =========================== 1 failed in 0.03 seconds =========================== + + +And now it's like: + +.. code-block:: console + + =================================== FAILURES =================================== + _____________________________________ test _____________________________________ + + def test(): + > assert Help().fun() == 2 + E assert 1 == 2 + E + where 1 = fun() + E + where fun = .fun + E + where = Help() + + test_local.py:13: AssertionError + =========================== 1 failed in 0.03 seconds =========================== diff --git a/src/_pytest/_io/saferepr.py b/src/_pytest/_io/saferepr.py index 5ace418227..13b793f0a7 100644 --- a/src/_pytest/_io/saferepr.py +++ b/src/_pytest/_io/saferepr.py @@ -60,7 +60,6 @@ def repr(self, x: object) -> str: s = ascii(x) else: s = super().repr(x) - except (KeyboardInterrupt, SystemExit): raise except BaseException as exc: diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index 442fc5d9f3..bfcbcbd3f8 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -417,6 +417,10 @@ def _saferepr(obj: object) -> str: sequences, especially '\n{' and '\n}' are likely to be present in JSON reprs. """ + if isinstance(obj, types.MethodType): + # for bound methods, skip redundant information + return obj.__name__ + maxsize = _get_maxsize_for_saferepr(util._config) return saferepr(obj, maxsize=maxsize).replace("\n", "\\n") diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index 185cd5ef2e..5ee40ee656 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -10,6 +10,7 @@ import os from pathlib import Path import py_compile +import re import stat import sys import textwrap @@ -24,6 +25,7 @@ from _pytest.assertion import util from _pytest.assertion.rewrite import _get_assertion_exprs from _pytest.assertion.rewrite import _get_maxsize_for_saferepr +from _pytest.assertion.rewrite import _saferepr from _pytest.assertion.rewrite import AssertionRewritingHook from _pytest.assertion.rewrite import get_cache_dir from _pytest.assertion.rewrite import PYC_TAIL @@ -2036,7 +2038,9 @@ def test_foo(): assert test_foo_pyc.is_file() # normal file: not touched by pytest, normal cache tag - bar_init_pyc = get_cache_dir(bar_init) / f"__init__.{sys.implementation.cache_tag}.pyc" + bar_init_pyc = ( + get_cache_dir(bar_init) / f"__init__.{sys.implementation.cache_tag}.pyc" + ) assert bar_init_pyc.is_file() @@ -2103,3 +2107,26 @@ def test_foo(): ) result = pytester.runpytest() assert result.ret == 0 + + +class TestSafereprUnbounded: + class Help: + def bound_method(self): # pragma: no cover + pass + + def test_saferepr_bound_method(self): + """saferepr() of a bound method should show only the method name""" + assert _saferepr(self.Help().bound_method) == "bound_method" + + def test_saferepr_unbounded(self): + """saferepr() of an unbound method should still show the full information""" + obj = self.Help() + # using id() to fetch memory address fails on different platforms + pattern = re.compile( + rf"<{Path(__file__).stem}.{self.__class__.__name__}.Help object at 0x[0-9a-fA-F]*>", + ) + assert pattern.match(_saferepr(obj)) + assert ( + _saferepr(self.Help) + == f"" + )