summaryrefslogtreecommitdiffstats
path: root/Lib/inspect.py
diff options
context:
space:
mode:
authorPablo Galindo Salgado <Pablogsal@gmail.com>2022-04-23 02:16:48 (GMT)
committerGitHub <noreply@github.com>2022-04-23 02:16:48 (GMT)
commit0daa99f68b7b9f02b37a2f34508f33ae66d95fc4 (patch)
tree7d0ed2795e92ba42aa80f6b8e70f1bac2216c681 /Lib/inspect.py
parenta3f2cf3ced378db2569df4e7389ec1f79c85d55c (diff)
downloadcpython-0daa99f68b7b9f02b37a2f34508f33ae66d95fc4.zip
cpython-0daa99f68b7b9f02b37a2f34508f33ae66d95fc4.tar.gz
cpython-0daa99f68b7b9f02b37a2f34508f33ae66d95fc4.tar.bz2
gh-88116: Enhance the inspect frame APIs to use the extended position information (GH-91531)
Diffstat (limited to 'Lib/inspect.py')
-rw-r--r--Lib/inspect.py61
1 files changed, 54 insertions, 7 deletions
diff --git a/Lib/inspect.py b/Lib/inspect.py
index 9c1283a..5bc9c04 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -1638,7 +1638,30 @@ def getclosurevars(func):
# -------------------------------------------------- stack frame extraction
-Traceback = namedtuple('Traceback', 'filename lineno function code_context index')
+_Traceback = namedtuple('_Traceback', 'filename lineno function code_context index')
+
+class Traceback(_Traceback):
+ def __new__(cls, filename, lineno, function, code_context, index, *, positions=None):
+ instance = super().__new__(cls, filename, lineno, function, code_context, index)
+ instance.positions = positions
+ return instance
+
+ def __repr__(self):
+ return ('Traceback(filename={!r}, lineno={!r}, function={!r}, '
+ 'code_context={!r}, index={!r}, positions={!r})'.format(
+ self.filename, self.lineno, self.function, self.code_context,
+ self.index, self.positions))
+
+def _get_code_position_from_tb(tb):
+ code, instruction_index = tb.tb_frame.f_code, tb.tb_lasti
+ return _get_code_position(code, instruction_index)
+
+def _get_code_position(code, instruction_index):
+ if instruction_index < 0:
+ return (None, None, None, None)
+ positions_gen = code.co_positions()
+ # The nth entry in code.co_positions() corresponds to instruction (2*n)th since Python 3.10+
+ return next(itertools.islice(positions_gen, instruction_index // 2, None))
def getframeinfo(frame, context=1):
"""Get information about a frame or traceback object.
@@ -1649,10 +1672,20 @@ def getframeinfo(frame, context=1):
The optional second argument specifies the number of lines of context
to return, which are centered around the current line."""
if istraceback(frame):
+ positions = _get_code_position_from_tb(frame)
lineno = frame.tb_lineno
frame = frame.tb_frame
else:
lineno = frame.f_lineno
+ positions = _get_code_position(frame.f_code, frame.f_lasti)
+
+ if positions[0] is None:
+ frame, *positions = (frame, lineno, *positions[1:])
+ else:
+ frame, *positions = (frame, *positions)
+
+ lineno = positions[0]
+
if not isframe(frame):
raise TypeError('{!r} is not a frame or traceback object'.format(frame))
@@ -1670,14 +1703,26 @@ def getframeinfo(frame, context=1):
else:
lines = index = None
- return Traceback(filename, lineno, frame.f_code.co_name, lines, index)
+ return Traceback(filename, lineno, frame.f_code.co_name, lines,
+ index, positions=dis.Positions(*positions))
def getlineno(frame):
"""Get the line number from a frame object, allowing for optimization."""
# FrameType.f_lineno is now a descriptor that grovels co_lnotab
return frame.f_lineno
-FrameInfo = namedtuple('FrameInfo', ('frame',) + Traceback._fields)
+_FrameInfo = namedtuple('_FrameInfo', ('frame',) + Traceback._fields)
+class FrameInfo(_FrameInfo):
+ def __new__(cls, frame, filename, lineno, function, code_context, index, *, positions=None):
+ instance = super().__new__(cls, frame, filename, lineno, function, code_context, index)
+ instance.positions = positions
+ return instance
+
+ def __repr__(self):
+ return ('FrameInfo(frame={!r}, filename={!r}, lineno={!r}, function={!r}, '
+ 'code_context={!r}, index={!r}, positions={!r})'.format(
+ self.frame, self.filename, self.lineno, self.function,
+ self.code_context, self.index, self.positions))
def getouterframes(frame, context=1):
"""Get a list of records for a frame and all higher (calling) frames.
@@ -1686,8 +1731,9 @@ def getouterframes(frame, context=1):
name, a list of lines of context, and index within the context."""
framelist = []
while frame:
- frameinfo = (frame,) + getframeinfo(frame, context)
- framelist.append(FrameInfo(*frameinfo))
+ traceback_info = getframeinfo(frame, context)
+ frameinfo = (frame,) + traceback_info
+ framelist.append(FrameInfo(*frameinfo, positions=traceback_info.positions))
frame = frame.f_back
return framelist
@@ -1698,8 +1744,9 @@ def getinnerframes(tb, context=1):
name, a list of lines of context, and index within the context."""
framelist = []
while tb:
- frameinfo = (tb.tb_frame,) + getframeinfo(tb, context)
- framelist.append(FrameInfo(*frameinfo))
+ traceback_info = getframeinfo(tb, context)
+ frameinfo = (tb.tb_frame,) + traceback_info
+ framelist.append(FrameInfo(*frameinfo, positions=traceback_info.positions))
tb = tb.tb_next
return framelist