Issue #24485: Function source inspection fails on closures.
The fix for Issue #21217 introduced a regression that caused `inspect.getsource` to return incorrect results on nested functions. The root cause of the regression was due to switching the implementation to analyze the underlying bytecode instead of the source code. This commit switches things back to analyzing the source code in a more complete way. The original bug and the regression are both fixed by the new source code analysis.
This commit is contained in:
parent
7039839895
commit
5b718d7f4f
|
@ -859,21 +859,37 @@ class BlockFinder:
|
||||||
self.islambda = False
|
self.islambda = False
|
||||||
self.started = False
|
self.started = False
|
||||||
self.passline = False
|
self.passline = False
|
||||||
|
self.indecorator = False
|
||||||
|
self.decoratorhasargs = False
|
||||||
self.last = 1
|
self.last = 1
|
||||||
|
|
||||||
def tokeneater(self, type, token, srowcol, erowcol, line):
|
def tokeneater(self, type, token, srowcol, erowcol, line):
|
||||||
if not self.started:
|
if not self.started and not self.indecorator:
|
||||||
|
# skip any decorators
|
||||||
|
if token == "@":
|
||||||
|
self.indecorator = True
|
||||||
# look for the first "def", "class" or "lambda"
|
# look for the first "def", "class" or "lambda"
|
||||||
if token in ("def", "class", "lambda"):
|
elif token in ("def", "class", "lambda"):
|
||||||
if token == "lambda":
|
if token == "lambda":
|
||||||
self.islambda = True
|
self.islambda = True
|
||||||
self.started = True
|
self.started = True
|
||||||
self.passline = True # skip to the end of the line
|
self.passline = True # skip to the end of the line
|
||||||
|
elif token == "(":
|
||||||
|
if self.indecorator:
|
||||||
|
self.decoratorhasargs = True
|
||||||
|
elif token == ")":
|
||||||
|
if self.indecorator:
|
||||||
|
self.indecorator = False
|
||||||
|
self.decoratorhasargs = False
|
||||||
elif type == tokenize.NEWLINE:
|
elif type == tokenize.NEWLINE:
|
||||||
self.passline = False # stop skipping when a NEWLINE is seen
|
self.passline = False # stop skipping when a NEWLINE is seen
|
||||||
self.last = srowcol[0]
|
self.last = srowcol[0]
|
||||||
if self.islambda: # lambdas always end at the first NEWLINE
|
if self.islambda: # lambdas always end at the first NEWLINE
|
||||||
raise EndOfBlock
|
raise EndOfBlock
|
||||||
|
# hitting a NEWLINE when in a decorator without args
|
||||||
|
# ends the decorator
|
||||||
|
if self.indecorator and not self.decoratorhasargs:
|
||||||
|
self.indecorator = False
|
||||||
elif self.passline:
|
elif self.passline:
|
||||||
pass
|
pass
|
||||||
elif type == tokenize.INDENT:
|
elif type == tokenize.INDENT:
|
||||||
|
@ -913,8 +929,10 @@ def getsourcelines(object):
|
||||||
object = unwrap(object)
|
object = unwrap(object)
|
||||||
lines, lnum = findsource(object)
|
lines, lnum = findsource(object)
|
||||||
|
|
||||||
if ismodule(object): return lines, 0
|
if ismodule(object):
|
||||||
else: return getblock(lines[lnum:]), lnum + 1
|
return lines, 0
|
||||||
|
else:
|
||||||
|
return getblock(lines[lnum:]), lnum + 1
|
||||||
|
|
||||||
def getsource(object):
|
def getsource(object):
|
||||||
"""Return the text of the source code for an object.
|
"""Return the text of the source code for an object.
|
||||||
|
|
|
@ -464,7 +464,6 @@ class TestDecorators(GetSourceBase):
|
||||||
def test_getsource_unwrap(self):
|
def test_getsource_unwrap(self):
|
||||||
self.assertSourceEqual(mod2.real, 130, 132)
|
self.assertSourceEqual(mod2.real, 130, 132)
|
||||||
|
|
||||||
@unittest.expectedFailure
|
|
||||||
def test_decorator_with_lambda(self):
|
def test_decorator_with_lambda(self):
|
||||||
self.assertSourceEqual(mod2.func114, 113, 115)
|
self.assertSourceEqual(mod2.func114, 113, 115)
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,9 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #22485: Fixed an issue that caused `inspect.getsource` to return incorrect
|
||||||
|
results on nested functions.
|
||||||
|
|
||||||
- Issue #22153: Improve unittest docs. Patch from Martin Panter and evilzero.
|
- Issue #22153: Improve unittest docs. Patch from Martin Panter and evilzero.
|
||||||
|
|
||||||
- Issue #24580: Symbolic group references to open group in re patterns now are
|
- Issue #24580: Symbolic group references to open group in re patterns now are
|
||||||
|
@ -551,6 +554,10 @@ Library
|
||||||
- Issue #23342: Add a subprocess.run() function than returns a CalledProcess
|
- Issue #23342: Add a subprocess.run() function than returns a CalledProcess
|
||||||
instance for a more consistent API than the existing call* functions.
|
instance for a more consistent API than the existing call* functions.
|
||||||
|
|
||||||
|
- Issue #21217: inspect.getsourcelines() now tries to compute the start and end
|
||||||
|
lines from the code object, fixing an issue when a lambda function is used as
|
||||||
|
decorator argument. Patch by Thomas Ballinger and Allison Kaptur.
|
||||||
|
|
||||||
- Issue #24521: Fix possible integer overflows in the pickle module.
|
- Issue #24521: Fix possible integer overflows in the pickle module.
|
||||||
|
|
||||||
- Issue #22931: Allow '[' and ']' in cookie values.
|
- Issue #22931: Allow '[' and ']' in cookie values.
|
||||||
|
|
Loading…
Reference in New Issue