Skip to content

Commit

Permalink
Remove newlines from left/right operands with '-vv' (#9743)
Browse files Browse the repository at this point in the history
The left/right operands produced when `verbose > 1` should not contain newlines, because they are used to 
build the `summary` string. The `assertrepr_compare` function returns a list of lines, and the summary is one of those lines and should not contain newlines itself. 

Fix #9742

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
  • Loading branch information
3 people committed Mar 19, 2022
1 parent eb22339 commit b75cbee
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 3 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``.
17 changes: 17 additions & 0 deletions src/_pytest/_io/saferepr.py
Expand Up @@ -107,6 +107,23 @@ 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.
Note: a cleaner solution would be to alter ``saferepr``this way
when maxsize=None, but that might affect some other code.
"""
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,8 +14,8 @@
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
from _pytest._io.saferepr import saferepr_unlimited
from _pytest.config import Config

# The _reprcompare attribute on the util module is used by the new assertion
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
21 changes: 21 additions & 0 deletions testing/io/test_saferepr.py
Expand Up @@ -2,6 +2,7 @@
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


def test_simple_repr():
Expand Down Expand Up @@ -179,3 +180,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"
)
15 changes: 15 additions & 0 deletions testing/test_assertion.py
Expand Up @@ -1695,3 +1695,18 @@ 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 is not None
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 b75cbee

Please sign in to comment.