Getting Along with Python

Manage Python dependencies with virtualenv

virtualenv lets each project run on its own Python version and its own set of installed libraries, so changes to one project’s dependencies (i.e., packages it depends on) don’t affect other projects.

To explain the basic mechanism, a virtualenv is really just a directory containing a mini-installation of Python. Once you ‘activate’ the virtualenv, running ‘python’ (or running most command line python scripts) doesn’t run the system’s python, but the virtualenv’s python, which also uses its own set of libraries. Once you ‘deactivate’ the virtualenv, you are back to normal Python.

You can get technical details and instructions for use in virtualenv’s docs. These should suffice for the experienced or well-caffeinated. But many beginners have a tougher time with them and don’t fully understand the uses of virtualenv and why we bother with it. So this document tries to explain the reasoning and introduce basic usage in simple language.

If you aren’t interested in detailed reasons, you can go directly to the section titled Basic Usage or directly to virtualenv’s docs.

Examples of Dependency Problems

A lot of operating systems come with Python, because they need it to run some system scripts. Since it’s already installed, lots of people just use it to work on their projects. And often they install all their libraries where the system Python will get them (e.g.: with sudo pip install), which means a ton of libraries accumulate in the same place, alongside and overlapping with whatever the OS scripts need. Alternatively, one may use an OS which doesn’t depend on Python at all, but still do everything from one Python installation.

Maybe nothing has gone wrong yet. But this is the foundation of many real-world confusions and disasters.

Upgrades

If an OS upgrade or downgrade changes the Python version to one which your program doesn’t support, the program will break. If you do a crude job of installing a different Python version to use, or install and then partly uninstall one, and it overrides the system Python, then OS scripts will break. Same for libraries.

Using virtualenv to separate OS dependencies from project dependencies lets you stop worrying about this, because you can easily change versions for one thing without affecting the other.

Coexisting Projects

You have two projects, A and B. A uses version 1 of a library. B uses version 2 of the same library. The versions aren’t compatible, so they won’t both run on the same system. It isn’t very predictable when this will happen, so things may seem to be fine for a while, until cropping up at some inconvenient time in an unpredictable form.

Instead of heavyweight solutions like waiting to see these problems and then fixing code or running each on a different machine, you can just give each project its own virtualenv as a matter of course.

Implicit Dependencies

You’ve installed hundreds of libraries for other projects, all sharing the same environment. You write some new code which imports some of these libraries. Everything seems to be working great for a while. But when you go to share or deploy your code on a new machine, it fails because of some libraries which are missing or have the wrong versions. These errors can be painful to track down, and this might be the worst possible time to have to do that.

If you start from a clean virtualenv when you start the project, you would install each thing needed for that project far ahead of time. Then you would use pip freeze > requirements.txt to dump a list of everything you installed, along with its version. Then any time you needed to install in a new place, you would create a virtualenv, change to the directory containing requirements.txt (using cd), and use pip install -r requirements.txt to install all the dependencies automatically.

Even if you didn’t normally work inside virtualenvs, you might still want to test in them just to check that you have the right list of Python dependencies. This is something that tox can do for you without requiring you to directly use virtualenvs.

Playing Around

Make a virtualenv when you just want to play around with installing Python packages, or try to reproduce a problem with them. When you are done, you don’t have to individually uninstall things; just remove the virtualenv and none of the changes should be left behind.

A huge bonus of virtualenv is that all the files to manage can be installed using normal user privileges. For example, while you should never install a package you don’t trust, it only makes matters worse to install a possibly broken package with root privileges (as with sudo easy_install example, assuming the name of the package is example). Using virtualenv, that is not necessary to try the package, because the virtualenv is usually in some directory owned by a user and sudo is therefore not necessary. virtualenv makes it simpler to work effectively in Python without root, including if you want to deploy something as a limited user on a shared host.

People used to do all kinds of awful things before virtualenv came along. If you are curious about these or want more detail on why people use virtualenv to have a better life with Python, check out Virtualenv Workarounds.

Basic Usage

Assuming you use pip, you can install virtualenv with:

pip install virtualenv

(It is also possible to use virtualenv as a standalone file, virtualenv.py, but I’ll leave you to look into that yourself if you need it).

If you see old instructions that say to use easy_install, please just skip them. Normally it will be easy to just use pip instead. If you are using virtualenvwrapper (not necessary but OK), the rest of this is all a little different, but basically a different spelling of the same thing.

The next step would be to create a virtualenv to use for some purpose. A virtualenv is really just a disposable directory containing some Python files. Here one is created with the arbitrary name ‘example’ - but choose your own descriptive names.

virtualenv example

(where example is the name you want the directory to have). Normally you won’t mess with the contents of this directory, and you definitely shouldn’t put your own code there. It should always be OK to destroy and recreate the directories that virtualenv creates.

Now, after you’ve created a virtualenv (directory), you need to ‘activate’ it each time you want to use it. The exact method depends on your OS.

In most cases, Unix-like OSes using the bash shell (or compatible) will let you use:

source example/bin/activate

On Windows using cmd.exe, instead run:

example\Scripts\activate

The same should work in Powershell, but there is an additional issue with execution policies; see virtualenv docs on activate, in the section about using Powershell.

When you are done using a virtualenv for a bit, but don’t want to actually remove it or anything, you can type:

deactivate

When you are done using a virtualenv for good, you can just remove the directory that virtualenv created, e.g. example. Of course, be careful not to remove your own valuable code, which should never be inside virtualenv directories.

There are more advanced uses of virtualenv. For one example, if you want to create a virtualenv using a non-default python executable (e.g. an older or newer one you have somewhere, for example at example/python), you could use this command:

virtualenv -p example/python

here’s an example in Windows:

virtualenv -p C:\Python27\python.exe

Another fancy use is to make a virtualenv to be able to use globally installed packages. This isn’t the best habit, but if you need it, it’s possible to create a virtualenv like this using:

virtualenv --system-site-packages example

But the default for a virtualenv is NOT to use system site packages, and that is typically a better choice.

Many people like using virtualenvwrapper to keep their virtualenvs in one centralized directory. Consider it optional and try it if you want to.

If anything in this document is unclear or seems wrong, please refer directly to the actual documentation for virtualenv.

Tips on Dependency Management

Some additional tips on reducing pain with Python dependencies and virtualenv.

  1. Try not to install libraries for your projects into a global python installation (i.e.: try to avoid sudo pip install or making projects depend much on system libraries) but rather in virtualenvs specific to each project, or related group of projects.
  2. Treat virtualenv directories as disposable. For example, don’t put them into version control. Instead, put a requirements.txt and/or a script for recreating the virtualenv into version control with the project that uses it, and document it briefly in a README file.
  3. It’s really useful to write at least a basic set of automated tests, so you can quickly run one command to check if anything you did has broken the project (including dependencies changes). If you don’t know what to use, I might suggest pytest. But the point is to get started early. You can’t very easily or reliably ensure everything works without automated testing, so you can’t safely mess with dependencies either.
  4. When you have things working, dump the installed dependencies to requirements.txt using pip freeze > requirements.txt so you and others will know which libraries and versions are needed to run the project. Review that file make sure it only includes things that are requirements of the project, and update and retest it as needed. Of course, the point of this is when you make a new virtualenv, you can install dependencies using pip install -r requirements.txt.
  5. If you need to upgrade one or more libraries for an important project that’s currently working with older versions, at least create a separate virtualenv to install the new versions in and test thoroughly before the upgrade, leaving the existing stuff in place until you can really verify the new dependencies will work.
  6. If you need to make the same code work with multiple Python versions, use tox. This makes sure that your package can install and pass its tests in all the Python versions you want to support.
  7. If you need to develop against many versions of Python including the very old, consider developing on Ubuntu and installing old pythons from the Deadsnakes PPA (or something comparable if you know it on another distribution). Compiling Python is OK as compiling things goes, but if your interest is development then it is nicer to have someone doing this for you, or at worst supplying a script to do it.