gh-88116: Enhance the inspect frame APIs to use the extended position information (GH-91531)

This commit is contained in:
Pablo Galindo Salgado 2022-04-23 03:16:48 +01:00 committed by GitHub
parent a3f2cf3ced
commit 0daa99f68b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 193 additions and 38 deletions

View file

@ -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