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 <nicoddemus@gmail.com>
This commit is contained in:
parent
eb22339dc3
commit
b75cbee290
1
AUTHORS
1
AUTHORS
|
@ -289,6 +289,7 @@ Ruaridh Williamson
|
||||||
Russel Winder
|
Russel Winder
|
||||||
Ryan Wooden
|
Ryan Wooden
|
||||||
Saiprasad Kale
|
Saiprasad Kale
|
||||||
|
Samuel Colvin
|
||||||
Samuel Dion-Girardeau
|
Samuel Dion-Girardeau
|
||||||
Samuel Searles-Bryant
|
Samuel Searles-Bryant
|
||||||
Samuele Pedroni
|
Samuele Pedroni
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Display assertion message without escaped newline characters with ``-vv``.
|
|
@ -107,6 +107,23 @@ def saferepr(obj: object, maxsize: Optional[int] = DEFAULT_REPR_MAX_SIZE) -> str
|
||||||
return SafeRepr(maxsize).repr(obj)
|
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):
|
class AlwaysDispatchingPrettyPrinter(pprint.PrettyPrinter):
|
||||||
"""PrettyPrinter that always dispatches (regardless of width)."""
|
"""PrettyPrinter that always dispatches (regardless of width)."""
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,8 @@ from typing import Sequence
|
||||||
import _pytest._code
|
import _pytest._code
|
||||||
from _pytest import outcomes
|
from _pytest import outcomes
|
||||||
from _pytest._io.saferepr import _pformat_dispatch
|
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
|
||||||
|
from _pytest._io.saferepr import saferepr_unlimited
|
||||||
from _pytest.config import Config
|
from _pytest.config import Config
|
||||||
|
|
||||||
# The _reprcompare attribute on the util module is used by the new assertion
|
# 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[
|
||||||
"""Return specialised explanations for some operators/operands."""
|
"""Return specialised explanations for some operators/operands."""
|
||||||
verbose = config.getoption("verbose")
|
verbose = config.getoption("verbose")
|
||||||
if verbose > 1:
|
if verbose > 1:
|
||||||
left_repr = safeformat(left)
|
left_repr = saferepr_unlimited(left)
|
||||||
right_repr = safeformat(right)
|
right_repr = saferepr_unlimited(right)
|
||||||
else:
|
else:
|
||||||
# XXX: "15 chars indentation" is wrong
|
# XXX: "15 chars indentation" is wrong
|
||||||
# ("E AssertionError: assert "); should use term width.
|
# ("E AssertionError: assert "); should use term width.
|
||||||
|
|
|
@ -2,6 +2,7 @@ import pytest
|
||||||
from _pytest._io.saferepr import _pformat_dispatch
|
from _pytest._io.saferepr import _pformat_dispatch
|
||||||
from _pytest._io.saferepr import DEFAULT_REPR_MAX_SIZE
|
from _pytest._io.saferepr import DEFAULT_REPR_MAX_SIZE
|
||||||
from _pytest._io.saferepr import saferepr
|
from _pytest._io.saferepr import saferepr
|
||||||
|
from _pytest._io.saferepr import saferepr_unlimited
|
||||||
|
|
||||||
|
|
||||||
def test_simple_repr():
|
def test_simple_repr():
|
||||||
|
@ -179,3 +180,23 @@ def test_broken_getattribute():
|
||||||
assert saferepr(SomeClass()).startswith(
|
assert saferepr(SomeClass()).startswith(
|
||||||
"<[RuntimeError() raised in repr()] SomeClass object at 0x"
|
"<[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"
|
||||||
|
)
|
||||||
|
|
|
@ -1695,3 +1695,18 @@ def test_assertion_location_with_coverage(pytester: Pytester) -> None:
|
||||||
"*= 1 failed in*",
|
"*= 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}"
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in New Issue