changeset: 96717:2958079884f4 branch: 3.5 tag: issue24485 tag: qbase tag: qtip tag: tip parent: 96695:85d49f65a410 user: Meador Inge date: Sun Jun 28 12:32:33 2015 -0500 files: Lib/inspect.py Lib/test/inspect_fodder2.py Lib/test/test_inspect.py description: [mq]: issue24485 diff --git a/Lib/inspect.py b/Lib/inspect.py --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -856,21 +856,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: @@ -899,14 +915,6 @@ def getblock(lines): pass return lines[:blockfinder.last] -def _line_number_helper(code_obj, lines, lnum): - """Return a list of source lines and starting line number for a code object. - - The arguments must be a code object with lines and lnum from findsource. - """ - _, end_line = list(dis.findlinestarts(code_obj))[-1] - return lines[lnum:end_line], lnum + 1 - def getsourcelines(object): """Return a list of source lines and starting line number for an object. @@ -920,12 +928,6 @@ def getsourcelines(object): if ismodule(object): return lines, 0 - elif iscode(object): - return _line_number_helper(object, lines, lnum) - elif isfunction(object): - return _line_number_helper(object.__code__, lines, lnum) - elif ismethod(object): - return _line_number_helper(object.__func__.__code__, lines, lnum) else: return getblock(lines[lnum:]), lnum + 1 diff --git a/Lib/test/inspect_fodder2.py b/Lib/test/inspect_fodder2.py --- a/Lib/test/inspect_fodder2.py +++ b/Lib/test/inspect_fodder2.py @@ -130,3 +130,10 @@ def decorator(func): @decorator def real(): return 20 + +#line 134 +class cls135: + def func136(): + def func137(): + never_reached1 + never_reached2 diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -568,6 +568,10 @@ class TestBuggyCases(GetSourceBase): def test_getsource_on_method(self): self.assertSourceEqual(mod2.ClassWithMethod.method, 118, 119) + def test_nested_func(self): + self.assertSourceEqual(mod2.cls135.func136, 136, 139) + + class TestNoEOL(GetSourceBase): def setUp(self): self.tempdir = TESTFN + '_dir'