66
77__all__ = ["BreakpointManager" , "Breakpoint" ]
88
9- import os .path
10- from types import CodeType
9+ import os .path as osp
10+ from types import CodeType , ModuleType
1111from typing import Optional
12+ from xdis import load_module
1213
1314
1415class Breakpoint :
@@ -38,7 +39,7 @@ def __init__(
3839
3940 self .filename = filename
4041 if filename :
41- self .filename = os . path .realpath (filename )
42+ self .filename = osp .realpath (filename )
4243
4344 # Needed if funcname is not None.
4445 self .func_first_executable_line = None
@@ -70,12 +71,21 @@ def __str__(self):
7071 offset_str = " any"
7172 else :
7273 offset_str = "%4d" % self .offset
73- msg = "%-4dbreakpoint %s %s at %s:%d" % (
74+ if self .offset is None :
75+ offset_str = " any"
76+ else :
77+ offset_str = "%4d" % self .offset
78+ if self .line is None :
79+ line_str = ""
80+ else :
81+ line_str = ":%d" % self .line
82+
83+ msg = "%-4dbreakpoint %s %s at %s%s" % (
7484 self .number ,
7585 disp ,
7686 offset_str ,
7787 self .filename ,
78- self . line ,
88+ line_str ,
7989 )
8090 if self .condition :
8191 msg += f"\n \t stop only if { self .condition } "
@@ -175,20 +185,29 @@ def add_breakpoint(
175185 """
176186 bpnum = len (self .bpbynumber )
177187 if filename :
178- filename = os . path .realpath (filename )
188+ filename = osp .realpath (filename )
179189
180190 assert (
181191 isinstance (filename , str ) or func is not None
182192 ), "You must either supply a filename or give a line number"
183193
184194 if isinstance (func , CodeType ):
185195 code = func
196+ elif isinstance (func , ModuleType ):
197+ if hasattr (func , "__cached__" ):
198+ # FIXME: we can probably do better hooking into importlib
199+ # or something lower-level
200+ _ , _ , _ , code , _ , _ , _ = load_module (func .__cached__ , fast_load = True , get_code = True )
201+ else :
202+ print (f"Don't know what to do with frozen module { func } " )
203+ return
186204 elif hasattr (func , "__code__" ):
187205 code = func .__code__
188206 elif hasattr (func , "f_code" ):
189207 code = func .f_code
190208 else :
191209 print (f"Don't know what to do with { func } , { type (func )} " )
210+ return
192211 brkpt = Breakpoint (bpnum , filename , lineno , temporary , condition , code , offset )
193212
194213 # Build the internal lists of breakpoints
@@ -392,15 +411,34 @@ def checkfuncname(brkpt: Breakpoint, frame):
392411# Demo
393412
394413if __name__ == "__main__" :
414+ def foo (bp , bpmgr ):
415+ frame = inspect .currentframe ()
416+ assert frame
417+ print (f"Stop at bp2: { checkfuncname (bp , frame )} " )
418+ # frame.f_lineno is constantly updated. So adjust for the
419+ # line difference between the add_breakpoint and the check.
420+ bp3 = bpmgr .add_breakpoint (__file__ , 0 , frame .f_lineno + 1 , func = foo )
421+ print (f"Stop at bp3: { checkfuncname (bp3 , frame )} " )
422+ return
423+
424+ def bar () -> int :
425+ frame = inspect .currentframe ()
426+ assert frame is not None
427+ return frame .f_lasti
428+
395429 bpmgr = BreakpointManager ()
396430 print (bpmgr .last ())
397- bp = bpmgr .add_breakpoint ("foo" , 0 , 5 )
431+ line_number = foo .__code__ .co_firstlineno
432+ bp = bpmgr .add_breakpoint (__file__ , line_number , func = foo )
398433 print (bp .icon_char ())
399434 print (bpmgr .last ())
400435 print (repr (bp ))
401436 print (str (bp ))
402437 bp .disable ()
403438 print (str (bp ))
439+ import xdis
440+ bp = bpmgr .add_breakpoint (xdis .__file__ , offset = 0 , func = xdis )
441+ print (bp )
404442 for i in 10 , 1 :
405443 status , msg = bpmgr .delete_breakpoint_by_number (i )
406444 print (
@@ -411,27 +449,19 @@ def checkfuncname(brkpt: Breakpoint, frame):
411449 frame = inspect .currentframe ()
412450 print (f"Stop at bp: { checkfuncname (bp , frame )} " )
413451
414- def foo (bp , bpmgr ):
415- frame = inspect .currentframe ()
416- assert frame
417- print (f"Stop at bp2: { checkfuncname (bp , frame )} " )
418- # frame.f_lineno is constantly updated. So adjust for the
419- # line difference between the add_breakpoint and the check.
420- bp3 = bpmgr .add_breakpoint ("foo" , 0 , frame .f_lineno + 1 )
421- print (f"Stop at bp3: { checkfuncname (bp3 , frame )} " )
422- return
423-
424452 bp2 = bpmgr .add_breakpoint (None , None , - 1 , False , None , foo )
425453 foo (bp2 , bpmgr )
426- bp3 = bpmgr .add_breakpoint ("foo" , 5 , 2 , temporary = True )
454+ bp3 = bpmgr .add_breakpoint (__file__ , line_number , 0 , temporary = True , func = foo )
427455 print (bp3 .icon_char ())
428456 print (bpmgr .bpnumbers ())
429457
430- bp = bpmgr .add_breakpoint ("bar" , 10 , 3 )
458+ line_number = bar .__code__ .co_firstlineno
459+ bp = bpmgr .add_breakpoint (__file__ , line_number , 0 , func = bar )
431460 filename = bp .filename
432461 assert filename
462+ line_number = bar ()
433463 for i in range (3 ):
434- bp = bpmgr .add_breakpoint ("bar" , 2 , 6 )
435- print (bpmgr .delete_breakpoints_by_lineno (filename , 6 ))
436- print (bpmgr .delete_breakpoints_by_lineno (filename , 6 ))
464+ bp = bpmgr .add_breakpoint (None , None , line_number , func = bar )
465+ print (bpmgr .delete_breakpoints_by_lineno (filename , line_number ))
466+ print (bpmgr .delete_breakpoints_by_lineno (filename , line_number ))
437467 print (bpmgr .bpnumbers ())
0 commit comments