Skip to content

Commit

Permalink
improve -vv repr, fix pytest-dev#9742
Browse files Browse the repository at this point in the history
  • Loading branch information
samuelcolvin committed Mar 8, 2022
1 parent 4a8f8ad commit 3c629dc
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 4 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Expand Up @@ -289,6 +289,7 @@ Ruaridh Williamson
Russel Winder
Ryan Wooden
Saiprasad Kale
Samuel Colvin
Samuel Dion-Girardeau
Samuel Searles-Bryant
Samuele Pedroni
Expand Down
1 change: 1 addition & 0 deletions changelog/9742.improvement.rst
@@ -0,0 +1 @@
Display assertion message without escaped newline characters with ``-vv``.
1 change: 1 addition & 0 deletions extra/setup-py.test/setup.py
@@ -1,4 +1,5 @@
import sys

from distutils.core import setup

if __name__ == "__main__":
Expand Down
14 changes: 14 additions & 0 deletions src/_pytest/_io/saferepr.py
Expand Up @@ -107,6 +107,20 @@ def saferepr(obj: object, maxsize: Optional[int] = DEFAULT_REPR_MAX_SIZE) -> str
return SafeRepr(maxsize).repr(obj)


def saferepr_unlimited(obj: object) -> str:
"""Return an unlimited-size safe repr-string for the given object.
As with saferepr, failing __repr__ functions of user instances
will be represented with a short exception info.
This function is a wrapper around simple repr.
"""
try:
return repr(obj)
except Exception as exc:
return _format_repr_exception(exc, obj)


class AlwaysDispatchingPrettyPrinter(pprint.PrettyPrinter):
"""PrettyPrinter that always dispatches (regardless of width)."""

Expand Down
6 changes: 3 additions & 3 deletions src/_pytest/assertion/util.py
Expand Up @@ -14,7 +14,7 @@
import _pytest._code
from _pytest import outcomes
from _pytest._io.saferepr import _pformat_dispatch
from _pytest._io.saferepr import safeformat
from _pytest._io.saferepr import saferepr_unlimited
from _pytest._io.saferepr import saferepr
from _pytest.config import Config

Expand Down Expand Up @@ -160,8 +160,8 @@ def assertrepr_compare(config, op: str, left: Any, right: Any) -> Optional[List[
"""Return specialised explanations for some operators/operands."""
verbose = config.getoption("verbose")
if verbose > 1:
left_repr = safeformat(left)
right_repr = safeformat(right)
left_repr = saferepr_unlimited(left)
right_repr = saferepr_unlimited(right)
else:
# XXX: "15 chars indentation" is wrong
# ("E AssertionError: assert "); should use term width.
Expand Down
22 changes: 21 additions & 1 deletion testing/io/test_saferepr.py
@@ -1,7 +1,7 @@
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, saferepr_unlimited


def test_simple_repr():
Expand Down Expand Up @@ -179,3 +179,23 @@ def __repr__(self):
assert saferepr(SomeClass()).startswith(
"<[RuntimeError() raised in repr()] SomeClass object at 0x"
)


def test_saferepr_unlimited():
dict5 = {f'v{i}': i for i in range(5)}
assert saferepr_unlimited(dict5) == "{'v0': 0, 'v1': 1, 'v2': 2, 'v3': 3, 'v4': 4}"

dict_long = {f'v{i}': i for i in range(1_000)}
r = saferepr_unlimited(dict_long)
assert '...' not in r
assert '\n' not in r


def test_saferepr_unlimited_exc():
class A:
def __repr__(self):
raise ValueError(42)

assert saferepr_unlimited(A()).startswith(
'<[ValueError(42) raised in repr()] A object at 0x'
)
14 changes: 14 additions & 0 deletions testing/test_assertion.py
Expand Up @@ -1695,3 +1695,17 @@ def test():
"*= 1 failed in*",
]
)


def test_reprcompare_verbose_long() -> None:
a = {f'v{i}': i for i in range(11)}
b = a.copy()
b['v2'] += 10
lines = callop("==", a, b, verbose=2)
assert lines[0] == (
"{'v0': 0, 'v1': 1, 'v2': 2, 'v3': 3, 'v4': 4, 'v5': 5, "
"'v6': 6, 'v7': 7, 'v8': 8, 'v9': 9, 'v10': 10}"
" == "
"{'v0': 0, 'v1': 1, 'v2': 12, 'v3': 3, 'v4': 4, 'v5': 5, "
"'v6': 6, 'v7': 7, 'v8': 8, 'v9': 9, 'v10': 10}"
)

0 comments on commit 3c629dc

Please sign in to comment.