Nix is a great tool to set up development environments. It allows us to have simultaneous installations of various versions of tools—such as Python—required for our projects. This means Nix makes it easy to have Python 2.7 installed for one project, and Python 3.6 for another. Projects using the same Python version can have different Python packages.
Of course, Python’s VirtualEnv also enables us to do this. Nix, however, is more powerful. It can handle all our system’s packages; not just Python’s. This means it enables us to hold different versions of any dependency. For example, if one project requires a specific version of OpenCL and another project requires an incompatible version, VirtualEnv won’t help us. Nix will.
There are many Python packages, and to install one such package through Nix requires it to be available in the Nix package repository. Understandably, not all Python packages are packaged for Nix—and those that are, often are not the newest version, nor at some other specific version we require.
We can use Nix to provision a Python environment for our project that works similarly to VirtualEnv’s. We can then use pip to handle such per-project Python dependencies, allowing us to grab Python packages directly from the regular Python package repositories without going through Nix. This also allows us to quickly get to work with others’ Python projects that are not set up to work with Nix.
We do this by managing Python and pip as Nix dependencies (as well as any any other Python packages we wish to have managed through Nix, for example those with system dependencies such as NumPy).
For example, create a nix.shell in your project root as follows:
with import <nixpkgs> {};
let
my-python-packages = python-packages: [
python-packages.pip
python-packages.numpy
];
my-python = python36.withPackages my-python-packages;
in
pkgs.mkShell {
buildInputs = [
bashInteractive
my-python
];
shellHook = ''
export PIP_PREFIX="$(pwd)/_build/pip_packages"
export PYTHONPATH="$(pwd)/_build/pip_packages/lib/python3.6/site-packages:$PYTHONPATH"
unset SOURCE_DATE_EPOCH
'';
}
Activate it in your shell by running $ nix-shell
.
This makes available Python 3.6, pip and NumPy. Environment variable
PIP_PREFIX
tells pip to install its packages in subdirectory _build in your
project root’s folder. PYTHONPATH
is extended with the directory where this
project’s pip will now install packages. These environment changes are
necessary because this Python installation lives inside the read-only Nix
store, and pip would not be able to install packages there.
Note that we unset SOURCE_DATE_EPOCH
. Nix sets it to a value of 1 for
reproducible builds, causing Python’s bdist_wheel
to fail as it requires
SOURCE_DATE_EPOCH
to correspond to 1980 or later.
You can now use pip as you normally would, such as
$ pip install -r requirements.txt