https://github.com/python/cpython/commit/c04b3de517720ea6e8a6d8ae6e1fadb9bed43895
commit: c04b3de517720ea6e8a6d8ae6e1fadb9bed43895
branch: 3.13
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2025-08-20T13:43:28Z
summary:
[3.13] gh-137477: Fix inspect.getblock() for generator expressions (GH-137488)
(GH-137995)
This fixes also inspect.getsourcelines() and inspect.getsource().
(cherry picked from commit eae9d7de1c45a64076a926d53672823e6ae1777d)
files:
A Misc/NEWS.d/next/Library/2025-08-06-23-16-42.gh-issue-137477.bk6BDV.rst
M Lib/inspect.py
M Lib/test/test_inspect/inspect_fodder2.py
M Lib/test/test_inspect/test_inspect.py
diff --git a/Lib/inspect.py b/Lib/inspect.py
index de2fe7e1ae0f6d..5b3e63ce44e2e0 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -1159,7 +1159,7 @@ class BlockFinder:
"""Provide a tokeneater() method to detect the end of a code block."""
def __init__(self):
self.indent = 0
- self.islambda = False
+ self.singleline = False
self.started = False
self.passline = False
self.indecorator = False
@@ -1168,19 +1168,22 @@ def __init__(self):
def tokeneater(self, type, token, srowcol, erowcol, line):
if not self.started and not self.indecorator:
+ if type == tokenize.INDENT or token == "async":
+ pass
# skip any decorators
- if token == "@":
+ elif token == "@":
self.indecorator = True
- # look for the first "def", "class" or "lambda"
- elif token in ("def", "class", "lambda"):
- if token == "lambda":
- self.islambda = True
+ else:
+ # For "def" and "class" scan to the end of the block.
+ # For "lambda" and generator expression scan to
+ # the end of the logical line.
+ self.singleline = token not in ("def", "class")
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]
- if self.islambda: # lambdas always end at the first NEWLINE
+ if self.singleline:
raise EndOfBlock
# hitting a NEWLINE when in a decorator without args
# ends the decorator
diff --git a/Lib/test/test_inspect/inspect_fodder2.py
b/Lib/test/test_inspect/inspect_fodder2.py
index 43fda6622537fc..1de283f672d362 100644
--- a/Lib/test/test_inspect/inspect_fodder2.py
+++ b/Lib/test/test_inspect/inspect_fodder2.py
@@ -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
diff --git a/Lib/test/test_inspect/test_inspect.py
b/Lib/test/test_inspect/test_inspect.py
index 977c3d07252d04..e51ff58041921e 100644
--- a/Lib/test/test_inspect/test_inspect.py
+++ b/Lib/test/test_inspect/test_inspect.py
@@ -1187,12 +1187,18 @@ def
test_nested_class_definition_inside_async_function(self):
self.addCleanup(asyncio.set_event_loop_policy, None)
self.assertSourceEqual(asyncio.run(mod2.func225()), 226, 227)
self.assertSourceEqual(mod2.cls226, 231, 235)
+ self.assertSourceEqual(mod2.cls226.func232, 232, 235)
self.assertSourceEqual(asyncio.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'
diff --git
a/Misc/NEWS.d/next/Library/2025-08-06-23-16-42.gh-issue-137477.bk6BDV.rst
b/Misc/NEWS.d/next/Library/2025-08-06-23-16-42.gh-issue-137477.bk6BDV.rst
new file mode 100644
index 00000000000000..a6e097ea026293
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-08-06-23-16-42.gh-issue-137477.bk6BDV.rst
@@ -0,0 +1,2 @@
+Fix :func:`!inspect.getblock`, :func:`inspect.getsourcelines` and
+:func:`inspect.getsource` for generator expressions.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]