diff --git a/Lib/inspect.py b/Lib/inspect.py index cbc0632484b832..ccbf8fbd4e92eb 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -1673,6 +1673,10 @@ 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): + lineno = _tblineno(frame) + frame = frame.tb_frame + else: + lineno = getlineno(frame) positions = _get_code_position_from_tb(frame) lineno = frame.tb_lineno frame = frame.tb_frame @@ -1707,10 +1711,34 @@ def getframeinfo(frame, context=1): return Traceback(filename, lineno, frame.f_code.co_name, lines, index, positions=dis.Positions(*positions)) + +def _tblineno(tb): + tb_lineno = tb.tb_lineno + if tb_lineno is not None: + return tb_lineno + + return _lasti2lineno(tb.tb_frame.f_code, tb.tb_lasti) + + +def _lasti2lineno(code, lasti): + prev_line = code.co_firstlineno + + for start, next_line in dis.findlinestarts(code): + if lasti < start: + return prev_line + prev_line = next_line + + return prev_line + 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 + # FrameType.f_lineno is now a descriptor that often grovels co_lnotab + f_lineno = frame.f_lineno + if f_lineno is not None: + return f_lineno + + return _lasti2lineno(frame.f_code, frame.f_lasti) + _FrameInfo = namedtuple('_FrameInfo', ('frame',) + Traceback._fields) class FrameInfo(_FrameInfo): diff --git a/Misc/NEWS.d/next/Library/2022-03-22-10-16-11.bpo-45563.o-oP2m.rst b/Misc/NEWS.d/next/Library/2022-03-22-10-16-11.bpo-45563.o-oP2m.rst new file mode 100644 index 00000000000000..d4e0076be67e11 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-03-22-10-16-11.bpo-45563.o-oP2m.rst @@ -0,0 +1 @@ +handle frame.f_lineno is None in inspect.getframeinfo