diff --git a/Lib/inspect.py b/Lib/inspect.py index 9eb87b0d277918..0a1bc8ccc311b0 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -697,6 +697,12 @@ def indentsize(line): return len(expline) - len(expline.lstrip()) def _findclass(func): + try: + idx = func.__code__.co_freevars.index('__class__') + except ValueError: + pass + else: + return func.__closure__[0].cell_contents cls = sys.modules.get(func.__module__) if cls is None: return None diff --git a/Lib/test/test_inspect/inspect_fodder.py b/Lib/test/test_inspect/inspect_fodder.py index febd54c86fe1d1..aeced786130c52 100644 --- a/Lib/test/test_inspect/inspect_fodder.py +++ b/Lib/test/test_inspect/inspect_fodder.py @@ -118,3 +118,15 @@ async def asyncf(self): # a closing parenthesis with the opening paren being in another line ( ); after_closing = lambda: 1 + +def gen_subclass(abuse_msg): + + class subclass(StupidGit): + def abuse(self, a, b, c): + print(abuse_msg) + super().abuse(a, b, c) + + return subclass + +DynamicSubclass = gen_subclass("some message") +del gen_subclass diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index efe9d27e3407ff..66ac65d71e6b49 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -629,7 +629,8 @@ class TestRetrievingSourceCode(GetSourceBase): def test_getclasses(self): classes = inspect.getmembers(mod, inspect.isclass) self.assertEqual(classes, - [('FesteringGob', mod.FesteringGob), + [('DynamicSubclass', mod.DynamicSubclass), + ('FesteringGob', mod.FesteringGob), ('MalodorousPervert', mod.MalodorousPervert), ('ParrotDroppings', mod.ParrotDroppings), ('StupidGit', mod.StupidGit), @@ -649,7 +650,8 @@ def test_getclasses(self): mod.ParrotDroppings)) ] ], - (mod.WhichComments, (object,),) + (mod.WhichComments, (object,),), + (mod.DynamicSubclass, (mod.StupidGit,)), ] ]) tree = inspect.getclasstree([cls[1] for cls in classes], True) @@ -662,7 +664,8 @@ def test_getclasses(self): mod.ParrotDroppings)) ] ], - (mod.WhichComments, (object,),) + (mod.WhichComments, (object,),), + (mod.DynamicSubclass, (mod.StupidGit,)), ] ]) @@ -697,6 +700,10 @@ def test_getdoc_inherited(self): 'Another\n\ndocstring\n\ncontaining\n\ntabs') self.assertEqual(inspect.getdoc(mod.FesteringGob.contradiction), 'The automatic gainsaying.') + self.assertEqual(inspect.getdoc(mod.DynamicSubclass.abuse), + 'Another\n\ndocstring\n\ncontaining\n\ntabs') + self.assertEqual(inspect.getdoc(mod.DynamicSubclass().abuse), + 'Another\n\ndocstring\n\ncontaining\n\ntabs') @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") diff --git a/Misc/NEWS.d/next/Library/2019-11-24-21-58-04.bpo-38603.2RMn1t.rst b/Misc/NEWS.d/next/Library/2019-11-24-21-58-04.bpo-38603.2RMn1t.rst new file mode 100644 index 00000000000000..85f0694ef83051 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-11-24-21-58-04.bpo-38603.2RMn1t.rst @@ -0,0 +1,3 @@ +`inspect.getdoc` now correctly inherits docstrings in dynamically generated +subclasses for methods that rely on `super()` (as the owning class can be +retrieved by inspecting the `__class__` cell).