Skip to content

Commit

Permalink
FormattedExcinfo.get_source: avoid crash when line number is out-of-b…
Browse files Browse the repository at this point in the history
…ounds/negative

pytest could crash given pathological AST position attributes, which shouldn't happen when testing real Python code, but could happen when testing AST produced by e.g. Hylang.

Another example of the failure is in the nightly CI for the JAX project: https://github.com/google/jax/actions/runs/4607513902/jobs/8142126075

Co-authored-by: Bruno Oliveira <[email protected]>
Co-authored-by: Jake VanderPlas <[email protected]>
  • Loading branch information
3 people committed Apr 6, 2023
1 parent 31d0b51 commit 3683722
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 3 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ Ionuț Turturică
Itxaso Aizpurua
Iwan Briquemont
Jaap Broekhuizen
Jake VanderPlas
Jakob van Santen
Jakub Mitoraj
James Bourbeau
Expand Down
1 change: 1 addition & 0 deletions changelog/10840.improvement.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pytest should no longer crash on AST with pathological position attributes, for example testing AST produced by `Hylang <https://github.com/hylang/hy>__`.
8 changes: 5 additions & 3 deletions src/_pytest/_code/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -743,11 +743,13 @@ def get_source(
) -> List[str]:
"""Return formatted and marked up source lines."""
lines = []
if source is None or line_index >= len(source.lines):
if source is not None and line_index < 0:
line_index += len(source)
if source is None or line_index >= len(source.lines) or line_index < 0:
# `line_index` could still be outside `range(len(source.lines))` if
# we're processing AST with pathological position attributes.
source = Source("???")
line_index = 0
if line_index < 0:
line_index += len(source)
space_prefix = " "
if short:
lines.append(space_prefix + source.lines[line_index].strip())
Expand Down
18 changes: 18 additions & 0 deletions testing/code/test_excinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,24 @@ def f(x):
assert lines[0] == "| def f(x):"
assert lines[1] == " pass"

def test_repr_source_out_of_bounds(self):
pr = FormattedExcinfo()
source = _pytest._code.Source(
"""\
def f(x):
pass
"""
).strip()
pr.flow_marker = "|" # type: ignore[misc]

lines = pr.get_source(source, 100)
assert len(lines) == 1
assert lines[0] == "| ???"

lines = pr.get_source(source, -100)
assert len(lines) == 1
assert lines[0] == "| ???"

def test_repr_source_excinfo(self) -> None:
"""Check if indentation is right."""
try:
Expand Down

0 comments on commit 3683722

Please sign in to comment.