summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorMeador Inge <meadori@gmail.com>2015-07-24 03:49:37 (GMT)
committerMeador Inge <meadori@gmail.com>2015-07-24 03:49:37 (GMT)
commit5b718d7f4fc3ad28ffb774aa96324a81f5f3e742 (patch)
tree3322ddcf2a68142b501f0f83dca61ac8e48b073d /Lib
parent70398398959045a1b617059201ed4fa02b4fa0fa (diff)
downloadcpython-5b718d7f4fc3ad28ffb774aa96324a81f5f3e742.zip
cpython-5b718d7f4fc3ad28ffb774aa96324a81f5f3e742.tar.gz
cpython-5b718d7f4fc3ad28ffb774aa96324a81f5f3e742.tar.bz2
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.
Diffstat (limited to 'Lib')
-rw-r--r--Lib/inspect.py26
-rw-r--r--Lib/test/test_inspect.py1
2 files changed, 22 insertions, 5 deletions
diff --git a/Lib/inspect.py b/Lib/inspect.py
index 24c8df7..bf4f87d 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -859,21 +859,37 @@ class BlockFinder:
self.islambda = False
self.started = False
self.passline = False
+ self.indecorator = False
+ self.decoratorhasargs = False
self.last = 1
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"
- if token in ("def", "class", "lambda"):
+ elif token in ("def", "class", "lambda"):
if token == "lambda":
self.islambda = True
self.started = True
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:
self.passline = False # stop skipping when a NEWLINE is seen
self.last = srowcol[0]
if self.islambda: # lambdas always end at the first NEWLINE
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:
pass
elif type == tokenize.INDENT:
@@ -913,8 +929,10 @@ def getsourcelines(object):
object = unwrap(object)
lines, lnum = findsource(object)
- if ismodule(object): return lines, 0
- else: return getblock(lines[lnum:]), lnum + 1
+ if ismodule(object):
+ return lines, 0
+ else:
+ return getblock(lines[lnum:]), lnum + 1
def getsource(object):
"""Return the text of the source code for an object.
diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py
index 042617b..955b2ad 100644
--- a/Lib/test/test_inspect.py
+++ b/Lib/test/test_inspect.py
@@ -464,7 +464,6 @@ class TestDecorators(GetSourceBase):
def test_getsource_unwrap(self):
self.assertSourceEqual(mod2.real, 130, 132)
- @unittest.expectedFailure
def test_decorator_with_lambda(self):
self.assertSourceEqual(mod2.func114, 113, 115)