uv2nix

repository·master·Indexed 20 days ago

https://github.com/pyproject-nix/uv2nix

A utility for ingesting uv workspaces into Nix environments, leveraging the pyproject.nix ecosystem to provide reproducible Python environments. It allows developers to bridge uv's Python dependency management with Nix's system-level package management for both development environment management and production packaging.

Tokens
9.3K
Snippets
45
Records
58
Agent score
70%

What's inside uv2nix

  1. What is uv2nix?

    master

    uv2nix is a tool that ingests uv workspaces and generates Nix derivations dynamically using pure Nix code. It serves two primary purposes:

    1. Development Environment Management: Providing reproducible development environments for uv-based Python projects.
    2. Production Packaging: Building production-ready packages from uv workspaces using Nix.

    uv2nix is built upon the infrastructure of pyproject.nix.

  2. Implement tests as separate derivations in uv2nix

    master

    Because uv2nix uses the pyproject.nix build infrastructure, runtime and test dependencies are not available during the build phase. To ensure tests have access to the necessary environment, you must implement them as separate Nix derivations rather than running them within the main build process.

    To implement this pattern:

    1. Override your package to add test commands/logic to the passthru.tests attribute.
    2. Use passthru.tests within your Flake checks to execute the test suite.
  3. Nix store path reference safety check

    master
    To prevent leaking Nix-specific paths (like references to shared libraries or Nix store paths) into redistributable artifacts, uv2nix automatically scans the outputs. If any Nix store path references are found within the produced wheel or sdist, the build will fail.
  4. Understand why uv2nix does not include bundled overrides

    master

    Unlike poetry2nix, uv2nix does not ship with a collection of pre-defined overrides. This design choice is based on several factors:

    1. Metadata Focus: uv2nix focuses on accurately translating pyproject.toml and uv.lock into Nix without attempting to compensate for deficiencies in Python tooling metadata.
    2. Source Preference: uv2nix does not enforce a default package source (e.g., sdist vs. wheels). Users are expected to choose their preferred source.
    3. Wheel Compatibility: Because binary wheels are more likely to work out-of-the-box, many users can successfully use uv2nix without any overrides at all.
    4. Maintainability: Avoiding bundled overrides reduces the maintenance burden on the project.

    If your project requires overrides, you should maintain your own set or use a third-party override collection.

  5. How to filter sources in uv2nix

    master

    Nix allows filtering local sources when copying them to the store. This controls which file changes affect the Nix store path hashing, effectively tuning how often a package is rebuilt.

    Important: Do NOT apply source filtering at the workspace root level (e.g., using builtins.filterSource on ./.). Because uv2nix reads the workspace root at evaluation time, filtering there causes import-from-derivation issues and breaks editable packages.

    Instead, apply source filtering at the individual Python package level using an overlay or overrideAttrs.

  6. Filter sources for an individual package

    master

    To prevent unnecessary rebuilds, apply builtins.filterSource to the src attribute of a specific package using overrideAttrs. This is typically done within a pyprojectOverrides function passed to lib.composeManyExtensions.

    app = prev.app.overrideAttrs (old: {
      src = builtins.filterSource (_: _: true) old.src;
    });
  7. Override package sources for Flake evaluation

    master

    When using the UV_FIND_LINKS method to install nixpkgs wheels, you must override the package's src attribute to point to the .dist output. This ensures the Nix evaluation can resolve the package sources correctly. You should also ensure buildInputs are preserved.

    let
      pyprojectOverrides = final: prev: {
        seccomp = prev.seccomp.overrideAttrs(old: {
          buildInputs = (old.buildInputs or []) ++ python.pkgs.seccomp.buildInputs;
          src = python.pkgs.seccomp.dist;
        });
      };
    in ...
  8. Set up a Uv workspace with uv2nix

    master

    You can use uv2nix to ingest a uv workspace into Nix. This allows you to create a package set from a uv.lock file that can be built using nix build, and provides different development shell options.

    Development Shells

    uv2nix provides two distinct ways to manage your development environment via Nix:

    1. Nix-managed virtual environments: Uses Nix to manage the virtual environment. Dependencies are installed in editable mode.

      • Enter this shell using: nix develop .#uv2nix
    2. UV-managed virtual environments (Impure): Uses uv directly to manage the virtual environment.

      • Enter this shell using: nix develop .#impure
    nix develop .#uv2nix
    nix develop .#impure
  9. Ship Python applications with mkApplication

    master

    By default, uv2nix builds virtual environments. If you want to hide the fact that an application is implemented via a Python virtualenv and instead provide a clean Nix package, use the mkApplication utility function.

    mkApplication creates a derivation that wraps the virtual environment but only links the content present in the package attribute. This automatically excludes virtualenv-specific files like Python interpreters, activation scripts, and pyvenv.cfg, while including application-specific files like binaries, man pages, and systemd units.

    {
        packages = forAllSystems (
          system:
          let
            pythonSet = pythonSets.${system};
            pkgs = nixpkgs.legacyPackages.${system};
            inherit (pkgs.callPackages pyproject-nix.build.util { }) mkApplication;
          in
          {
            default = mkApplication {
              venv = pythonSet.mkVirtualEnv "application-env" workspace.deps.default;
              package = pythonSet.hello-world;
            };
          }
        );
    }
  10. Build packages with private dependencies in the Nix sandbox

    master

    Because the Nix build occurs in a sandbox, the .netrc file is not accessible by default. You must explicitly provide the path to the netrc file using the --option extra-sandbox-paths flag during the build command.

    For a persistent configuration, add extra-sandbox-paths to your nix.conf file.

    nix build -L -v --option extra-sandbox-paths /etc/nix/netrc