Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

save variable to interactive namespace from python debugger

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
like image 567
Robert Muil Avatar asked Feb 16 '23 15:02

Robert Muil


1 Answers

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
>>>
like image 158
Lukas Graf Avatar answered Feb 18 '23 15:02

Lukas Graf