11# -*- coding: utf-8 -*-
22
33# Copyright (C) 2008-2010, 2013, 2015, 2017-2018, 2020-2021,
4- # 2023-2025 Rocky Bernstein <rocky@gnu.org>
4+ # 2023-2026 Rocky Bernstein <rocky@gnu.org>
55#
66# This program is free software: you can redistribute it and/or modify
77# it under the terms of the GNU General Public License as published by
1515#
1616# You should have received a copy of the GNU General Public License
1717# along with this program. If not, see <http://www.gnu.org/licenses/>.
18- """ Functions for working with Python frames"""
18+ """Functions for working with Python frames"""
1919
2020import dis
2121import inspect
2727from reprlib import repr
2828from types import CodeType , FrameType
2929from typing import Optional , Tuple
30-
3130import xdis
32- from xdis import get_opcode
3331from xdis .version_info import PYTHON_IMPLEMENTATION , PYTHON_VERSION_TRIPLE
3432
3533from trepan .lib .bytecode import op_at_frame
4846try :
4947 from trepan .processor .cmdfns import deparse_fn
5048except ImportError :
49+
5150 def deparse_fn (code ):
5251 raise NotImplementedError
52+
53+
5354try :
5455 from trepan .lib .deparse import deparse_offset
5556
@@ -63,6 +64,8 @@ def deparse_offset(_code, _name: str, _list_i: int, _) -> tuple:
6364
6465_with_local_varname = re .compile (r"_\[[0-9+]]" )
6566
67+ opc = xdis .get_opcode_module (PYTHON_VERSION_TRIPLE , PYTHON_IMPLEMENTATION )
68+
6669
6770def count_frames (frame : FrameType , count_start = 0 ) -> int :
6871 """Return a count of the number of frames"""
@@ -77,6 +80,7 @@ def count_frames(frame: FrameType, count_start=0) -> int:
7780 return 1000
7881 return count
7982
83+
8084def get_column_start_from_frame (frame : FrameType ) -> int :
8185 """
8286 Given a code frame, return the start column for that
@@ -85,6 +89,7 @@ def get_column_start_from_frame(frame: FrameType) -> int:
8589 """
8690 return get_column_start_from_code (frame .f_code , frame .f_lasti )
8791
92+
8893def get_column_start_from_code (code : CodeType , f_lasti : int ) -> int :
8994 """
9095 Given a code object, return the start column for that
@@ -100,6 +105,7 @@ def get_column_start_from_code(code: CodeType, f_lasti: int) -> int:
100105 return position_tuple [2 ]
101106 return - 1
102107
108+
103109_re_pseudo_file = re .compile (r"^<.+>" )
104110
105111
@@ -147,12 +153,14 @@ def deparse_source_from_code(code):
147153 return source_text
148154
149155
150- def format_function_name (frame : FrameType , style : str ) -> Tuple [Optional [str ], Optional [str ]]:
156+ def format_function_name (
157+ frame : FrameType , style : str
158+ ) -> Tuple [Optional [str ], Optional [str ]]:
151159 """
152160 Pick out the function name from ``frame`` and return both the name
153161 and the name styled according to ``style``
154162 """
155- if ( exec_type := is_eval_or_exec_stmt (frame ) ):
163+ if exec_type := is_eval_or_exec_stmt (frame ):
156164 funcname = get_call_function_name (frame )
157165 if funcname is None :
158166 funcname = exec_type
@@ -167,7 +175,9 @@ def format_function_name(frame: FrameType, style: str) -> Tuple[Optional[str], O
167175 return funcname , format_token (Function , funcname , style = style )
168176
169177
170- def format_function_and_parameters (frame : FrameType , debugger , style : str ) -> Tuple [bool , str ]:
178+ def format_function_and_parameters (
179+ frame : FrameType , debugger , style : str
180+ ) -> Tuple [bool , str ]:
171181 """ """
172182
173183 funcname , s = format_function_name (frame , style )
@@ -248,12 +258,12 @@ def format_return_and_location(
248258 elif s == "?()" :
249259 if is_eval_or_exec_stmt (frame ):
250260 s = "in exec"
251- # exec_str = get_exec_string(frame.f_back)
252- # if exec_str != None:
253- # filename = exec_str
254- # add_quotes_around_file = False
255- # pass
256- # pass
261+ exec_str = get_exec_string (frame .f_back )
262+ if exec_str is not None :
263+ filename = exec_str
264+ add_quotes_around_file = False
265+ pass
266+ pass
257267 elif not is_pseudo_file :
258268 s = "in file"
259269 pass
@@ -336,6 +346,25 @@ def frame2filesize(frame):
336346 return None , None
337347
338348
349+ def get_exec_string (frame : FrameType ) -> Optional [str ]:
350+ if (call_frame := frame .f_back ) is not None :
351+ offset = call_frame .f_lasti - 2
352+ code = call_frame .f_code
353+ while offset > 0 :
354+ inst = list (xdis .bytecode .get_logical_instruction_at_offset (
355+ code .co_code , offset , opc , constants = code .co_consts
356+ ))[0 ]
357+ if inst .opname in ("PRECALL" , "CACHE" ):
358+ pass
359+ elif inst .opname == "LOAD_CONST" :
360+ return inst .argval
361+ else :
362+ break
363+ offset -= 2
364+
365+ return None
366+
367+
339368def check_path_with_frame (frame , path ):
340369 my_size = os .stat (path ).st_size
341370 fs_size , bc_size = frame2filesize (frame )
@@ -365,9 +394,6 @@ def is_eval_or_exec_stmt(frame) -> Optional[str]:
365394 return None
366395
367396
368- opc = get_opcode (PYTHON_VERSION_TRIPLE , PYTHON_IMPLEMENTATION )
369-
370-
371397def get_call_function_name (frame ) -> Optional [str ]:
372398 """If f_back is looking at a call function, return
373399 the name for it. Otherwise, return None"""
@@ -575,6 +601,7 @@ def __init__(self):
575601 # print(pyc_file, getsourcefile(pyc_file))
576602
577603 from trepan .debugger import Trepan
604+
578605 m = MockDebugger ()
579606
580607 # For testing print_stack_entry()
@@ -597,6 +624,7 @@ def __init__(self):
597624 )
598625 )
599626 import sys
627+
600628 sys .exit (0 )
601629 # print("frame count: %d" % count_frames(frame))
602630 # print("frame count: %d" % count_frames(frame.f_back))
@@ -613,7 +641,11 @@ def fn(x):
613641 eval_str = is_eval_or_exec_stmt (frame .f_back )
614642 if eval_str :
615643 print (f"Caller is { eval_str } stmt" )
616- print (format_stack_entry (dd , (frame .f_back , frame .f_back .f_code .co_firstlineno )))
644+ print (
645+ format_stack_entry (
646+ dd , (frame .f_back , frame .f_back .f_code .co_firstlineno )
647+ )
648+ )
617649
618650 _ , mess = format_function_and_parameters (frame , dd , style = "tango" )
619651 print (mess )
0 commit comments