When I am running inside an interactive session (in my case ipython
), and am currently inside a debugger (ipdb
or pdb
) I would like to be able to put a variable into the main interactive namespace from within the debugging namespace.
This is useful if my code crashes, but has already done significant work, some of which is salvageable to save time (for example, loading data from disk).
so what I'd like is something like this, for example:
>>> run -m my.module
loading data from disk...
done loading data.
processing data...
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
...
-> here something goes wrong during processing, but the loading of data worked fine... so I'd go into the debugger to check out what happened, and to see the loaded_data
:
>>> debug
ipdb> len(loaded_data)
100000
-> Then I would like to be able to save this variable to the interactive namespace for use outside the debugger, like so:
ipdb> save_to_interactive('loaded_data')
ipdb> exit
>>> len(loaded_data)
100000
You can accomplish this by getting a reference to the outer interpreter's stack frame, and writing to its frame globals.
Given a sample module with a breakpoint that kicks us into pdb:
my_module.py:
def fun(arg):
import pdb; pdb.set_trace()
print arg
Example demonstrating the basic concept:
>>> import my_module
>>> my_module.fun(1)
> /Users/lukasgraf/src/stackoverflow/my_module.py(3)fun()
-> print arg
(Pdb) import sys
(Pdb) sys._getframe(0)
<frame object at 0x1032ab290>
# this is the current frame
(Pdb) sys._getframe(0).f_globals['__name__']
'my_module'
# Next outer frame
(Pdb) sys._getframe(1).f_globals['__name__']
'pdb'
# etc...
# In this example, frame 10 happens to be
# the one from the outer interpreter
(Pdb) sys._getframe(10).f_globals['__name__']
'__main__'
So here's a quick and dirty function that walks up the stack looking for '__name__'
with a value of '__main__'
in frame globals:
debughelper.py:
import sys
# Be safe and define a maximum of frames we're trying to walk up
MAX_FRAMES = 20
def save_to_interactive(dct):
n = 0
# Walk up the stack looking for '__name__'
# with a value of '__main__' in frame globals
for n in range(MAX_FRAMES):
cur_frame = sys._getframe(n)
name = cur_frame.f_globals.get('__name__')
if name == '__main__':
# Yay - we're in the stack frame of the interactive interpreter!
# So we update its frame globals with the dict containing our data
cur_frame.f_globals.update(dct)
break
Usage:
>>> import my_module
>>> my_module.fun('foo')
> /Users/lukasgraf/src/stackoverflow/my_module.py(3)fun()
-> print arg
(Pdb) import debughelper
(Pdb) debughelper.save_to_interactive({'mykey': 42})
(Pdb) c
foo
# We continued PDB, so we're in the outer interpreter again
>>> print mykey
42
>>>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With