このページについて

nixを使ってみようかなーと思っていたところ、nixとuvを結合するuv2nixというツールがあるのを知り、NotebookLMでまとめてみました。
この文章はNotebookLMによって生成されました。

ライセンスについて

[1]のライセンスがCC BY 4.0であることに基づき、この文章もCC BY 4.0でライセンスされています。


NixとPythonパッケージ管理:uv2nixとflake.nixの役割

1. なぜ flake.nix (Nixpkgs) だけで管理しないのか

従来のNixpkgsを使用したPython開発(flake.nixのみでの管理)には、主に以下の技術的・実用的な課題が存在します。

  • 二重管理と非効率性: Python標準の pyproject.toml があるにもかかわらず、その内容(依存関係)を flake.nix 内の withPackages 引数として手動で翻訳・記述する必要があり、非効率(unergonomic)です,。
  • 開発ツールの非互換性: Nixpkgsは標準的な仮想環境(virtual environment / venv)を作成しません。その結果、標準的なvenvの存在を前提とするLSP(例: Pyright)やエディタの補完機能が正常に動作しない場合があります。
  • バージョンの制約: Nixpkgsに収録されているPythonパッケージセットのバージョン(例: Jinja2 3.1.5)が強制されるため、プロジェクトが要求する特定のバージョン(例: 3.1.6)と不整合が生じることがあります,。
  • 環境変数による汚染(Footguns): NixpkgsのPython環境は PYTHONPATH を使用して依存関係を伝播させます。これにより、開発環境に入った際、Nixpkgs側の依存関係が優先されてしまい、意図しないライブラリが読み込まれる問題が発生します,。

2. uv2nix とは

uv2nix は、Rust製の高速なPythonパッケージマネージャ uv のワークスペースを取り込み、純粋なNixコード(Pure Nix code)を使用して動的にNixのDerivation(ビルド定義)を生成するツールです。

  • PyPIからの直接構築: NixpkgsのPythonパッケージセットに依存せず、uv.lock ファイルに基づいてPyPIから直接ソースやバイナリ(Wheel)を取得し、環境を構築します。
  • 標準的な仮想環境の提供: Nix独自の環境構造ではなく、標準的な仮想環境(bog-standard virtual environment)を作成するため、LSPなどの既存のPython開発ツールがそのまま動作します。
  • ワークスペースとEditableインストール: 複数のパッケージを含むワークスペースや、開発中のローカルパッケージのEditableインストール(編集可能モード)に対応しており、スクリプトなどがパスに通った状態で開発できます,。
  • 基盤技術: pyproject.nix ライブラリとそのビルドインフラストラクチャをベースに構築されています。

チュートリアル:uv2nix による Python 開発環境の構築

このガイドでは、Rust製の高速なパッケージマネージャ uv と Nix を組み合わせ、再現性が高く、かつ既存の開発ツール(LSP等)と互換性のある開発環境を構築します。

このページの末尾に、当チュートリアルとは違う手順で構築したものですが、完成形を掲載しています。良ければ参考にしてください。

1. 準備とプロジェクトの初期化

まだ uv がインストールされていない場合、Nix を使って一時的なシェルに入り、プロジェクトの雛形を作成します。

コマンド:

# 1. uv と python が使えるシェルに入る
nix-shell -p python3 uv

# 2. プロジェクトの初期化(pyproject.toml, .python-version 等が作成される)
uv init --app --package

# 3. ロックファイルの生成
uv lock

2. flake.nix の設定(環境の定義)

uv2nix の核心は、uv.lock を読み込んで Nix のパッケージセットに変換することです。以下の概念を組み合わせて flake.nix を記述します。

  1. ワークスペースのロード: プロジェクト全体(uv.lock)を解析します。
  2. オーバーレイの作成: mkPyprojectOverlay を使い、uv.lock の情報を Nix のビルド指示書(Derivation)に変換します。ここでは「Wheel(バイナリ)」または「sdist(ソース)」の優先度を指定できます。
  3. Pythonセットの構築: 基本となる Python に、ビルドシステム(setuptools等)と上記のオーバーレイを結合します。
  4. Editable モード(開発用): 開発中はソースコードの変更を即座に反映させるため、mkEditablePyprojectOverlay を使用して、ソースを仮想環境にリンクさせます。

手順 2-1: flake.nix の作成

以下のファイルをflake.nixとして保存します。 uv2nix/templates/hello-world/flake.nix at master · pyproject-nix/uv2nix

3. パッケージの追加と反映(日常のフロー)

uv2nix を導入後のパッケージ管理は、Python 標準のワークフローに従います。flake.nix を編集する必要はありません。

手順 3-1: 依存関係の定義

pyproject.toml を編集し、追加したいライブラリを記述します。

[project]
dependencies = [
    "requests",
    "numpy",
]

手順 3-2: ロックファイルの更新

ターミナルで以下を実行し、uv.lock を更新します。

uv lock

手順 3-3: 環境の再構築

開発シェルに入り直します。uv2nix が自動的に新しい uv.lock を読み込み、環境を構築します。

nix develop

VS Codeでのimportエラーを修正する方法

このままだと.venvディレクトリが存在しないため、VS Code上でpandasパッケージへのimportエラーが発生する。 VS Codeにdirenv/direnv-vscode: unclutter your .profileをインストールすることで、エディタ上でパッケージへの参照が可能となる。

4. トラブルシューティング:C拡張/バイナリライブラリ

NumPy や PyQt のような C/C++ ライブラリに依存するパッケージを追加した場合、PyPI からダウンロードされた Wheel(バイナリ)が動かないことがあります。

  • 自動修正: 通常、uv2nixautoPatchelfHook を使用して、Linux 上でバイナリが動くように自動修正を試みます。
  • 手動修正(オーバーライド): それでも「ライブラリが見つからない(例: libstdc++.so.6 missing)」というエラーが出る場合は、flake.nix で不足しているライブラリを手動で追加する必要があります。

修正例 (flake.nix 内のオーバーレイ):

(final: prev: {
  numpy = prev.numpy.overrideAttrs (old: {
    # 不足しているライブラリ (例: zlib, stdenv.cc.cc.lib) を buildInputs に追加
    buildInputs = old.buildInputs ++ [ final.pkgs.zlib ]; 
  });
})

Note: 現在、この不足ライブラリの特定と追加を自動化するツール(auto-rider)が開発中ですが、現時点では手動での対応が必要になることがあります。


この構成により、flake.nix の管理コストを最小限に抑えつつ、Python エコシステムの利便性と Nix の再現性を両立させた開発が可能になります。

uv2nixのhello-worldテンプレートpandasを追加したサンプルをGithubで公開しました。
takanotume24/uv2nix-add-package-sample: uv2nixを用いてPythonパッケージを追加する場合のサンプル