@@ -1095,42 +1095,118 @@ def runScript(cmds=[], wait=False, G2frame=None):
10951095 sys .exit ()
10961096
10971097def IPyBreak_base (userMsg = None ):
1098- '''A routine that invokes an IPython session at the calling location
1098+ '''A routine that invokes an IPython session at the calling location.
1099+ Called by a call to :func:`IPyBreak` or command ``breakpoint()``.
10991100 This routine is only used when debug=True is set in the configuration
1100- settings
1101+ settings.
11011102 '''
11021103 savehook = sys .excepthook # save the exception hook
11031104 try :
1104- from IPython .terminal .embed import InteractiveShellEmbed
1105+ import IPython .core
1106+ # get the Ipython shell routine
1107+ if IPython .core .getipython .get_ipython () is None :
1108+ ipshell = IPython .terminal .embed .InteractiveShellEmbed .instance (banner1 = '' )
1109+ else : # older IPython (still needed?)
1110+ ipshell = IPython .terminal .embed .InteractiveShellEmbed ()
1111+ import inspect
11051112 except ImportError :
1106- try :
1107- # try the IPython 0.12 approach
1108- from IPython .frontend .terminal .embed import InteractiveShellEmbed
1109- except ImportError :
1110- print ('IPython InteractiveShellEmbed not found' )
1111- return
1112- import inspect
1113- #from IPython import __version__
1114- #if __version__.startswith('8.12.'): # see https://github.com/ipython/ipython/issues/13966
1115- from IPython .core import getipython
1116- if getipython .get_ipython () is None :
1117- ipshell = InteractiveShellEmbed .instance ()
1118- else :
1119- ipshell = InteractiveShellEmbed ()
1113+ return
1114+
1115+ stack = inspect .stack () # Get the full stack
1116+ current_level = [1 ] # mutable so inner function can modify it
1117+
1118+ def up (levels = 1 ):
1119+ '''Move the IPython shell environment up the call stack.
1120+ Initially coded by Claude Sonnet 4.5
1121+ '''
1122+ new_level = current_level [0 ] + levels
1123+ if new_level < len (stack ):
1124+ current_level [0 ] = new_level
1125+ frame = stack [current_level [0 ]].frame
1126+ print (f"Moving to: { stack [current_level [0 ]].filename } :{ stack [current_level [0 ]].lineno } in { stack [current_level [0 ]].function } " )
1127+ # Replace the IPython namespace (not update), but preserve IPython internals
1128+ # Save IPython internal variables and built-in commands
1129+ ipython_preserve = {k : v for k , v in ipshell .user_ns .items ()
1130+ if k .startswith ('_' ) or k in ('exit' , 'quit' , 'get_ipython' )}
1131+ ipshell .user_ns .clear ()
1132+ ipshell .user_ns .update (ipython_preserve ) # Restore IPython internals
1133+ ipshell .user_ns .update (frame .f_globals )
1134+ ipshell .user_ns .update (frame .f_locals )
1135+ ipshell .user_ns ['TB' ] = TB
1136+ ipshell .user_ns ['up' ] = up
1137+ ipshell .user_ns ['down' ] = down
1138+ ipshell .user_ns ['where' ] = where
1139+ else :
1140+ print (f"Already at top of stack (level { current_level [0 ]} of { len (stack )- 1 } )" )
1141+
1142+ def down (levels = 1 ):
1143+ '''Move the IPython shell environment down the call stack.
1144+ Initially coded by Claude Sonnet 4.5
1145+ '''
1146+ new_level = current_level [0 ] - levels
1147+ if new_level >= 0 :
1148+ current_level [0 ] = new_level
1149+ frame = stack [current_level [0 ]].frame
1150+ print (f"Moving to: { stack [current_level [0 ]].filename } :{ stack [current_level [0 ]].lineno } in { stack [current_level [0 ]].function } " )
1151+ # Replace the IPython namespace (not update), but preserve IPython internals
1152+ # Save IPython internal variables and built-in commands
1153+ ipython_preserve = {k : v for k , v in ipshell .user_ns .items ()
1154+ if k .startswith ('_' ) or k in ('exit' , 'quit' , 'get_ipython' )}
1155+ ipshell .user_ns .clear ()
1156+ ipshell .user_ns .update (ipython_preserve ) # Restore IPython internals
1157+ ipshell .user_ns .update (frame .f_globals )
1158+ ipshell .user_ns .update (frame .f_locals )
1159+ ipshell .user_ns ['TB' ] = TB
1160+ ipshell .user_ns ['up' ] = up
1161+ ipshell .user_ns ['down' ] = down
1162+ ipshell .user_ns ['where' ] = where
1163+ else :
1164+ print (f"Already at bottom of stack (level 0)" )
1165+
1166+ def where ():
1167+ '''Show current position in stack related to up/down commands.
1168+ Initially coded by Claude Sonnet 4.5 & revised to reverse display order.
1169+ '''
1170+ i = len (stack )
1171+ for frame_info in reversed (stack ):
1172+ i -= 1
1173+ marker = ">>>" if i == current_level [0 ] else " "
1174+ print (f"{ marker } [{ i } ] { frame_info .filename } :{ frame_info .lineno } in { frame_info .function } " )
1175+
1176+ def TB (depth = None ):
1177+ '''Show a traceback (to the optional specified depth) to how we got to
1178+ the breakpoint. Omit the current level as the calling level to here
1179+ and above is likely all that we would be interested in.
1180+ '''
1181+ if depth is not None : depth += 1
1182+ for frame_info in reversed (stack [1 :depth ]):
1183+ print (f' File "{ frame_info .filename } ", line { frame_info .lineno } , in { frame_info .function } ' )
1184+ if frame_info .code_context :
1185+ print (f' { frame_info .code_context [0 ].strip ()} ' )
11201186
11211187 frame = inspect .currentframe ().f_back
11221188 msg = 'Entering IPython console inside {0.f_code.co_filename} at line {0.f_lineno}\n ' .format (frame )
1189+ msg += '\n [up()/down()/where() to navigate stack; TB()/TB(n) for n level tracebacks]'
11231190 if userMsg : msg += userMsg
1191+
1192+ # Add commands to shell namespace
1193+ locals = frame .f_locals
1194+ locals ['TB' ] = TB
1195+ locals ['up' ] = up
1196+ locals ['down' ] = down
1197+ locals ['where' ] = where
1198+
11241199 # globals().update(locals()) # This might help with vars inside list comprehensions, etc.
1125- ipshell (msg ,stack_depth = 2 ) # Go up one level, to see the calling routine
1200+ ipshell (msg ,stack_depth = 2 , local_ns = locals , global_ns = frame . f_globals )
11261201 sys .excepthook = savehook # reset IPython's change to the exception hook
11271202
11281203def exceptHook (* args ):
11291204 '''A routine to be called when an exception occurs. It prints the traceback
11301205 with fancy formatting and then calls an IPython shell with the environment
11311206 of the exception location.
11321207
1133- This routine is only used when debug=True is set in the configuration settings
1208+ This routine is only used when debug=True is set in the configuration
1209+ settings.
11341210 '''
11351211 import IPython .core
11361212 savehook = sys .excepthook # save the exception hook
@@ -1139,7 +1215,6 @@ def exceptHook(*args):
11391215 tb_formatter = IPython .core .ultratb .ListTB () # better for windows?
11401216 else :
11411217 tb_formatter = IPython .core .ultratb .FormattedTB ()
1142- print () # blank line
11431218 print (tb_formatter .text (* args ,- 1 ),end = '' ) # show only last routine
11441219 # get the Ipython shell routine
11451220 if IPython .core .getipython .get_ipython () is None :
0 commit comments