braindump_
  • Home
  • Categories
  • Tags
  • Archives

Making pdb work with psyco

Note

This is a rewrite of an old blog post from way back, it still might be useful so I'm throwing it on here.

Pdb, the standard python debugger, does not play nice with psyco. Or maybe it's the opposite. To illustrate this, let's start with a simple python program, that will surely provoke a breakpoint:

import psyco
psyco.full()

def foo(arg):
    """triggers a breakpoint"""
    i, j, k = [arg]*3
    1/0 
    return arg*2

if __name__ == '__main__':
    print foo(i)

A debugging session with pdb should look like that:

$ pdb hello_pdb.py
> /home/sevas/code/python/hello_pdb.py(1)()
-> import psyco
(Pdb) c
Traceback (most recent call last):
File "/usr/bin/pdb", line 1213, in main
pdb._runscript(mainpyfile)
File "/usr/bin/pdb", line 1138, in _runscript
self.run(statement, globals=globals_, locals=locals_)
File "/usr/lib/python2.5/bdb.py", line 366, in run
exec cmd in globals, locals
File "", line 1, in 
File "hello_pdb.py", line 11, in 
print foo(i)
File "hello_pdb.py", line 7, in foo
1/0
ZeroDivisionError: integer division or modulo by zero
Uncaught exception. Entering post mortem debugging
Running 'cont' or 'step' will restart the program
> /home/sevas/code/python/hello_pdb.py(7)foo()
-> 1/0
(Pdb) arg
*** NameError: name 'arg' is not defined
(Pdb) locals()
{}
(Pdb) q
Post mortem debugger finished. The hello.py will be restarted
> /home/sevas/code/python/hello_pdb.py(1)()

So, what happenned here?

  • We start a pdb debugging session on the hello_pdb.py file
  • the pdb command c (continue) starts the program
  • The program throws a ZeroDivisionError, pdb enters post-mortem debugging
  • We try to inspect the environment : the variable arg does not exist, the locals() dictionnary is empty.

The reason for that is, when psyco is active, all your psyco-compiled functions, along with their stack frames, are stored away in a secret place which poor pdb is not aware of.

At least 2 solutions exist. First, you could comment out the psyco.full() statement for every debug session. However, this isn't always an option, as some libraries always makes the use of psyco [1] if it is available. Uncommenting and recommenting in every third party library is not a reasonable option.

So, how do I seamlessly disable psyco while debugging a module with Pdb ? After some research, it seemed like the pydev eclipse extension had just the workaround I needed. First, we need a fake psyco module, implementing the psyco interface. Something like this :

"""
Psyco stub: should implement all the external API from psyco.
Taken from the pydev eclipse extension.
"""

def proxy(func, *args, **kwargs):
    return func

def bind(func, *args, **kwargs):
    return func

def unbind(func, *args, **kwargs):
    return func

def unproxy(func, *args, **kwargs):
    return func

def full(*args, **kwargs):
    pass

def log(*args, **kwargs):
    pass

def runonly(*args, **kwargs):
    pass

This is just a copy of the pydevd_psyco_stub.py file shipped with pydev, I'm not taking credit for anything here.

Next, we need to tell pdb to replace the psyco entry in sys.modules with that fake module. This is done through the .pdbrc configuration file. Conveniently, the .pdbrc file accepts plain python code, so it's like super easy and stuff. Here's my .pdbrc :

#.pdbrc
import sys
import os

sys.path += [os.getenv("HOME")]
import pdb_psyco_stub
sys.modules['psyco'] = pdb_psyco_stub
print '*** Info : Psyco module replaced with pdb_psyco_stub'

from pprint import pprint

And that's it.

A few remarks:

  • Both .pdbrc and pydevd_psyco_stub.py need to be in your home directory
  • On Windows, this means [2] putting them in an arbitrary folder, and then create a %HOME% environment variable pointing to that directory [3]
  • I strongly recommend the use of the pprint function (especially for inspecting your data while debugging)

So what does it mean for our test module from earlier ? Let me show it to you:

$ pdb hello_pdb.py
*** Info : Psyco module replaced with pdb_psyco_stub
> /home/sevas/code/python/hello_pdb.py(1)()
-> import psyco
(Pdb) c
Traceback (most recent call last):
...snip...
File "hello_pdb.py", line 7, in foo
1/0
ZeroDivisionError: integer division or modulo by zero
Uncaught exception. Entering post mortem debugging
Running 'cont' or 'step' will restart the program
> /home/sevas/code/python/hello_pdb.py(7)foo()
-> 1/0
(Pdb) arg
0
(Pdb) locals()
{'i': 0, 'k': 0, 'j': 0, 'arg': 0}
(Pdb)

Great success.

[1]This is actually my primary motivation
[2]Among other options, but I find this one easy enough
[3]And windows users should use Rapid Environment Editor for this.

Published

Sep 11, 2010

Last Updated

Jun 20, 2015

Category

Python

Tags

  • pdb 1
  • psyco 1
  • python 7

Contact

  • Powered by Pelican. Theme: Elegant by Talha Mansoor