Often, virtualenv is overkill for the basic task of installing project dependencies and keeping them isolated. We present a simple alternative consisting of:
./.pip
to your PYTHONPATH
pip install -t .pip
to install modules locallypython
from your project's root directoryInstalling dependencies is a required step for almost any Python application. Each Python app depends on a different set of libraries, and to be sure that it behaves as expected, the best thing is to install exactly the right version of each library.
The standard practice is to ship Python projects with a requirements.txt
file. This file lists the libraries
that the project depends on, and a version number for each one. If present, installing the dependencies
is as easy as:
$ pip install -r requirements.txt
So far so good! The problems start when using two or more projects with conflicting dependencies. Let's suppose project A only works with library X version 0.1, and project B uses the same library X, but only works with version 0.2. By default, pip installs libraries globally into the Python interpreter's library path. This means that issuing the
$ pip install X==0.2
command will make X version 0.2 available in every Python instance, overwriting version 0.1 if it was
previously installed. Switching between project A and project B would require reinstalling the right version of X each time,
which is time-consuming and inconvenient.
One popular solution to this commonly encountered problem is virtual environments. The virtualenv
framework allows you to create isolated Python environments. The dependencies for each project are kept
separate from each other. However, some users find virtualenv complicated to use, so packages like virtualenvwrapper
and autoenv extend its functionality in an attempt to make things easier. Other solutions include
Anaconda environments
in the Anaconda Python distribution, and pyvenv
which is baked into the Python standard
library starting from Python 3.3.
Though these are great tools, we have always felt that they represent a rather heavy and complicated toolset for what should essentially be a very simple task.
Looking at Javascript, tools like npm and Bower provide the easy, reliable and powerful package management capabilities which it feels like Python is missing. The key to their success? Both tools download a copy of the right versions of the right libraries, by default placing them in a special folder directly within your project's directory. The downloaded libraries remain local to only that project, meaning that you automatically avoid the issues described above.
As it turns out, there's a simple way of replicating the npm/Bower approach for Python packages, involving these easy steps:
./.pip
to your PYTHONPATH
. pip
with -t .pip
to install your libraries locally.
Then, simply execute your code from within your project directory and forget about source env/bin/active
and deactivate
!
The trick works because ./.pip
is a relative path. As a result, if you run python
from
~/dev/project_a
then ~/dev/project_a/.pip
gets included in that Python instance's
library path. If you run python
from ~/dev/project_b
, then ~/dev/project_b/.pip
gets included instead. This works on all major platforms: Linux, Mac and Windows.
The folder name .pip
is arbitrary of course — for example, one could choose to name the folder
pip_components
or libs
instead. However, .pip
is quick to type, and the initial
.
hides the folder by default on Linux/Mac.
The following command will permanently set the PYTHONPATH for standard terminal sessions:
$ echo 'export PYTHONPATH="./.pip:$PYTHONPATH"' >> ~/.bash_profile
After that, either restart your terminal or do $ source .bash_profile
to make sure the PYTHONPATH
is loaded into the current session. Depending on your platform, you might want to use ~/.bashrc
instead of ~/.bash_profile
.
Go to Control Panel > System and Security > System > Change Settings > Advanced > Environment Variables,
and add/edit the PYTHONPATH variable either to your user variables or system variables, setting it to
.\.pip
or .\.pip;(...other paths...)
. Then restart your command prompt.
If you prefer to change the PYTHONPATH only temporarily for the duration of your terminal session, you can also do
$ export PYTHONPATH=./.pip
on Mac/Linux or > set PYTHONPATH=.\.pip
on Windows.
On Mac/Linux, you can even set the PYTHONPATH just for the duration of a Python session: $ PYTHONPATH=./.pip python main.py
.
Now that we have set the PYTHONPATH, the only thing left to do is to install our packages into the right
location using pip. For this, we use the -t
or --target
switch to indicate the directory
into which pip should install the packages:
$ cd project_a
project_a$ pip install requests==2.7.0 -t .pip
project_a$ python
>>> import requests
>>> requests.__version__
'2.7.0'
Now let's do the same for another project with another version:
$ cd project_b
project_b$ pip install requests==2.6.0 -t .pip
project_b$ python
>>> import requests
>>> requests.__version__
'2.6.0'
This works equally well when using a requirements.txt
file:
$ pip install -r requirements.txt -t .pip
$ /path/to/python main.py
However, there's an issue if you are switching between Python 2 and 3 and you are using packages that don't have
a single code base, i.e. they compile their source code during installation using 2to3
. In that case, you
would have to introduce something like .pip3
and add this path in front of your PYTHONPATH when running
things with Python 3.
If you happen to have packages that have been installed globally using easy_install
, you've got the
issue that easy_install prepends the path to those libraries to your sys.path
which
gives them priority over whatever you have in .pip
. The solution here is to get rid of global
easy_install installations. You can easily check if something hijacks your .pip setup by running
import sys;sys.path
within a Python session. If there are paths in front of
./.pip
, then you might need to clean up things first.
或是邮件反馈可也:
askdama[AT]googlegroups.com
订阅 substack 体验古早写作:
关注公众号, 持续获得相关各种嗯哼: