IPython and virtualenv

IPython is a feature-rich interactive shell for Python developers. Virtualenv creates isolated development environments so you can test or install packages without introducing conflicts. This month, Doug examines how both tools can make your life a little easier.

Last month, around the time I started working on my January column, I posted to my blog asking readers to tell me about their favorite Python development tools. I received several good tips from a variety of developers. While some of the responses were scattered all over the map, there were two tools that stood out from the rest with their popularity. The overwhelming favorite of all commenters was IPython, an alternate interactive shell. I had heard about IPython previously from co-workers, but never really looked at it very closely. After the responses to my post, however, I decided I needed to give it a more serious review. The other tool mentioned several times was virtualenv, a “Virtual Python Environment builder”. I was already a virtualenv user, so I was pleased to see it mentioned. Let’s look at virtualenv first, and then use it to test IPython.

virtualenv

Ian Bicking’s virtualenv creates a “private” Python environment, or sandbox, complete with your own interpreter and site-packages directory. Having a private sandbox like this is useful in situations where you don’t have root access to install packages into the global site-packages area, or where you want to test newer versions of modules without corrupting your development system.

When you run virtualenv, it sets up a fresh sandbox version of Python in a directory you specify by copying or linking files from your default installation to create new bin and lib directories. The sys.path for the new environment is configured based on options you provide when you create it. By default, the original Python environment is included at the end of the search path. This configuration allows you to share common modules in a global site-packages directory, so they can be used in several projects. Listing 1 shows some basic details from a default virtualenv environment. As you can see starting on line 31, the sys.path has the user library directories before the global versions of those same directories and the local lib/python2.5 comes before the global lib/python2.5, etc.

Listing 1

$ pwd
/Users/dhellmann/Documents/PythonMagazine/tmp

$ virtualenv default
New python executable in default/bin/python
Installing setuptools....................done.

$ find default -type d
default
default/bin
default/lib
default/lib/python2.5
default/lib/python2.5/distutils
default/lib/python2.5/site-packages

$ ls -l default/bin
total 96
-rw-r--r--  1 dhellmann  501   1213 Jan 31 09:30 activate
-rwxr-xr-x  1 dhellmann  501    329 Jan 31 09:30 easy_install
-rwxr-xr-x  1 dhellmann  501    337 Jan 31 09:30 easy_install-2.5
-rwxr-xr-x  1 dhellmann  501  30028 Jan 31 09:30 python
lrwxr-xr-x  1 dhellmann  501      6 Jan 31 09:30 python2.5 -> python

$ default/bin/python
Python 2.5.1 (r251:54869, Apr 18 2007, 22:08:04)
[GCC 4.0.1 (Apple Computer, Inc. build 5367)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.prefix
'/Users/dhellmann/PythonMagazine/default/bin/..'
>>> pprint.pprint(sys.path)
['',
 '/Users/dhellmann/PythonMagazine/default/lib/python2.5/site-packages/setuptools-0.6c7-py2.5.egg',
 '/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/site-packages/setuptools-0.6c5-py2.5.egg',
 '/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/site-packages/virtualenv-0.9.2-py2.5.egg',
 '/Users/dhellmann/PythonMagazine/default/lib/python25.zip',
 '/Users/dhellmann/PythonMagazine/default/lib/python2.5',
 '/Users/dhellmann/PythonMagazine/default/lib/python2.5/plat-darwin',
 '/Users/dhellmann/PythonMagazine/default/lib/python2.5/plat-mac',
 '/Users/dhellmann/PythonMagazine/default/lib/python2.5/plat-mac/lib-scriptpackages',
 '/Users/dhellmann/PythonMagazine/default/lib/python2.5/lib-tk',
 '/Users/dhellmann/PythonMagazine/default/lib/python2.5/lib-dynload',
 '/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5',
 '/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/lib-tk',
 '/Users/dhellmann/PythonMagazine/default/lib/python2.5/site-packages',
 '/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/site-packages',
 '/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/site-packages/Py2App',
 '/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/site-packages/PyObjC',
]

Alternately, if you specify --no-site-packages when creating the new environment, you end up with a completely stand-alone environment with no reference back to the global site-packages. Listing 2 shows the search path for a private environment without the global site-packages library directories. Notice that although the global standard library directory is still included, third-party libraries are supposed to go into site-packages, so that should not cause any confusion or conflict when importing modules.

Listing 2

$ virtualenv --no-site-packages private
New python executable in private/bin/python
Installing setuptools............done.

$ private/bin/python
Python 2.5.1 (r251:54869, Apr 18 2007, 22:08:04)
[GCC 4.0.1 (Apple Computer, Inc. build 5367)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.prefix
'/Users/dhellmann/PythonMagazine/private/bin/..'
>>> import pprint
>>> pprint.pprint(sys.path)
['',
 '/Users/dhellmann/PythonMagazine/private/lib/python2.5/site-packages/setuptools-0.6c7-py2.5.egg',
 '/Users/dhellmann/PythonMagazine/private/lib/python25.zip',
 '/Users/dhellmann/PythonMagazine/private/lib/python2.5',
 '/Users/dhellmann/PythonMagazine/private/lib/python2.5/plat-darwin',
 '/Users/dhellmann/PythonMagazine/private/lib/python2.5/plat-mac',
 '/Users/dhellmann/PythonMagazine/private/lib/python2.5/plat-mac/lib-scriptpackages',
 '/Users/dhellmann/PythonMagazine/private/lib/python2.5/lib-tk',
 '/Users/dhellmann/PythonMagazine/private/lib/python2.5/lib-dynload',
 '/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5',
 '/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/lib-tk',
 '/Users/dhellmann/PythonMagazine/private/lib/python2.5/site-packages']
>>>

In both examples so far, I have run the local version of the interpreter directly so I could examine the module search path. The sandbox created by virtualenv also includes a simple script called activate to set up your current shell to use the files in that sandbox by default. When you source activate, it sets up several environment variables in your current shell. For example, VIRTUAL_ENV is set to point to the root of the sandbox, the directory you specified when you created it. Your shell PATH variable is also updated to start with the sandbox’s bin directory. This makes the sandbox version of the interpreter the default, so when you run scripts they use the libraries from the sandbox. Any commands you install with easy_install also end up in $VIRTUAL_ENV/bin, making it easier to run those programs since you don’t have to remember to type the full path to the program. As a reminder that you are using a custom environment, your command prompt also changes to show the name of the virtual environment sandbox.

$ source default/bin/activate
(default)$ echo $VIRTUAL_ENV
/Users/dhellmann/PythonMagazine/default
(default)$ which python
/Users/dhellmann/PythonMagazine/default/bin/python

When I started as Technical Editor here at Python Magazine, I had some concerns about installing all of the dependencies I would need to have in order to test code associated with articles submitted for review by our authors. I knew some articles would have conflicting requirements, and managing those packages was going to be a bit of a hassle. Originally, I planned to set up a virtual machine so I could at least isolate code for the magazine from my own development environment, and wiping it clean between issues (or even articles) would be straightforward. Using virtualenv has turned out to be so much easier that I haven’t bothered with the VM. I typically create a separate environment for each article in a sandbox, verify the dependencies listed by the author along with their code, then delete the entire sandbox. No muss, no fuss.

IPython

IPython is an enhanced interactive shell, intended to be used as a replacement for the standard interactive Python interpreter. Started by Fernando Pérez as a set of enhancements to the basic interpreter prompt, it has grown to include a host of other powerful features and contributions from several developers. Many of the features of IPython derive from its background in scientific computing, but it can be useful outside of scientific fields for anyone who works with Python as well.

The installation instructions have the usual steps (note the use of sudo to perform the final step with admin privileges):

$ tar -xvzf ipython-0.7.3.tar.gz
$ cd ipython-0.7.3
$ python setup.py build
$ sudo python setup.py install

To illustrate how I usually review contributions for this column, though, I decided to use easy_install, even though it wasn’t listed as an option, and virtualenv. Listing 3 shows how I created the new sandbox and then installed IPython into it.

Listing 3

$ virtualenv ipython
New python executable in ipython/bin/python
Installing setuptools............done.
$ source ipython/bin/activate
(ipython)$

(ipython)$ cd $VIRTUAL_ENV
(ipython)$ ls
bin     lib

(ipython)$ easy_install ipython
Searching for ipython
Reading http://pypi.python.org/simple/ipython/
Reading http://ipython.scipy.org
Reading http://ipython.scipy.org/dist
Best match: ipython 0.8.2
Downloading http://ipython.scipy.org/dist/ipython-0.8.2-py2.5.egg
Processing ipython-0.8.2-py2.5.egg
creating /Users/dhellmann/PythonMagazine/PythonEnv/ipython/lib/python2.5/site-packages/ipython-0.8.2-py2.5.egg
Extracting ipython-0.8.2-py2.5.egg to /Users/dhellmann/PythonMagazine/PythonEnv/ipython/lib/python2.5/site-packages
Adding ipython 0.8.2 to easy-install.pth file
Installing ipython script to /Users/dhellmann/PythonMagazine/PythonEnv/ipython/bin
Installing pycolor script to /Users/dhellmann/PythonMagazine/PythonEnv/ipython/bin

Installed /Users/dhellmann/PythonMagazine/PythonEnv/ipython/lib/python2.5/site-packages/ipython-0.8.2-py2.5.egg
Processing dependencies for ipython
Finished processing dependencies for ipython
(ipython)$
(ipython)$ which ipython
/Users/dhellmann/PythonMagazine/PythonEnv/ipython/bin/ipython

Prompt History

Now that IPython is installed, let’s start it up and take it for a spin. Listing 4 shows the “startup screen”. The first thing to notice, after the boiler-plate help text, is the different prompt, In [1]:. If your terminal supports it, the prompt will be in color (mine is green). The different prompt is the first indication of one of the powerful features of IPython. As you issue instructions, IPython tracks every input and output expression used in your interactive session. You can refer back to them to build new expressions in a couple of different ways. Each input has a history “number”, similar to the way many Unix shells refer to your command line history. In the case of IPython, though, this is extended to allow you to refer back to the outputs as well, without recomputing them. This feature is especially useful if you are experimenting with code and creating lots of objects with small variations in their settings.

Listing 4

(ipython)$ ipython
Python 2.5.1 (r251:54869, Apr 18 2007, 22:08:04)
Type "copyright", "credits" or "license" for more information.

IPython 0.8.2 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object'. ?object also works, ?? prints more.

In [1]:

There are two syntaxes for using back references. You can use built-in In and Out variables as lists, and index into them. Or if you only want the output value, you can use the shorthand underscore notation (_). The values from In are the strings you input, while the values from Out are the results of evaluating those expressions. For example:

In [1]: 5*5

Out[1]: 25

In [2]: In[1]

Out[2]: u'5*5n'

In [3]: _1

Out[3]: 25

In [4]: Out[3]

Out[4]: 25

In [5]: _4 * 5

Out[5]: 125

In [6]:

If you are comfortable working at the interactive prompt in this way, but want to record what you do for future reference after you close your session, you can use IPython’s logging feature to write the session to a file. To activate the log, use the control command %logstart, as illustrated in Listing 5. The output file is a Python source file, so it is easy to clean it up and turn it into a “real” module when you are done experimenting.

Listing 5

In [6]: %logstart
Activating auto-logging. Current session state plus future input saved.
Filename       : ipython_log.py
Mode           : rotate
Output logging : False
Raw input log  : False
Timestamping   : False
State          : active

In [7]: a = 5

In [8]: b = 6

In [9]: c = a * b

In [10]: c

Out[10]: 30

In [11]: d = [ a, b, c]

In [12]: d

Out[12]: [5, 6, 30]

In [13]: %logstop

If we examine the file ipython_log.py (Listing 6), we can see that everything I typed was logged to the file. The command %logstart is just one of many magic control commands for giving IPython instructions about how you want to interact with it.

Listing 6

#log# Automatic Logger file. *** THIS MUST BE THE FIRST LINE ***
#log# DO NOT CHANGE THIS LINE OR THE TWO BELOW
#log# opts = Struct({'__allownew': True, 'logfile': 'ipython_log.py'})
#log# args = []
#log# It is safe to make manual edits below here.
#log#-----------------------------------------------------------------------
5*5
In[1]
_1
Out[3]
_4 * 5
_ip.magic("logstart ")

a = 5
b = 6
c = a * b
c
d = [a, b, c]
d

Shell Commands

In addition to standard Python expressions and magic commands, IPython supports “shelling out” to run commands in your normal command line shell. This feature, and the fact that some shell commands are built into IPython, enables some users to use IPython as their primary command line shell, instead of a more traditional shell such as bash. Notice the difference in this example between !pwd and pwd – the latter results in an output value being saved to Out.

In [1]: cd
/Users/dhellmann

In [2]: !pwd
/Users/dhellmann

In [3]: pwd

Out[3]: '/Users/dhellmann'

IPython is smart enough that many system commands can be run directly from the command prompt without the ! prefix, and any can be run with the prefix. The text output of the commands can be captured, just as with more traditional shells, so the values can be used by your Python code.

Help Is Never Far Away

With all of the additional features, a new user may feel that the complexity of IPython can be a bit much to take in at once. As with any good interactive system, help is available right there in the shell. The %quickref command prints a quick reference card that lists many of the magic commands, special operators, and other features with examples and basic instructions for using them. More specific details are available when you use the ? operator. By itself, ? displays a help document for IPython itself. It can be combined with dynamically created objects to show help text for just about anything in the system (classes, functions, methods, objects, etc.). Providing built-in help like this makes it easy to find method signatures and docstrings when you are in the middle of your work, without having to shift gears to search through online documentation.

Debugging

IPython makes debugging your scripts easier by letting you run them in the context of the interactive session, then retaining all of the script state once it is completed. This makes it easy to go back and look at objects created by the script as though you had typed all of the commands into the interactive interpreter directly. For example, take this small bit of sample code in my_sample.py:

#!/usr/bin/env python
my_list = []
my_list.append('first')
my_list.append('second')
print 'End of sample'

When run through IPython, the output looks like this:

In [1]: %run my_sample.py
End of sample

In [2]: my_list

Out[2]: ['first', 'second']

All of the globals from the script are available to the interactive session, so I can inspect them to see what their settings are. If the script raises an exception, the source code where the error originates is displayed along with information about the exception. Working this way shortens your development cycle because each time you %run a script, it is reloaded – you don’t have to restart the interpreter to reset its state.

Advanced Uses

While IPython does have features that make it well suited as an interactive shell for any Python programmer, it is also being designed with more advanced purposes in mind. For example, it is possible to add new syntax profiles so that it understands your own domain-specific language, making it a natural choice for an embedded interpreter. For example, IPython comes with a profile to let it understand units of measure frequently used in physics computations. This lets you type mass = 3 kg and have it translated to an object that knows about the 3 as well as “kilograms” to represent a mass.

Another advanced feature is the ability to control and interact with GUI programs. Usually, GUIs run an infinite event loop to collect input events, process them, and then update the screen. There are bindings available to allow IPython to control most of the popular GUI toolkits so the interactive prompt is not blocked when the event loop runs. This sort of support is integral for combining IPython with something like matplotlib to generate graphs interactively, a key feature for its scientific audience.

In the highly advanced feature category, IPython includes support for controlling parallel processing systems directly from the shell prompt. It supports running Python code distributed over multiple CPUs and hosts, as well as passing Python objects between the processes. Message passing, task farming, and shared memory parallelism are supported. You can even connect and disconnect remote processes dynamically. This lets you start a job and check in later to see how it is running, or share the running state with someone else on your team.

Conclusion

As I mentioned earlier, before I started this series of columns I had already added virtualenv to my standard toolbox, and I use it on a regular basis. Combined with easy_install, I have found it to be irreplaceable for testing new configurations or working on projects with different dependencies. On the other hand, I’m still learning the many features of IPython. It is certainly nicer in many respects than the standard Python shell, but I’m not used to working from the interactive prompt as much as some other developers are, so it may take some time for me to understand its potential fully. Based on the strength of the recommendations I have received and what I have seen so far, I will continue experimenting with it. In the mean time, check it out yourself and let me know what you think.

Next month I will continue this series by introducing you to more tools to enhance your programming productivity. If you have a tip to share, feedback on something I’ve written, or if there is a topic you would like for me to cover in this column, send a note with the details to doug dot hellmann at pythonmagazine dot com and let me know, or add the link to your del.icio.us account with the tag pymagdifferent. And if you’re going to PyCon in March, look me up at the conference.