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 18:22:06 2015 -0400 @@ -32,6 +32,7 @@ 'Yury Selivanov ') import ast +import dis import enum import importlib.machinery import itertools @@ -49,18 +50,10 @@ from collections import namedtuple, OrderedDict # Create constants for the compiler flags in Include/code.h -# We try to get them from dis to avoid duplication, but fall -# back to hard-coding so the dependency is optional -try: - from dis import COMPILER_FLAG_NAMES as _flag_names -except ImportError: - CO_OPTIMIZED, CO_NEWLOCALS = 0x1, 0x2 - CO_VARARGS, CO_VARKEYWORDS = 0x4, 0x8 - CO_NESTED, CO_GENERATOR, CO_NOFREE = 0x10, 0x20, 0x40 -else: - mod_dict = globals() - for k, v in _flag_names.items(): - mod_dict["CO_" + v] = k +# We try to get them from dis to avoid duplication +mod_dict = globals() +for k, v in dis.COMPILER_FLAG_NAMES.items(): + mod_dict["CO_" + v] = k # See Include/object.h TPFLAGS_IS_ABSTRACT = 1 << 20 @@ -888,6 +881,14 @@ 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. @@ -899,8 +900,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 18:22:06 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 18:22:06 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'