Skip to content

gh-137477: Fix inspect.getblock() for generator expressions #137488

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
gh-137477: Fix inspect.getblock() for generator expressions
This fixes also inspect.getsourcelines() and inspect.getsource().
  • Loading branch information
serhiy-storchaka committed Aug 6, 2025
commit f080ac326e5e601553874fdcedc8b8045dd1353e
22 changes: 14 additions & 8 deletions Lib/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -1065,15 +1065,21 @@ def __init__(self):

def tokeneater(self, type, token, srowcol, erowcol, line):
if not self.started and not self.indecorator:
# skip any decorators
if token == "@":
self.indecorator = True
# look for the first "def", "class" or "lambda"
elif token in ("def", "class", "lambda"):
if token == "lambda":
if type != tokenize.INDENT:
# skip any decorators
if token == "@":
self.indecorator = True
elif token == "async":
pass
# look for the first "def", "class" or "lambda"
elif token in ("def", "class", "lambda"):
if token == "lambda":
self.islambda = True
self.started = True
else:
self.islambda = True
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain this part a bit to me? What's in the else case? Why is islambda set? I was a bit confused (without testing too much into it) when I read the code because I thought this is definitely not a lambda here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For def and class we search to the end of the block. For lambda and generator expressions we search to the end of the logical line. self.islambda is used to distinguish between these modes. We could rename it, but this will increase the size of the diff and may break user code that uses this private class, so I leave it for a time.

Although the code can be simplified.

self.started = True
self.passline = True # skip to the end of the line
self.started = True
self.passline = True # skip to the end of the line
elif type == tokenize.NEWLINE:
self.passline = False # stop skipping when a NEWLINE is seen
self.last = srowcol[0]
Expand Down
20 changes: 20 additions & 0 deletions Lib/test/test_inspect/inspect_fodder2.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,3 +369,23 @@ class dc364:
# line 369
dc370 = dataclasses.make_dataclass('dc370', (('x', int), ('y', int)))
dc371 = dataclasses.make_dataclass('dc370', (('x', int), ('y', int)), module=__name__)

import inspect
import itertools

# line 376
ge377 = (
inspect.currentframe()
for i in itertools.count()
)

# line 382
def func383():
# line 384
ge385 = (
inspect.currentframe()
for i in itertools.count()
)
return ge385

pass # end of file
6 changes: 6 additions & 0 deletions Lib/test/test_inspect/test_inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -1189,12 +1189,18 @@ def test_nested_class_definition_inside_async_function(self):

self.assertSourceEqual(run(mod2.func225), 226, 227)
self.assertSourceEqual(mod2.cls226, 231, 235)
self.assertSourceEqual(mod2.cls226.func232, 232, 235)
self.assertSourceEqual(run(mod2.cls226().func232), 233, 234)

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

def test_generator_expression(self):
self.assertSourceEqual(next(mod2.ge377), 377, 380)
self.assertSourceEqual(next(mod2.func383()), 385, 388)


class TestNoEOL(GetSourceBase):
def setUp(self):
self.tempdir = TESTFN + '_dir'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix :func:`inspect.getblock`, :func:`inspect.getsourcelines` and
:func:`inspect.getsource` for generator expressions.
Loading