Skip to content

Commit

Permalink
code: do not truncate args when running with -vvv (#12241)
Browse files Browse the repository at this point in the history
Related to #2871.
  • Loading branch information
mvo5 authored Jun 3, 2024
1 parent 177f2ae commit 7be95f9
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 1 deletion.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ Michael Droettboom
Michael Goerz
Michael Krebs
Michael Seifert
Michael Vogt
Michal Wajszczuk
Michał Górny
Michał Zięba
Expand Down
1 change: 1 addition & 0 deletions changelog/2871.improvement.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Do not truncate arguments to functions in output when running with `-vvv`.
12 changes: 11 additions & 1 deletion src/_pytest/_code/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,7 @@ def getrepr(
] = True,
funcargs: bool = False,
truncate_locals: bool = True,
truncate_args: bool = True,
chain: bool = True,
) -> Union["ReprExceptionInfo", "ExceptionChainRepr"]:
"""Return str()able representation of this exception info.
Expand Down Expand Up @@ -665,6 +666,9 @@ def getrepr(
:param bool truncate_locals:
With ``showlocals==True``, make sure locals can be safely represented as strings.
:param bool truncate_args:
With ``showargs==True``, make sure args can be safely represented as strings.
:param bool chain:
If chained exceptions in Python 3 should be shown.
Expand All @@ -691,6 +695,7 @@ def getrepr(
tbfilter=tbfilter,
funcargs=funcargs,
truncate_locals=truncate_locals,
truncate_args=truncate_args,
chain=chain,
)
return fmt.repr_excinfo(self)
Expand Down Expand Up @@ -809,6 +814,7 @@ class FormattedExcinfo:
tbfilter: Union[bool, Callable[[ExceptionInfo[BaseException]], Traceback]] = True
funcargs: bool = False
truncate_locals: bool = True
truncate_args: bool = True
chain: bool = True
astcache: Dict[Union[str, Path], ast.AST] = dataclasses.field(
default_factory=dict, init=False, repr=False
Expand Down Expand Up @@ -839,7 +845,11 @@ def repr_args(self, entry: TracebackEntry) -> Optional["ReprFuncArgs"]:
if self.funcargs:
args = []
for argname, argvalue in entry.frame.getargs(var=True):
args.append((argname, saferepr(argvalue)))
if self.truncate_args:
str_repr = saferepr(argvalue)
else:
str_repr = saferepr(argvalue, maxsize=None)
args.append((argname, str_repr))
return ReprFuncArgs(args)
return None

Expand Down
3 changes: 3 additions & 0 deletions src/_pytest/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,8 @@ def _repr_failure_py(
else:
truncate_locals = True

truncate_args = False if self.config.getoption("verbose", 0) > 2 else True

# excinfo.getrepr() formats paths relative to the CWD if `abspath` is False.
# It is possible for a fixture/test to change the CWD while this code runs, which
# would then result in the user seeing confusing paths in the failure message.
Expand All @@ -466,6 +468,7 @@ def _repr_failure_py(
style=style,
tbfilter=tbfilter,
truncate_locals=truncate_locals,
truncate_args=truncate_args,
)

def repr_failure(
Expand Down
24 changes: 24 additions & 0 deletions testing/code/test_excinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import sys
import textwrap
from typing import Any
from typing import cast
from typing import TYPE_CHECKING

import _pytest._code
Expand Down Expand Up @@ -712,6 +713,29 @@ def test_repr_local_truncated(self) -> None:
assert full_reprlocals.lines
assert full_reprlocals.lines[0] == "l = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]"

def test_repr_args_not_truncated(self, importasmod) -> None:
mod = importasmod(
"""
def func1(m):
raise ValueError("hello\\nworld")
"""
)
excinfo = pytest.raises(ValueError, mod.func1, "m" * 500)
excinfo.traceback = excinfo.traceback.filter(excinfo)
entry = excinfo.traceback[-1]
p = FormattedExcinfo(funcargs=True, truncate_args=True)
reprfuncargs = p.repr_args(entry)
assert reprfuncargs is not None
arg1 = cast(str, reprfuncargs.args[0][1])
assert len(arg1) < 500
assert "..." in arg1
# again without truncate
p = FormattedExcinfo(funcargs=True, truncate_args=False)
reprfuncargs = p.repr_args(entry)
assert reprfuncargs is not None
assert reprfuncargs.args[0] == ("m", repr("m" * 500))
assert "..." not in cast(str, reprfuncargs.args[0][1])

def test_repr_tracebackentry_lines(self, importasmod) -> None:
mod = importasmod(
"""
Expand Down
33 changes: 33 additions & 0 deletions testing/test_assertion.py
Original file line number Diff line number Diff line change
Expand Up @@ -2045,3 +2045,36 @@ def test_long_text_fail():
f"E AssertionError: assert 'hello world' in '{long_text}'",
]
)


def test_full_output_vvv(pytester: Pytester) -> None:
pytester.makepyfile(
r"""
def crash_helper(m):
assert 1 == 2
def test_vvv():
crash_helper(500 * "a")
"""
)
result = pytester.runpytest("")
# without -vvv, the passed args are truncated
expected_non_vvv_arg_line = "m = 'aaaaaaaaaaaaaaa*..aaaaaaaaaaaa*"
result.stdout.fnmatch_lines(
[
expected_non_vvv_arg_line,
"test_full_output_vvv.py:2: AssertionError",
],
)
# double check that the untruncated part is not in the output
expected_vvv_arg_line = "m = '{}'".format(500 * "a")
result.stdout.no_fnmatch_line(expected_vvv_arg_line)

# but with "-vvv" the args are not truncated
result = pytester.runpytest("-vvv")
result.stdout.fnmatch_lines(
[
expected_vvv_arg_line,
"test_full_output_vvv.py:2: AssertionError",
]
)
result.stdout.no_fnmatch_line(expected_non_vvv_arg_line)

0 comments on commit 7be95f9

Please sign in to comment.