Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
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
3 changes: 3 additions & 0 deletions src/_pytest/_code/source.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ def get_statement_startend2(lineno: int, node: ast.AST) -> tuple[int, int | None
values.append(val[0].lineno - 1 - 1)
values.sort()
insert_index = bisect_right(values, lineno)
if insert_index == 0:
return 0, None
start = values[insert_index - 1]
if insert_index >= len(values):
end = None
Expand Down Expand Up @@ -216,6 +218,7 @@ def getstatementrange_ast(
pass

# The end might still point to a comment or empty line, correct it.
end = min(end, len(source.lines))
while end:
line = source.lines[end - 1].lstrip()
if line.startswith("#") or not line:
Expand Down
25 changes: 25 additions & 0 deletions testing/code/test_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import sys
import textwrap
from typing import Any
from unittest.mock import patch

from _pytest._code import Code
from _pytest._code import Frame
Expand Down Expand Up @@ -647,3 +648,27 @@ def __init__(self, *args):
# fmt: on
values = [i for i in x.source.lines if i.strip()]
assert len(values) == 4


def test_patched_compile() -> None:
# ensure Source doesn't break
# when compile() modifies code dynamically
from builtins import compile

def patched_compile1(_, *args, **kwargs):
return compile("", *args, **kwargs)

with patch("builtins.compile", new=patched_compile1):
Source(patched_compile1).getstatement(1)

# fmt: off
def patched_compile2(_, *args, **kwargs):

# first line of this function (the one above this one) must be empty
# LINES must be equal or higher than number of lines of this function
LINES = 99
return compile("\ndef a():\n" + "\n" * LINES + " pass", *args, **kwargs)
# fmt: on

with patch("builtins.compile", new=patched_compile2):
Source(patched_compile2).getstatement(1)