Issue #21217: inspect.getsourcelines() now tries to compute the start and
end lines from the code object, fixing an issue when a lambda function is used as decorator argument. Patch by Thomas Ballinger.
This commit is contained in:
parent
97100c0e3d
commit
a8723a02ea
|
@ -32,6 +32,7 @@ __author__ = ('Ka-Ping Yee <ping@lfw.org>',
|
||||||
'Yury Selivanov <yselivanov@sprymix.com>')
|
'Yury Selivanov <yselivanov@sprymix.com>')
|
||||||
|
|
||||||
import ast
|
import ast
|
||||||
|
import dis
|
||||||
import enum
|
import enum
|
||||||
import importlib.machinery
|
import importlib.machinery
|
||||||
import itertools
|
import itertools
|
||||||
|
@ -49,18 +50,10 @@ from operator import attrgetter
|
||||||
from collections import namedtuple, OrderedDict
|
from collections import namedtuple, OrderedDict
|
||||||
|
|
||||||
# Create constants for the compiler flags in Include/code.h
|
# Create constants for the compiler flags in Include/code.h
|
||||||
# We try to get them from dis to avoid duplication, but fall
|
# We try to get them from dis to avoid duplication
|
||||||
# back to hard-coding so the dependency is optional
|
mod_dict = globals()
|
||||||
try:
|
for k, v in dis.COMPILER_FLAG_NAMES.items():
|
||||||
from dis import COMPILER_FLAG_NAMES as _flag_names
|
mod_dict["CO_" + v] = k
|
||||||
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
|
|
||||||
|
|
||||||
# See Include/object.h
|
# See Include/object.h
|
||||||
TPFLAGS_IS_ABSTRACT = 1 << 20
|
TPFLAGS_IS_ABSTRACT = 1 << 20
|
||||||
|
@ -888,6 +881,14 @@ def getblock(lines):
|
||||||
pass
|
pass
|
||||||
return lines[:blockfinder.last]
|
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):
|
def getsourcelines(object):
|
||||||
"""Return a list of source lines and starting line number for an object.
|
"""Return a list of source lines and starting line number for an object.
|
||||||
|
|
||||||
|
@ -899,8 +900,16 @@ def getsourcelines(object):
|
||||||
object = unwrap(object)
|
object = unwrap(object)
|
||||||
lines, lnum = findsource(object)
|
lines, lnum = findsource(object)
|
||||||
|
|
||||||
if ismodule(object): return lines, 0
|
if ismodule(object):
|
||||||
else: return getblock(lines[lnum:]), lnum + 1
|
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):
|
def getsource(object):
|
||||||
"""Return the text of the source code for an object.
|
"""Return the text of the source code for an object.
|
||||||
|
|
|
@ -110,6 +110,14 @@ def annotated(arg1: list):
|
||||||
def keyword_only_arg(*, arg):
|
def keyword_only_arg(*, arg):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@wrap(lambda: None)
|
||||||
|
def func114():
|
||||||
|
return 115
|
||||||
|
|
||||||
|
class ClassWithMethod:
|
||||||
|
def method(self):
|
||||||
|
pass
|
||||||
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
def decorator(func):
|
def decorator(func):
|
||||||
|
@ -118,7 +126,7 @@ def decorator(func):
|
||||||
return 42
|
return 42
|
||||||
return fake
|
return fake
|
||||||
|
|
||||||
#line 121
|
#line 129
|
||||||
@decorator
|
@decorator
|
||||||
def real():
|
def real():
|
||||||
return 20
|
return 20
|
||||||
|
|
|
@ -392,6 +392,9 @@ class TestRetrievingSourceCode(GetSourceBase):
|
||||||
finally:
|
finally:
|
||||||
linecache.getlines = getlines
|
linecache.getlines = getlines
|
||||||
|
|
||||||
|
def test_getsource_on_code_object(self):
|
||||||
|
self.assertSourceEqual(mod.eggs.__code__, 12, 18)
|
||||||
|
|
||||||
class TestDecorators(GetSourceBase):
|
class TestDecorators(GetSourceBase):
|
||||||
fodderModule = mod2
|
fodderModule = mod2
|
||||||
|
|
||||||
|
@ -402,7 +405,10 @@ class TestDecorators(GetSourceBase):
|
||||||
self.assertSourceEqual(mod2.gone, 9, 10)
|
self.assertSourceEqual(mod2.gone, 9, 10)
|
||||||
|
|
||||||
def test_getsource_unwrap(self):
|
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):
|
class TestOneliners(GetSourceBase):
|
||||||
fodderModule = mod2
|
fodderModule = mod2
|
||||||
|
@ -497,6 +503,9 @@ class TestBuggyCases(GetSourceBase):
|
||||||
self.assertRaises(IOError, inspect.findsource, co)
|
self.assertRaises(IOError, inspect.findsource, co)
|
||||||
self.assertRaises(IOError, inspect.getsource, co)
|
self.assertRaises(IOError, inspect.getsource, co)
|
||||||
|
|
||||||
|
def test_getsource_on_method(self):
|
||||||
|
self.assertSourceEqual(mod2.ClassWithMethod.method, 118, 119)
|
||||||
|
|
||||||
class TestNoEOL(GetSourceBase):
|
class TestNoEOL(GetSourceBase):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.tempdir = TESTFN + '_dir'
|
self.tempdir = TESTFN + '_dir'
|
||||||
|
|
|
@ -72,6 +72,7 @@ Dwayne Bailey
|
||||||
Stig Bakken
|
Stig Bakken
|
||||||
Greg Ball
|
Greg Ball
|
||||||
Luigi Ballabio
|
Luigi Ballabio
|
||||||
|
Thomas Ballinger
|
||||||
Jeff Balogh
|
Jeff Balogh
|
||||||
Manuel Balsera
|
Manuel Balsera
|
||||||
Matt Bandy
|
Matt Bandy
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
+++++++++++
|
+++++++++++
|
||||||
Python News
|
Python News
|
||||||
+++++++++++
|
+++++++++++
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ Release date: XXX
|
||||||
|
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
- Issue #22631: Added Linux-specific socket constant CAN_RAW_FD_FRAMES.
|
- Issue #22631: Added Linux-specific socket constant CAN_RAW_FD_FRAMES.
|
||||||
Patch courtesy of Joe Jevnik.
|
Patch courtesy of Joe Jevnik.
|
||||||
|
|
||||||
|
@ -31,6 +32,10 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #21217: inspect.getsourcelines() now tries to compute the start and
|
||||||
|
end lines from the code object, fixing an issue when a lambda function is
|
||||||
|
used as decorator argument. Patch by Thomas Ballinger.
|
||||||
|
|
||||||
- Issue #23811: Add missing newline to the PyCompileError error message.
|
- Issue #23811: Add missing newline to the PyCompileError error message.
|
||||||
Patch by Alex Shkop.
|
Patch by Alex Shkop.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue