Home Explore Blog Models CI



nixpkgs

11th chunk of `doc/languages-frameworks/python.section.md`
2697a429355059077b472d7457f6237f8565b8b3750d48040000000100000fc4
b = np.array([3,4])
print(f"The dot product of {a} and {b} is: {np.dot(a, b)}")
```

Then we execute it, without requiring any environment setup at all!

```sh
$ ./foo.py
The dot product of [1 2] and [3 4] is: 11
```

If the dependencies are not available on the host where `foo.py` is executed, it
will build or download them from a Nix binary cache prior to starting up, prior
that it is executed on a machine with a multi-user Nix installation.

This provides a way to ship a self bootstrapping Python script, akin to a
statically linked binary, where it can be run on any machine (provided nix is
installed) without having to assume that `numpy` is installed globally on the
system.

By default it is pulling the import checkout of Nixpkgs itself from our nix
channel, which is nice as it cache-aligns with our other package builds, but we
can make it fully reproducible by pinning the `nixpkgs` import:

```python
#!/usr/bin/env nix-shell
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ps.numpy ])"
#!nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/e51209796c4262bfb8908e3d6d72302fe4e96f5f.tar.gz
import numpy as np
a = np.array([1,2])
b = np.array([3,4])
print(f"The dot product of {a} and {b} is: {np.dot(a, b)}")
```

This will execute with the exact same versions of Python 3.10, numpy, and system
dependencies a year from now as it does today, because it will always use
exactly git commit `e51209796c4262bfb8908e3d6d72302fe4e96f5f` of Nixpkgs for all
of the package versions.

This is also a great way to ensure the script executes identically on different
servers.

##### Load environment from `.nix` expression {#load-environment-from-.nix-expression}

We've now seen how to create an ad-hoc temporary shell session, and how to
create a single script with Python dependencies, but in the course of normal
development we're usually working in an entire package repository.

As explained [in the `nix-shell` section](https://nixos.org/manual/nix/stable/command-ref/nix-shell) of the Nix manual, `nix-shell` can also load an expression from a `.nix` file.
Say we want to have Python 3.13, `numpy` and `toolz`, like before,
in an environment. We can add a `shell.nix` file describing our dependencies:

```nix
with import <nixpkgs> { };
(python313.withPackages (
  ps: with ps; [
    numpy
    toolz
  ]
)).env
```

And then at the command line, just typing `nix-shell` produces the same
environment as before. In a normal project, we'll likely have many more
dependencies; this can provide a way for developers to share the environments
with each other and with CI builders.

What's happening here?

1. We begin with importing the Nix Packages collections. `import <nixpkgs>`
   imports the `<nixpkgs>` function, `{}` calls it and the `with` statement
   brings all attributes of `nixpkgs` in the local scope. These attributes form
   the main package set.
2. Then we create a Python 3.13 environment with the [`withPackages`](#python.withpackages-function) function, as before.
3. The [`withPackages`](#python.withpackages-function) function expects us to provide a function as an argument
   that takes the set of all Python packages and returns a list of packages to
   include in the environment. Here, we select the packages `numpy` and `toolz`
   from the package set.

To combine this with `mkShell` you can:

```nix
with import <nixpkgs> { };
let
  pythonEnv = python313.withPackages (ps: [
    ps.numpy
    ps.toolz
  ]);
in
mkShell {
  packages = [
    pythonEnv

    black
    mypy

    libffi
    openssl
  ];
}
```

This will create a unified environment that has not just our Python interpreter
and its Python dependencies, but also tools like `black` or `mypy` and libraries
like `libffi` and `openssl` in scope. This is generic and can span any number of
tools or languages across the Nixpkgs ecosystem.

##### Installing environments globally on the system {#installing-environments-globally-on-the-system}

Up to now, we've been creating environments scoped to an ad-hoc shell session,

Title: Nixpkgs Python: Reproducible Scripts and Project-Wide Environments
Summary
This chunk details advanced Python environment management with Nix. It demonstrates how to create self-bootstrapping Python scripts by embedding `nix-shell` commands in their shebang, enabling automatic dependency fetching/building and making them portable across Nix-enabled machines. For maximum reproducibility, it shows how to pin the exact `nixpkgs` commit for script dependencies. The text then introduces managing project-wide environments using `shell.nix` files, explaining how to define Python versions and packages (e.g., `numpy`, `toolz`) and combine them with other development tools (e.g., `black`, `mypy`) and system libraries (`libffi`, `openssl`) using `mkShell`.