For the sake of completeness, here's how to install the environment system-wide
on NixOS.
```nix
{
# ...
environment.systemPackages = with pkgs; [
(python310.withPackages (
ps: with ps; [
numpy
toolz
]
))
];
}
```
### Developing with Python {#developing-with-python}
Above, we were mostly just focused on use cases and what to do to get started
creating working Python environments in nix.
Now that you know the basics to be up and running, it is time to take a step
back and take a deeper look at how Python packages are packaged on Nix.
#### Python library packages in Nixpkgs {#python-library-packages-in-nixpkgs}
With Nix all packages are built by functions. The main function in Nix for
building Python libraries is [`buildPythonPackage`](#buildpythonpackage-function). Let's see how we can build the
`toolz` package.
```nix
{
lib,
buildPythonPackage,
fetchPypi,
setuptools,
}:
buildPythonPackage rec {
pname = "toolz";
version = "0.10.0";
pyproject = true;
src = fetchPypi {
inherit pname version;
hash = "sha256-CP3V73yWSArRHBLUct4hrNMjWZlvaaUlkpm1QP66RWA=";
};
build-system = [
setuptools
];
# has no tests
doCheck = false;
pythonImportsCheck = [
"toolz.itertoolz"
"toolz.functoolz"
"toolz.dicttoolz"
];
meta = {
changelog = "https://github.com/pytoolz/toolz/releases/tag/${version}";
homepage = "https://github.com/pytoolz/toolz";
description = "List processing tools and functional utilities";
license = lib.licenses.bsd3;
};
}
```
What happens here? The function [`buildPythonPackage`](#buildpythonpackage-function) is called and as argument
it accepts a set. In this case the set is a recursive set, `rec`. One of the
arguments is the name of the package, which consists of a basename (generally
following the name on PyPI) and a version. Another argument, `src` specifies the
source, which in this case is fetched from PyPI using the helper function
`fetchPypi`. The argument `doCheck` is used to set whether tests should be run
when building the package. Since there are no tests, we rely on [`pythonImportsCheck`](#using-pythonimportscheck)
to test whether the package can be imported. Furthermore, we specify some meta
information. The output of the function is a derivation.
An expression for `toolz` can be found in the Nixpkgs repository. As explained
in the introduction of this Python section, a derivation of `toolz` is available
for each interpreter version, e.g. `python312.pkgs.toolz` refers to the `toolz`
derivation corresponding to the CPython 3.12 interpreter.
The above example works when you're directly working on
`pkgs/top-level/python-packages.nix` in the Nixpkgs repository. Often though,
you will want to test a Nix expression outside of the Nixpkgs tree.
The following expression creates a derivation for the `toolz` package,
and adds it along with a `numpy` package to a Python environment.
```nix
with import <nixpkgs> { };
(
let
my_toolz = python312.pkgs.buildPythonPackage rec {
pname = "toolz";
version = "0.10.0";
pyproject = true;
src = fetchPypi {
inherit pname version;
hash = "sha256-CP3V73yWSArRHBLUct4hrNMjWZlvaaUlkpm1QP66RWA=";
};
build-system = [
python312.pkgs.setuptools
];
# has no tests
doCheck = false;
meta = {
homepage = "https://github.com/pytoolz/toolz/";
description = "List processing tools and functional utilities";
# [...]
};
};
in
python312.withPackages (
ps: with ps; [
numpy
my_toolz
]
)
).env
```
Executing `nix-shell` will result in an environment in which you can use
Python 3.12 and the `toolz` package. As you can see we had to explicitly mention
for which Python version we want to build a package.
So, what did we do here? Well, we took the Nix expression that we used earlier
to build a Python environment, and said that we wanted to include our own