From 60b547a4701d438af9fb088b3cdb612a8cd5ae45 Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Tue, 8 Apr 2025 23:07:58 -0500 Subject: [PATCH] switch to python --- flake.lock | 99 +++++++++++------------ flake.nix | 224 ++++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 212 insertions(+), 111 deletions(-) diff --git a/flake.lock b/flake.lock index 380a11f..159f6f6 100644 --- a/flake.lock +++ b/flake.lock @@ -1,92 +1,95 @@ { "nodes": { - "flake-parts": { - "inputs": { - "nixpkgs-lib": "nixpkgs-lib" - }, - "locked": { - "lastModified": 1743550720, - "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "c621e8422220273271f52058f618c94e405bb0f5", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, "nixpkgs": { "locked": { "lastModified": 1744098102, "narHash": "sha256-tzCdyIJj9AjysC3OuKA+tMD/kDEDAF9mICPDU7ix0JA=", - "owner": "nixos", + "owner": "NixOS", "repo": "nixpkgs", "rev": "c8cd81426f45942bb2906d5ed2fe21d2f19d95b7", "type": "github" }, "original": { - "owner": "nixos", + "owner": "NixOS", "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" } }, - "nixpkgs-lib": { + "pyproject-build-systems": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "pyproject-nix": [ + "pyproject-nix" + ], + "uv2nix": [ + "uv2nix" + ] + }, "locked": { - "lastModified": 1743296961, - "narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=", - "owner": "nix-community", - "repo": "nixpkgs.lib", - "rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa", + "lastModified": 1743993520, + "narHash": "sha256-DgQ/b7f7QIECEVYVIWrqYXl1iTzXTfibMhxp6QMsAwA=", + "owner": "pyproject-nix", + "repo": "build-system-pkgs", + "rev": "4470eb3e37dc5d2791b77b90c5228506d729800f", "type": "github" }, "original": { - "owner": "nix-community", - "repo": "nixpkgs.lib", + "owner": "pyproject-nix", + "repo": "build-system-pkgs", "type": "github" } }, - "nixpkgs_2": { + "pyproject-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, "locked": { - "lastModified": 1736320768, - "narHash": "sha256-nIYdTAiKIGnFNugbomgBJR+Xv5F1ZQU+HfaBqJKroC0=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "4bc9c909d9ac828a039f288cf872d16d38185db8", + "lastModified": 1743438845, + "narHash": "sha256-1GSaoubGtvsLRwoYwHjeKYq40tLwvuFFVhGrG8J9Oek=", + "owner": "pyproject-nix", + "repo": "pyproject.nix", + "rev": "8063ec98edc459571d042a640b1c5e334ecfca1e", "type": "github" }, "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", + "owner": "pyproject-nix", + "repo": "pyproject.nix", "type": "github" } }, "root": { "inputs": { - "flake-parts": "flake-parts", "nixpkgs": "nixpkgs", - "rust-overlay": "rust-overlay" + "pyproject-build-systems": "pyproject-build-systems", + "pyproject-nix": "pyproject-nix", + "uv2nix": "uv2nix" } }, - "rust-overlay": { + "uv2nix": { "inputs": { - "nixpkgs": "nixpkgs_2" + "nixpkgs": [ + "nixpkgs" + ], + "pyproject-nix": [ + "pyproject-nix" + ] }, "locked": { - "lastModified": 1744166053, - "narHash": "sha256-mpI7OzFwp+fUeDtZYQbVZ2YmtxTN2UNrrOwbYD27xKU=", - "owner": "oxalica", - "repo": "rust-overlay", - "rev": "896158be1835589db6f42f45ef0a49b8b492cc66", + "lastModified": 1744108242, + "narHash": "sha256-cvwVUlHKqjv/8NlO1681vcoB/lHPkV9gKm/HHED/dvw=", + "owner": "pyproject-nix", + "repo": "uv2nix", + "rev": "e00f7117aaebaa1f9ff846976d655c5a4d919321", "type": "github" }, "original": { - "owner": "oxalica", - "repo": "rust-overlay", + "owner": "pyproject-nix", + "repo": "uv2nix", "type": "github" } } diff --git a/flake.nix b/flake.nix index b59acc8..e743714 100644 --- a/flake.nix +++ b/flake.nix @@ -1,75 +1,173 @@ { + description = "tito flake using uv2nix"; + inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - flake-parts.url = "github:hercules-ci/flake-parts"; - rust-overlay.url = "github:oxalica/rust-overlay"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + pyproject-nix = { + url = "github:pyproject-nix/pyproject.nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + uv2nix = { + url = "github:pyproject-nix/uv2nix"; + inputs.pyproject-nix.follows = "pyproject-nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + pyproject-build-systems = { + url = "github:pyproject-nix/build-system-pkgs"; + inputs.pyproject-nix.follows = "pyproject-nix"; + inputs.uv2nix.follows = "uv2nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; - outputs = inputs: - inputs.flake-parts.lib.mkFlake { inherit inputs; } { - systems = [ "x86_64-linux" ]; - perSystem = { config, self', pkgs, lib, system, ... }: - let - runtimeDeps = with pkgs; [ alsa-lib speechd ]; - buildDeps = with pkgs; [ pkg-config rustPlatform.bindgenHook ]; - devDeps = with pkgs; [ gdb ]; + outputs = + { + nixpkgs, + uv2nix, + pyproject-nix, + pyproject-build-systems, + ... + }: + let + inherit (nixpkgs) lib; - cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml); - msrv = cargoToml.package.rust-version; + # Load a uv workspace from a workspace root. + # Uv2nix treats all uv projects as workspace projects. + workspace = uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; }; - rustPackage = features: - (pkgs.makeRustPlatform { - cargo = pkgs.rust-bin.stable.latest.minimal; - rustc = pkgs.rust-bin.stable.latest.minimal; - }).buildRustPackage { - inherit (cargoToml.package) name version; - src = ./.; - cargoLock.lockFile = ./Cargo.lock; - buildFeatures = features; - buildInputs = runtimeDeps; - nativeBuildInputs = buildDeps; - # Uncomment if your cargo tests require networking or otherwise - # don't play nicely with the Nix build sandbox: - # doCheck = false; + # Create package overlay from workspace. + overlay = workspace.mkPyprojectOverlay { + # Prefer prebuilt binary wheels as a package source. + # Sdists are less likely to "just work" because of the metadata missing from uv.lock. + # Binary wheels are more likely to, but may still require overrides for library dependencies. + sourcePreference = "wheel"; # or sourcePreference = "sdist"; + # Optionally customise PEP 508 environment + # environ = { + # platform_release = "5.10.65"; + # }; + }; + + # Extend generated overlay with build fixups + # + # Uv2nix can only work with what it has, and uv.lock is missing essential metadata to perform some builds. + # This is an additional overlay implementing build fixups. + # See: + # - https://pyproject-nix.github.io/uv2nix/FAQ.html + pyprojectOverrides = _final: _prev: { + # Implement build fixups here. + # Note that uv2nix is _not_ using Nixpkgs buildPythonPackage. + # It's using https://pyproject-nix.github.io/pyproject.nix/build.html + }; + + # This example is only using x86_64-linux + pkgs = nixpkgs.legacyPackages.x86_64-linux; + + # Use Python 3.12 from nixpkgs + python = pkgs.python312; + + # Construct package set + pythonSet = + # Use base package set from pyproject.nix builders + (pkgs.callPackage pyproject-nix.build.packages { + inherit python; + }).overrideScope + ( + lib.composeManyExtensions [ + pyproject-build-systems.overlays.default + overlay + pyprojectOverrides + ] + ); + + in + { + # Package a virtual environment as our main application. + # + # Enable no optional dependencies for production build. + packages.x86_64-linux.default = pythonSet.mkVirtualEnv "tito-env" workspace.deps.default; + + # This example provides two different modes of development: + # - Impurely using uv to manage virtual environments + # - Pure development using uv2nix to manage virtual environments + devShells.x86_64-linux = { + # It is of course perfectly OK to keep using an impure virtualenv workflow and only use uv2nix to build packages. + # This devShell simply adds Python and undoes the dependency leakage done by Nixpkgs Python infrastructure. + # impure = pkgs.mkShell { + # packages = [ + # python + # pkgs.uv + # ]; + # env = + # { + # # Prevent uv from managing Python downloads + # UV_PYTHON_DOWNLOADS = "never"; + # # Force uv to use nixpkgs Python interpreter + # UV_PYTHON = python.interpreter; + # } + # // lib.optionalAttrs pkgs.stdenv.isLinux { + # # Python libraries often load native shared objects using dlopen(3). + # # Setting LD_LIBRARY_PATH makes the dynamic library loader aware of libraries without using RPATH for lookup. + # LD_LIBRARY_PATH = lib.makeLibraryPath pkgs.pythonManylinuxPackages.manylinux1; + # }; + # shellHook = '' + # unset PYTHONPATH + # ''; + # }; + + # This devShell uses uv2nix to construct a virtual environment purely from Nix, using the same dependency specification as the application. + # The notable difference is that we also apply another overlay here enabling editable mode ( https://setuptools.pypa.io/en/latest/userguide/development_mode.html ). + # + # This means that any changes done to your local files do not require a rebuild. + uv2nix = + let + # Create an overlay enabling editable mode for all local dependencies. + # Note: Editable support is still under development and this API might change. + editableOverlay = workspace.mkEditablePyprojectOverlay { + # Use environment variable + root = "$REPO_ROOT"; + # Optional: Only enable editable for these packages + # members = [ "hello-world" ]; }; - mkDevShell = rustc: - pkgs.mkShell { - shellHook = '' - export RUST_SRC_PATH=${pkgs.rustPlatform.rustLibSrc} - export SHELL=${pkgs.lib.getExe pkgs.bashInteractive} - ''; - buildInputs = runtimeDeps; - nativeBuildInputs = buildDeps ++ devDeps ++ [ rustc ]; - packages = with pkgs; [ - rust-analyzer - lldb - (vscode-with-extensions.override { - vscode = vscodium; - vscodeExtensions = with vscode-extensions; [ - rust-lang.rust-analyzer - vadimcn.vscode-lldb - ]; - }) - ]; + # Override previous set with our overrideable overlay. + editablePythonSet = pythonSet.overrideScope editableOverlay; + + # Build virtual environment, with local packages being editable. + # + # Enable all optional dependencies for development. + virtualenv = editablePythonSet.mkVirtualEnv "tito-dev-env" workspace.deps.all; + + in + pkgs.mkShell { + packages = [ + virtualenv + pkgs.uv + ]; + + env = { + # Don't create venv using uv + UV_NO_SYNC = "1"; + + # Force uv to use Python interpreter from venv + UV_PYTHON = "${virtualenv}/bin/python"; + + # Prevent uv from downloading managed Python's + UV_PYTHON_DOWNLOADS = "never"; }; - in { - _module.args.pkgs = import inputs.nixpkgs { - inherit system; - overlays = [ (import inputs.rust-overlay) ]; + + shellHook = '' + # Undo dependency propagation by nixpkgs. + unset PYTHONPATH + + # Get repository root using git. This is expanded at runtime by the editable `.pth` machinery. + export REPO_ROOT=$(git rev-parse --show-toplevel) + export SHELL=${pkgs.lib.getExe pkgs.bashInteractive} + ''; }; - - packages.default = self'.packages.tito; - # devShells.default = self'.devShells.nightly; - devShells.default = self'.devShells.stable; - - packages.tito = (rustPackage "tito"); - packages.tito-base = (rustPackage ""); - - devShells.nightly = (mkDevShell (pkgs.rust-bin.selectLatestNightlyWith - (toolchain: toolchain.default))); - devShells.stable = (mkDevShell pkgs.rust-bin.stable.latest.default); - devShells.msrv = (mkDevShell pkgs.rust-bin.stable.${msrv}.default); - }; + }; }; } +