Skip to content

Commit b75cbee

Browse files
samuelcolvinpre-commit-ci[bot]nicoddemus
authored
Remove newlines from left/right operands with '-vv' (#9743)
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 <[email protected]>
1 parent eb22339 commit b75cbee

File tree

6 files changed

+58
-3
lines changed

6 files changed

+58
-3
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ Ruaridh Williamson
289289
Russel Winder
290290
Ryan Wooden
291291
Saiprasad Kale
292+
Samuel Colvin
292293
Samuel Dion-Girardeau
293294
Samuel Searles-Bryant
294295
Samuele Pedroni

changelog/9742.improvement.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Display assertion message without escaped newline characters with ``-vv``.

src/_pytest/_io/saferepr.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,23 @@ def saferepr(obj: object, maxsize: Optional[int] = DEFAULT_REPR_MAX_SIZE) -> str
107107
return SafeRepr(maxsize).repr(obj)
108108

109109

110+
def saferepr_unlimited(obj: object) -> str:
111+
"""Return an unlimited-size safe repr-string for the given object.
112+
113+
As with saferepr, failing __repr__ functions of user instances
114+
will be represented with a short exception info.
115+
116+
This function is a wrapper around simple repr.
117+
118+
Note: a cleaner solution would be to alter ``saferepr``this way
119+
when maxsize=None, but that might affect some other code.
120+
"""
121+
try:
122+
return repr(obj)
123+
except Exception as exc:
124+
return _format_repr_exception(exc, obj)
125+
126+
110127
class AlwaysDispatchingPrettyPrinter(pprint.PrettyPrinter):
111128
"""PrettyPrinter that always dispatches (regardless of width)."""
112129

src/_pytest/assertion/util.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
import _pytest._code
1515
from _pytest import outcomes
1616
from _pytest._io.saferepr import _pformat_dispatch
17-
from _pytest._io.saferepr import safeformat
1817
from _pytest._io.saferepr import saferepr
18+
from _pytest._io.saferepr import saferepr_unlimited
1919
from _pytest.config import Config
2020

2121
# The _reprcompare attribute on the util module is used by the new assertion
@@ -160,8 +160,8 @@ def assertrepr_compare(config, op: str, left: Any, right: Any) -> Optional[List[
160160
"""Return specialised explanations for some operators/operands."""
161161
verbose = config.getoption("verbose")
162162
if verbose > 1:
163-
left_repr = safeformat(left)
164-
right_repr = safeformat(right)
163+
left_repr = saferepr_unlimited(left)
164+
right_repr = saferepr_unlimited(right)
165165
else:
166166
# XXX: "15 chars indentation" is wrong
167167
# ("E AssertionError: assert "); should use term width.

testing/io/test_saferepr.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from _pytest._io.saferepr import _pformat_dispatch
33
from _pytest._io.saferepr import DEFAULT_REPR_MAX_SIZE
44
from _pytest._io.saferepr import saferepr
5+
from _pytest._io.saferepr import saferepr_unlimited
56

67

78
def test_simple_repr():
@@ -179,3 +180,23 @@ def __repr__(self):
179180
assert saferepr(SomeClass()).startswith(
180181
"<[RuntimeError() raised in repr()] SomeClass object at 0x"
181182
)
183+
184+
185+
def test_saferepr_unlimited():
186+
dict5 = {f"v{i}": i for i in range(5)}
187+
assert saferepr_unlimited(dict5) == "{'v0': 0, 'v1': 1, 'v2': 2, 'v3': 3, 'v4': 4}"
188+
189+
dict_long = {f"v{i}": i for i in range(1_000)}
190+
r = saferepr_unlimited(dict_long)
191+
assert "..." not in r
192+
assert "\n" not in r
193+
194+
195+
def test_saferepr_unlimited_exc():
196+
class A:
197+
def __repr__(self):
198+
raise ValueError(42)
199+
200+
assert saferepr_unlimited(A()).startswith(
201+
"<[ValueError(42) raised in repr()] A object at 0x"
202+
)

testing/test_assertion.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1695,3 +1695,18 @@ def test():
16951695
"*= 1 failed in*",
16961696
]
16971697
)
1698+
1699+
1700+
def test_reprcompare_verbose_long() -> None:
1701+
a = {f"v{i}": i for i in range(11)}
1702+
b = a.copy()
1703+
b["v2"] += 10
1704+
lines = callop("==", a, b, verbose=2)
1705+
assert lines is not None
1706+
assert lines[0] == (
1707+
"{'v0': 0, 'v1': 1, 'v2': 2, 'v3': 3, 'v4': 4, 'v5': 5, "
1708+
"'v6': 6, 'v7': 7, 'v8': 8, 'v9': 9, 'v10': 10}"
1709+
" == "
1710+
"{'v0': 0, 'v1': 1, 'v2': 12, 'v3': 3, 'v4': 4, 'v5': 5, "
1711+
"'v6': 6, 'v7': 7, 'v8': 8, 'v9': 9, 'v10': 10}"
1712+
)

0 commit comments

Comments
 (0)