From d874835e644afd9e4fb72568a0aacf5a9e6f51e0 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Wed, 1 Aug 2018 00:23:06 -0700 Subject: [PATCH 1/3] bpo-34305: Unwrap decorators in getfile, rather than downstream Among other functions, this fixes getsourcefile and getcomments. Removes some code duplication while I'm here --- Lib/inspect.py | 67 ++++++++++--------- Lib/test/test_inspect.py | 6 ++ .../2018-08-01-23-31-12.bpo-34305.CjGWSo.rst | 3 + 3 files changed, 44 insertions(+), 32 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2018-08-01-23-31-12.bpo-34305.CjGWSo.rst diff --git a/Lib/inspect.py b/Lib/inspect.py index bc97efe179ca1d..0f7c833c9bd043 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -639,6 +639,21 @@ def cleandoc(doc): lines.pop(0) return '\n'.join(lines) +def _get_code_object(obj): + """Walk through a callable or frame to find the code object""" + if ismethod(obj): + obj = obj.__func__ + if isfunction(obj): + obj = unwrap(obj) + obj = obj.__code__ + if istraceback(obj): + obj = obj.tb_frame + if isframe(obj): + obj = obj.f_code + if iscode(obj): + return obj + raise TypeError + def getfile(object): """Work out which source or compiled file an object was defined in.""" if ismodule(object): @@ -651,19 +666,13 @@ def getfile(object): if hasattr(object, '__file__'): return object.__file__ raise TypeError('{!r} is a built-in class'.format(object)) - if ismethod(object): - object = object.__func__ - if isfunction(object): - object = object.__code__ - if istraceback(object): - object = object.tb_frame - if isframe(object): - object = object.f_code - if iscode(object): - return object.co_filename - raise TypeError('module, class, method, function, traceback, frame, or ' - 'code object was expected, got {}'.format( - type(object).__name__)) + + try: + return _get_code_object(object).co_filename + except TypeError: + raise TypeError('module, class, method, function, traceback, frame, or ' + 'code object was expected, got {}'.format( + type(object).__name__)) from None def getmodulename(path): """Return the module name for a given file, or None.""" @@ -811,24 +820,19 @@ def findsource(object): else: raise OSError('could not find class definition') - if ismethod(object): - object = object.__func__ - if isfunction(object): - object = object.__code__ - if istraceback(object): - object = object.tb_frame - if isframe(object): - object = object.f_code - if iscode(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 - return lines, lnum - raise OSError('could not find code object') + try: + object = _get_code_object(object) + except TypeError: + raise OSError('could not find code object') from None + + 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 + return lines, lnum def getcomments(object): """Get lines of comments immediately preceding an object's source code. @@ -951,7 +955,6 @@ def getsourcelines(object): corresponding to the object and the line number indicates where in the original source file the first line of code was found. An OSError is raised if the source code cannot be retrieved.""" - object = unwrap(object) lines, lnum = findsource(object) if ismodule(object): diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 1a856f6387e8ad..62ef6507b04899 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -539,6 +539,12 @@ def test_replacing_decorator(self): def test_getsource_unwrap(self): self.assertSourceEqual(mod2.real, 130, 132) + def test_getcomments_unwrap(self): + self.assertEqual(inspect.getcomments(mod2.real), '#line 129\n') + + def test_getsourcefile_unwrap(self): + self.assertEqual(inspect.getsourcefile(mod2.real), mod2.__file__) + def test_decorator_with_lambda(self): self.assertSourceEqual(mod2.func114, 113, 115) diff --git a/Misc/NEWS.d/next/Library/2018-08-01-23-31-12.bpo-34305.CjGWSo.rst b/Misc/NEWS.d/next/Library/2018-08-01-23-31-12.bpo-34305.CjGWSo.rst new file mode 100644 index 00000000000000..549212c679ba65 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-08-01-23-31-12.bpo-34305.CjGWSo.rst @@ -0,0 +1,3 @@ +Decorators are now unwrapped in `inspect.getcomments`, `inspect.getfile`, +and `inspect.getsourcefile`, making these functions consistent with +`inspect.getsource`. From ab59766eac0651c7aa212b6caafac46edb73ff01 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sat, 21 Sep 2024 18:17:42 +0100 Subject: [PATCH 2/3] Update 2018-08-01-23-31-12.bpo-34305.CjGWSo.rst --- .../next/Library/2018-08-01-23-31-12.bpo-34305.CjGWSo.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2018-08-01-23-31-12.bpo-34305.CjGWSo.rst b/Misc/NEWS.d/next/Library/2018-08-01-23-31-12.bpo-34305.CjGWSo.rst index 549212c679ba65..2329e9d5a6a19d 100644 --- a/Misc/NEWS.d/next/Library/2018-08-01-23-31-12.bpo-34305.CjGWSo.rst +++ b/Misc/NEWS.d/next/Library/2018-08-01-23-31-12.bpo-34305.CjGWSo.rst @@ -1,3 +1,3 @@ -Decorators are now unwrapped in `inspect.getcomments`, `inspect.getfile`, -and `inspect.getsourcefile`, making these functions consistent with -`inspect.getsource`. +Decorators are now unwrapped in :func:`inspect.getcomments`, :func:`inspect.getfile`, +and :func:`inspect.getsourcefile`, making these functions consistent with +:func:`inspect.getsource`. From 79c9f91daefa738d0f7439863ef4d5d8548b6293 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sat, 21 Sep 2024 17:44:00 +0000 Subject: [PATCH 3/3] fix --- Lib/inspect.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/inspect.py b/Lib/inspect.py index bd7d7f81ab3c3a..262efcbfcccbf1 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -810,6 +810,7 @@ def _get_code_object(obj): obj = obj.__func__ if isfunction(obj): obj = unwrap(obj) + if isfunction(obj): obj = obj.__code__ if istraceback(obj): obj = obj.tb_frame