Skip to content

Commit eae9d7d

Browse files
gh-137477: Fix inspect.getblock() for generator expressions (GH-137488)
This fixes also inspect.getsourcelines() and inspect.getsource().
1 parent 7685b8a commit eae9d7d

File tree

4 files changed

+38
-7
lines changed

4 files changed

+38
-7
lines changed

Lib/inspect.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,7 +1056,7 @@ class BlockFinder:
10561056
"""Provide a tokeneater() method to detect the end of a code block."""
10571057
def __init__(self):
10581058
self.indent = 0
1059-
self.islambda = False
1059+
self.singleline = False
10601060
self.started = False
10611061
self.passline = False
10621062
self.indecorator = False
@@ -1065,19 +1065,22 @@ def __init__(self):
10651065

10661066
def tokeneater(self, type, token, srowcol, erowcol, line):
10671067
if not self.started and not self.indecorator:
1068+
if type == tokenize.INDENT or token == "async":
1069+
pass
10681070
# skip any decorators
1069-
if token == "@":
1071+
elif token == "@":
10701072
self.indecorator = True
1071-
# look for the first "def", "class" or "lambda"
1072-
elif token in ("def", "class", "lambda"):
1073-
if token == "lambda":
1074-
self.islambda = True
1073+
else:
1074+
# For "def" and "class" scan to the end of the block.
1075+
# For "lambda" and generator expression scan to
1076+
# the end of the logical line.
1077+
self.singleline = token not in ("def", "class")
10751078
self.started = True
10761079
self.passline = True # skip to the end of the line
10771080
elif type == tokenize.NEWLINE:
10781081
self.passline = False # stop skipping when a NEWLINE is seen
10791082
self.last = srowcol[0]
1080-
if self.islambda: # lambdas always end at the first NEWLINE
1083+
if self.singleline:
10811084
raise EndOfBlock
10821085
# hitting a NEWLINE when in a decorator without args
10831086
# ends the decorator

Lib/test/test_inspect/inspect_fodder2.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,3 +369,23 @@ class dc364:
369369
# line 369
370370
dc370 = dataclasses.make_dataclass('dc370', (('x', int), ('y', int)))
371371
dc371 = dataclasses.make_dataclass('dc370', (('x', int), ('y', int)), module=__name__)
372+
373+
import inspect
374+
import itertools
375+
376+
# line 376
377+
ge377 = (
378+
inspect.currentframe()
379+
for i in itertools.count()
380+
)
381+
382+
# line 382
383+
def func383():
384+
# line 384
385+
ge385 = (
386+
inspect.currentframe()
387+
for i in itertools.count()
388+
)
389+
return ge385
390+
391+
pass # end of file

Lib/test/test_inspect/test_inspect.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,12 +1189,18 @@ def test_nested_class_definition_inside_async_function(self):
11891189

11901190
self.assertSourceEqual(run(mod2.func225), 226, 227)
11911191
self.assertSourceEqual(mod2.cls226, 231, 235)
1192+
self.assertSourceEqual(mod2.cls226.func232, 232, 235)
11921193
self.assertSourceEqual(run(mod2.cls226().func232), 233, 234)
11931194

11941195
def test_class_definition_same_name_diff_methods(self):
11951196
self.assertSourceEqual(mod2.cls296, 296, 298)
11961197
self.assertSourceEqual(mod2.cls310, 310, 312)
11971198

1199+
def test_generator_expression(self):
1200+
self.assertSourceEqual(next(mod2.ge377), 377, 380)
1201+
self.assertSourceEqual(next(mod2.func383()), 385, 388)
1202+
1203+
11981204
class TestNoEOL(GetSourceBase):
11991205
def setUp(self):
12001206
self.tempdir = TESTFN + '_dir'
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix :func:`!inspect.getblock`, :func:`inspect.getsourcelines` and
2+
:func:`inspect.getsource` for generator expressions.

0 commit comments

Comments
 (0)