diff -r ef6c4712cc28 Lib/inspect.py --- a/Lib/inspect.py Mon Apr 13 10:53:11 2015 -0500 +++ b/Lib/inspect.py Mon Apr 13 16:59:02 2015 -0400 @@ -888,6 +888,15 @@ 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. + """ + line_offsets = list(code_obj.co_lnotab[1::2]) + end_line = sum(line_offsets) + lnum + 1 + return lines[lnum:end_line], lnum + 1 + def getsourcelines(object): """Return a list of source lines and starting line number for an object. @@ -899,8 +908,16 @@ 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 + 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 def getsource(object): """Return the text of the source code for an object. diff -r ef6c4712cc28 Lib/test/inspect_fodder2.py --- a/Lib/test/inspect_fodder2.py Mon Apr 13 10:53:11 2015 -0500 +++ b/Lib/test/inspect_fodder2.py Mon Apr 13 16:59:02 2015 -0400 @@ -110,6 +110,14 @@ def keyword_only_arg(*, arg): pass +@wrap(lambda: None) +def func114(): + return 115 + +class ClassWithMethod: + def method(self): + pass + from functools import wraps def decorator(func): @@ -118,7 +126,7 @@ return 42 return fake -#line 121 +#line 129 @decorator def real(): return 20 diff -r ef6c4712cc28 Lib/test/test_inspect.py --- a/Lib/test/test_inspect.py Mon Apr 13 10:53:11 2015 -0500 +++ b/Lib/test/test_inspect.py Mon Apr 13 16:59:02 2015 -0400 @@ -392,6 +392,9 @@ finally: linecache.getlines = getlines + def test_getsource_on_code_object(self): + self.assertSourceEqual(mod.eggs.__code__, 12, 18) + class TestDecorators(GetSourceBase): fodderModule = mod2 @@ -402,7 +405,10 @@ self.assertSourceEqual(mod2.gone, 9, 10) def test_getsource_unwrap(self): - self.assertSourceEqual(mod2.real, 122, 124) + self.assertSourceEqual(mod2.real, 130, 132) + + def test_decorator_with_lambda(self): + self.assertSourceEqual(mod2.func114, 113, 115) class TestOneliners(GetSourceBase): fodderModule = mod2 @@ -497,6 +503,9 @@ self.assertRaises(IOError, inspect.findsource, co) self.assertRaises(IOError, inspect.getsource, co) + def test_getsource_on_method(self): + self.assertSourceEqual(mod2.ClassWithMethod.method, 118, 119) + class TestNoEOL(GetSourceBase): def __init__(self, *args, **kwargs): self.tempdir = TESTFN + '_dir'