Skip to content

Commit c04b3de

Browse files
[3.13] pythongh-137477: Fix inspect.getblock() for generator expressions (pythonGH-137488) (pythonGH-137995)
This fixes also inspect.getsourcelines() and inspect.getsource(). (cherry picked from commit eae9d7d)
1 parent 47b1c5d commit c04b3de

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
@@ -1159,7 +1159,7 @@ class BlockFinder:
11591159
"""Provide a tokeneater() method to detect the end of a code block."""
11601160
def __init__(self):
11611161
self.indent = 0
1162-
self.islambda = False
1162+
self.singleline = False
11631163
self.started = False
11641164
self.passline = False
11651165
self.indecorator = False
@@ -1168,19 +1168,22 @@ def __init__(self):
11681168

11691169
def tokeneater(self, type, token, srowcol, erowcol, line):
11701170
if not self.started and not self.indecorator:
1171+
if type == tokenize.INDENT or token == "async":
1172+
pass
11711173
# skip any decorators
1172-
if token == "@":
1174+
elif token == "@":
11731175
self.indecorator = True
1174-
# look for the first "def", "class" or "lambda"
1175-
elif token in ("def", "class", "lambda"):
1176-
if token == "lambda":
1177-
self.islambda = True
1176+
else:
1177+
# For "def" and "class" scan to the end of the block.
1178+
# For "lambda" and generator expression scan to
1179+
# the end of the logical line.
1180+
self.singleline = token not in ("def", "class")
11781181
self.started = True
11791182
self.passline = True # skip to the end of the line
11801183
elif type == tokenize.NEWLINE:
11811184
self.passline = False # stop skipping when a NEWLINE is seen
11821185
self.last = srowcol[0]
1183-
if self.islambda: # lambdas always end at the first NEWLINE
1186+
if self.singleline:
11841187
raise EndOfBlock
11851188
# hitting a NEWLINE when in a decorator without args
11861189
# 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
@@ -1187,12 +1187,18 @@ def test_nested_class_definition_inside_async_function(self):
11871187
self.addCleanup(asyncio.set_event_loop_policy, None)
11881188
self.assertSourceEqual(asyncio.run(mod2.func225()), 226, 227)
11891189
self.assertSourceEqual(mod2.cls226, 231, 235)
1190+
self.assertSourceEqual(mod2.cls226.func232, 232, 235)
11901191
self.assertSourceEqual(asyncio.run(mod2.cls226().func232()), 233, 234)
11911192

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

1197+
def test_generator_expression(self):
1198+
self.assertSourceEqual(next(mod2.ge377), 377, 380)
1199+
self.assertSourceEqual(next(mod2.func383()), 385, 388)
1200+
1201+
11961202
class TestNoEOL(GetSourceBase):
11971203
def setUp(self):
11981204
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)