diff --git a/Lib/inspect.py b/Lib/inspect.py index b8a142232b88a0..bf8acdee149329 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -826,7 +826,7 @@ def findsource(object): if not hasattr(object, 'co_firstlineno'): raise OSError('could not find function definition') lnum = object.co_firstlineno - 1 - pat = re.compile(r'^(\s*def\s)|(\s*async\s+def\s)|(.*(? 0: if pat.match(lines[lnum]): break lnum = lnum - 1 diff --git a/Lib/test/inspect_fodder.py b/Lib/test/inspect_fodder.py index ff3f0e4b73b9ad..a55e40bf0d241e 100644 --- a/Lib/test/inspect_fodder.py +++ b/Lib/test/inspect_fodder.py @@ -80,3 +80,24 @@ async def lobbest(grenade): raise Exception() except: tb = sys.exc_info()[2] + +# line 84 +def extra_c(): + pass +class C: + def func(self): + pass + fr = inspect.currentframe() + +# line 92 +extra_b = 1 +class B: + def func(self): + pass + fr = inspect.currentframe() + +# line 99 +class A: + def func(self): + pass + fr = inspect.currentframe() diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index b9072e0137eb08..90eebec611ccc6 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -375,13 +375,20 @@ def assertSourceEqual(self, obj, top, bottom): self.assertEqual(inspect.getsource(obj), self.sourcerange(top, bottom)) + def assertNotSourceEqual(self, obj, top, bottom): + self.assertNotEqual(inspect.getsource(obj), + self.sourcerange(top, bottom)) + class TestRetrievingSourceCode(GetSourceBase): fodderModule = mod def test_getclasses(self): classes = inspect.getmembers(mod, inspect.isclass) self.assertEqual(classes, - [('FesteringGob', mod.FesteringGob), + [('A', mod.A), + ('B', mod.B), + ('C', mod.C), + ('FesteringGob', mod.FesteringGob), ('MalodorousPervert', mod.MalodorousPervert), ('ParrotDroppings', mod.ParrotDroppings), ('StupidGit', mod.StupidGit), @@ -390,7 +397,10 @@ def test_getclasses(self): tree = inspect.getclasstree([cls[1] for cls in classes]) self.assertEqual(tree, [(object, ()), - [(mod.ParrotDroppings, (object,)), + [(mod.A, (object,)), + (mod.B, (object,)), + (mod.C, (object,)), + (mod.ParrotDroppings, (object,)), [(mod.FesteringGob, (mod.MalodorousPervert, mod.ParrotDroppings)) ], @@ -405,7 +415,10 @@ def test_getclasses(self): tree = inspect.getclasstree([cls[1] for cls in classes], True) self.assertEqual(tree, [(object, ()), - [(mod.ParrotDroppings, (object,)), + [(mod.A, (object,)), + (mod.B, (object,)), + (mod.C, (object,)), + (mod.ParrotDroppings, (object,)), (mod.StupidGit, (object,)), [(mod.MalodorousPervert, (mod.StupidGit,)), [(mod.FesteringGob, (mod.MalodorousPervert, @@ -418,6 +431,7 @@ def test_getclasses(self): def test_getfunctions(self): functions = inspect.getmembers(mod, inspect.isfunction) self.assertEqual(functions, [('eggs', mod.eggs), + ('extra_c', mod.extra_c), ('lobbest', mod.lobbest), ('spam', mod.spam)]) @@ -552,7 +566,7 @@ def monkey(filename, module_globals=None): def test_getsource_on_code_object(self): self.assertSourceEqual(mod.eggs.__code__, 12, 18) -class TestGettingSourceOfToplevelFrames(GetSourceBase): +class TestGettingSourceOfFrames(GetSourceBase): fodderModule = mod def test_range_toplevel_frame(self): @@ -562,6 +576,14 @@ def test_range_toplevel_frame(self): def test_range_traceback_toplevel_frame(self): self.assertSourceEqual(mod.tb, 1, None) + def test_class_frame(self): + self.assertSourceEqual(mod.A.fr, 100, 103) + self.assertNotSourceEqual(mod.A.fr, 85, 86) + self.assertSourceEqual(mod.B.fr, 94, 97) + self.assertNotSourceEqual(mod.B.fr, 85, 86) + self.assertSourceEqual(mod.C.fr, 87, 90) + self.assertNotSourceEqual(mod.C.fr, 85, 86) + class TestDecorators(GetSourceBase): fodderModule = mod2 @@ -676,7 +698,6 @@ def test_getsource_on_method(self): def test_nested_func(self): self.assertSourceEqual(mod2.cls135.func136, 136, 139) - class TestNoEOL(GetSourceBase): def setUp(self): self.tempdir = TESTFN + '_dir' @@ -3905,7 +3926,7 @@ def test_main(): TestBoundArguments, TestSignaturePrivateHelpers, TestSignatureDefinitions, TestIsDataDescriptor, TestGetClosureVars, TestUnwrap, TestMain, TestReload, - TestGetCoroutineState, TestGettingSourceOfToplevelFrames + TestGetCoroutineState, TestGettingSourceOfFrames ) if __name__ == "__main__": diff --git a/Misc/NEWS.d/next/Library/2018-10-29-04-14-58.bpo-35101.xImh8d.rst b/Misc/NEWS.d/next/Library/2018-10-29-04-14-58.bpo-35101.xImh8d.rst new file mode 100644 index 00000000000000..375aef20ca7b94 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-10-29-04-14-58.bpo-35101.xImh8d.rst @@ -0,0 +1,2 @@ +Fix inspect.findsource incorrectly returning the line number of the first +function above any given class frame objects.