Skip to content

code: do not truncate args in output when running with -vvv #12241

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ Michael Droettboom
Michael Goerz
Michael Krebs
Michael Seifert
Michael Vogt
Michal Wajszczuk
Michał Górny
Michał Zięba
Expand Down
12 changes: 11 additions & 1 deletion src/_pytest/_code/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,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 @@ -666,6 +667,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 @@ -692,6 +696,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 @@ -810,6 +815,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 @@ -840,7 +846,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 = safeformat(argvalue)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about still calling saferepr, but passing max_size=None if truncate_args is False?

args.append((argname, str_repr))
return ReprFuncArgs(args)
return None

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

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

Check warning on line 452 in src/_pytest/nodes.py

View check run for this annotation

Codecov / codecov/patch

src/_pytest/nodes.py#L452

Added line #L452 was not covered by tests
else:
truncate_args = True
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use a ternary here to make the code a bit more succint, while also cheating a bit on the coverage.

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! I changed the code accordingly, this looks much nicer indeed.


# 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 +471,7 @@
style=style,
tbfilter=tbfilter,
truncate_locals=truncate_locals,
truncate_args=truncate_args,
)

def repr_failure(
Expand Down
22 changes: 22 additions & 0 deletions testing/code/test_excinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,28 @@ 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:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love the unittest!

Perhaps, in addition, we should have an integration test using pytester to ensure it does not get truncated there? It is interesting mostly to avoid future regressions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the pointer! I added one now, please let me know if I put it into the right place (not 100% certain about this, happy to switch/move as needed).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thanks!

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
assert len(reprfuncargs.args[0][1]) < 500
assert "..." in reprfuncargs.args[0][1]
# 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 reprfuncargs.args[0][1]

def test_repr_tracebackentry_lines(self, importasmod) -> None:
mod = importasmod(
"""
Expand Down
Loading