Skip to content

Commit 3683722

Browse files
Kodiologistnicoddemusjakevdp
authored
FormattedExcinfo.get_source: avoid crash when line number is out-of-bounds/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]>
1 parent 31d0b51 commit 3683722

File tree

4 files changed

+25
-3
lines changed

4 files changed

+25
-3
lines changed

AUTHORS

+1
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ Ionuț Turturică
163163
Itxaso Aizpurua
164164
Iwan Briquemont
165165
Jaap Broekhuizen
166+
Jake VanderPlas
166167
Jakob van Santen
167168
Jakub Mitoraj
168169
James Bourbeau

changelog/10840.improvement.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pytest should no longer crash on AST with pathological position attributes, for example testing AST produced by `Hylang <https://github.com/hylang/hy>__`.

src/_pytest/_code/code.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -743,11 +743,13 @@ def get_source(
743743
) -> List[str]:
744744
"""Return formatted and marked up source lines."""
745745
lines = []
746-
if source is None or line_index >= len(source.lines):
746+
if source is not None and line_index < 0:
747+
line_index += len(source)
748+
if source is None or line_index >= len(source.lines) or line_index < 0:
749+
# `line_index` could still be outside `range(len(source.lines))` if
750+
# we're processing AST with pathological position attributes.
747751
source = Source("???")
748752
line_index = 0
749-
if line_index < 0:
750-
line_index += len(source)
751753
space_prefix = " "
752754
if short:
753755
lines.append(space_prefix + source.lines[line_index].strip())

testing/code/test_excinfo.py

+18
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,24 @@ def f(x):
461461
assert lines[0] == "| def f(x):"
462462
assert lines[1] == " pass"
463463

464+
def test_repr_source_out_of_bounds(self):
465+
pr = FormattedExcinfo()
466+
source = _pytest._code.Source(
467+
"""\
468+
def f(x):
469+
pass
470+
"""
471+
).strip()
472+
pr.flow_marker = "|" # type: ignore[misc]
473+
474+
lines = pr.get_source(source, 100)
475+
assert len(lines) == 1
476+
assert lines[0] == "| ???"
477+
478+
lines = pr.get_source(source, -100)
479+
assert len(lines) == 1
480+
assert lines[0] == "| ???"
481+
464482
def test_repr_source_excinfo(self) -> None:
465483
"""Check if indentation is right."""
466484
try:

0 commit comments

Comments
 (0)