Ok, let's take a look at how my typical Python coding session starts off:

 

  1. Write a new awesome Python script and save it as "justdoit.py" all 1000 lines of it
  2. Run it to feel the power: "python justdoit.py"
  3. Groan as it crashes with a ValueError at justdoit.py:45
  4. Pull up the source and line -- hmm, what am I getting back from that library call?  Must not be what I thought!
  5. Add a print statement and then run it again and feel the power!
  6. Don't pass go, don't collect $200, go directly to step 3

 

That gets tedious after a while, particularly when the script is connecting to a remote device, making more than a handful of calls and takes a full 30 seconds to get to that crash site.  (30 seconds is just long enough to be a drag but really not long enough to justify checking the headlines on hacker news...)

 

After too many iterations of this (define stupid?), I admit defeat.... I need to try something different.

IPython to the rescue

 

In it's simplest form, you can use IPython as a shell just like python.  That gives you some key new features above and beyond the standard python shell:

 

  • tab completion - hit [tab] after typing some text, it will automatically complete matching text and display options.  This works for variables, functions, methods, classes, you name it
  • history - your history is saved, even if you exit and restart.  You start to rely on history when you just want to play around with a few new functions.

 

Ok, so how does this help with my debugging session?  Just put this little gem where the problem is and I can drop into an IPython session -- with full context of variables for inspection:

 

from IPython import embed
embed()


 

Let's see this in action.  Take the code (from the FlyScript Python SDK examples/profiler/top-ports.py):

 

from rvbd.profiler import *
from rvbd.profiler.utils.app import ProfilerApp
from rvbd.profiler.filters import TimeFilter, TrafficFilter
import pprint

def main(app):
    # Create and run a traffic summary report of all server ports in use
    # by hosts in 10/8
    report = TrafficSummaryReport(app.profiler)

    # Run the report
    report.run( groupby = app.profiler.groupbys.port,
        columns = [app.profiler.columns.key.protoport_name,
                    app.profiler.columns.value.avg_bytes] )

    # Retrieve and print data
    data = report.get_data()

    from IPython import embed
    embed()

    print "Report data has %d rows" % len(data)



ProfilerApp(main).run()


 

See lines 19 and 20?  This is where the magic occurs.  When I run this script, it will connect to my Profiler, create and run a traffic report, return the data and the drop into an IPython shell:

 

--> python /tmp/test.py tm08-1.lab.nbttech.com -u admin -p admin
Python 2.7.3 (default, Oct 22 2012, 06:12:32)
Type "copyright", "credits" or "license" for more information.

IPython 0.13 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

>>>


 

At this point I'm in the IPython shell right after the 'report.get_data()' line.  Now let's examine some of the local variables:

 

>>> print report
<rvbd.profiler.report.TrafficSummaryReport object at 0x10532df90>

>>> print len(data)
334

>>> data[0]
['snmp', 9327.5625]


 

Note that I can even call functions and methods:

 

>>> print report.get_legend()[0]
<rvbd.profiler._types.Column(id=19, key=protoport_name, iskey=True label=Port Name)>


 

I can do whatever I like in this session, including modifying variables.  When I exit, the script continues, as if the IPython session never happened (any variable changes are discarded):

 

>>> exit

Report data has 114 rows


 

This is really just scratching the surface of what IPython can do.  If you like what you've seen so far, I leave you with a little teaser: IPython Notebooks