Skip to content

Latest commit

 

History

History
168 lines (119 loc) · 5.78 KB

debugging.rst

File metadata and controls

168 lines (119 loc) · 5.78 KB

Debugging

Using PDB to Debug Your Application

pdb is an interactive tool that comes with Python, which allows you to break your program at an arbitrary point, examine values, and step through code. It's often much more useful than print statements or logging statements to examine program state. You can place a pdb.set_trace() statement in your Pyramid application at a place where you'd like to examine program state. When you issue a request to the application, and that point in your code is reached, you will be dropped into the pdb debugging console within the terminal that you used to start your application.

There are lots of great resources that can help you learn PDB.

Below is a debugging scenario using PDB to debug Pyramid.

Debugging Pyramid

This tutorial provides a brief introduction to using the python debugger (pdb) for debugging pyramid applications.

This scenario assume you've created a Pyramid project already. The scenario assumes you've created a Pyramid project named buggy using the alchemy scaffold.

Introducing PDB

  • This single line of python is your new friend:

    import pdb;  pdb.set_trace()
    
  • As valid python, that can be inserted practically anywhere in a Python source file. When the python interpreter hits it - execution will be suspended providing you with interactive control from the parent TTY.

PDB Commands

  • pdb exposes a number of standard interactive debugging commands, including:

    Documented commands (type help <topic>):
    ========================================
    EOF    bt         cont      enable  jump  pp       run      unt
    a      c          continue  exit    l     q        s        until
    alias  cl         d         h       list  quit     step     up
    args   clear      debug     help    n     r        tbreak   w
    b      commands   disable   ignore  next  restart  u        whatis
    break  condition  down      j       p     return   unalias  where
    
    Miscellaneous help topics:
    ==========================
    exec  pdb
    
    Undocumented commands:
    ======================
    retval  rv
    

Debugging Our buggy App

  • Back to our demo buggy application we generated from the alchemy scaffold, lets see if we can learn anything debugging it.
  • The traversal documentation describes how pyramid first acquires a root object, and then descends the resource tree using the __getitem__ for each respective resource.

Huh?

  • Let's drop a pdb statement into our root factory object's __getitem__ method and have a look. Edit the project's models.py and add the aforementioned pdb line in MyModel.__getitem__

    def __getitem__(self, key):
        import pdb; pdb.set_trace()
        session = DBSession()
        # ...
  • Restart the Pyramid application, and request a page. Note the request requires a path to hit our break-point:

    http://localhost:6543/   <- misses the break-point, no traversal
    http://localhost:6543/1  <- should find an object
    http://localhost:6543/2  <- does not
    
  • For a very simple case, attempt to insert a missing key by default. Set item to a valid new MyModel in MyRoot.__getitem__ if a match isn't found in the database

    item = session.query(MyModel).get(id)
    if item is None:
        item = MyModel(name='test %d'%id, value=str(id))  # naive insertion
  • Move the break-point within the if clause to avoid the false positive hits

    if item is None:
        import pdb; pdb.set_trace()
        item = MyModel(name='test %d'%id, value=str(id))  # naive insertion
  • Run again, note multiple request to the same id continue to create new MyModel instances. That's not right!

  • Ah, of course, we forgot to add the new item to the session. Another line added to our __getitem__ method

    if item is None:
        import pdb; pdb.set_trace()
        item = MyModel(name='test %d'%id, value=str(id))
        session.add(item)
  • Restart and test. Observe the stack; debug again. Examine the item returning from MyModel:

    (pdb) session.query(MyModel).get(id)
    
  • Finally, we realize the item.id needs to be set as well before adding

    if item is None:
        item = MyModel(name='test %d'%id, value=str(id))
        item.id = id
        session.add(item)
  • Many great resources can be found describing the details of using pdb. Try the interactive help (hit 'h') or a search engine near you.

Note

There is a well known bug in PDB in UNIX, when user cannot see what he is typing in terminal window after any interruption during PDB session (it can be caused by CTRL-C or when the server restarts automatically). This can be fixed by launching any of this commands in broken terminal: reset, stty sane. Also one can add one of this commands into ~/.pdbrc file, so they will be launched before PDB session:

from subprocess import Popen
Popen(["stty", "sane"])