Ok, let's take a look at how my typical Python coding session starts off:
- Write a new awesome Python script and save it as "justdoit.py" all 1000 lines of it
- Run it to feel the power: "python justdoit.py"
- Groan as it crashes with a ValueError at justdoit.py:45
- Pull up the source and line -- hmm, what am I getting back from that library call? Must not be what I thought!
- Add a print statement and then run it again and feel the power!
- 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 ['snmp', 9327.5625]
Note that I can even call functions and methods:
>>> print report.get_legend() <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
ft-sm-ipython.png 5.5 K