Skip to content

Commit 68f98f6

Browse files
authored
Add flake for linux development using nix (#1148)
- Creates an impure python environment using [uv2nix](https://github.com/pyproject-nix/uv2nix) - Uses [fenix](https://github.com/nix-community/fenix) to get latest rust tools
1 parent c9d0a61 commit 68f98f6

File tree

3 files changed

+363
-39
lines changed

3 files changed

+363
-39
lines changed

flake.lock

Lines changed: 138 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

flake.nix

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
{
2+
description = "Icechunk flake, python and rust";
3+
4+
inputs = {
5+
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
6+
7+
fenix = {
8+
url = "github:nix-community/fenix";
9+
inputs.nixpkgs.follows = "nixpkgs";
10+
};
11+
12+
pyproject-nix = {
13+
url = "github:pyproject-nix/pyproject.nix";
14+
inputs.nixpkgs.follows = "nixpkgs";
15+
};
16+
17+
uv2nix = {
18+
url = "github:pyproject-nix/uv2nix";
19+
inputs.pyproject-nix.follows = "pyproject-nix";
20+
inputs.nixpkgs.follows = "nixpkgs";
21+
};
22+
23+
pyproject-build-systems = {
24+
url = "github:pyproject-nix/build-system-pkgs";
25+
inputs.pyproject-nix.follows = "pyproject-nix";
26+
inputs.uv2nix.follows = "uv2nix";
27+
inputs.nixpkgs.follows = "nixpkgs";
28+
};
29+
};
30+
31+
outputs =
32+
{
33+
self,
34+
nixpkgs,
35+
fenix,
36+
uv2nix,
37+
pyproject-nix,
38+
pyproject-build-systems,
39+
...
40+
}:
41+
let
42+
inherit (nixpkgs) lib;
43+
44+
# Load a uv workspace from a workspace root.
45+
# Uv2nix treats all uv projects as workspace projects.
46+
workspace = uv2nix.lib.workspace.loadWorkspace {
47+
workspaceRoot = ./icechunk-python;
48+
};
49+
50+
# Create package overlay from workspace.
51+
overlay = workspace.mkPyprojectOverlay {
52+
# Prefer prebuilt binary wheels as a package source.
53+
# Sdists are less likely to "just work" because of the metadata missing from uv.lock.
54+
# Binary wheels are more likely to, but may still require overrides for library dependencies.
55+
sourcePreference = "wheel"; # or sourcePreference = "sdist";
56+
# Optionally customise PEP 508 environment
57+
# environ = {
58+
# platform_release = "5.10.65";
59+
# };
60+
};
61+
62+
# Extend generated overlay with build fixups
63+
#
64+
# Uv2nix can only work with what it has, and uv.lock is missing essential metadata to perform some builds.
65+
# This is an additional overlay implementing build fixups.
66+
# See:
67+
# - https://pyproject-nix.github.io/uv2nix/FAQ.html
68+
pyprojectOverrides = _final: _prev: {
69+
# Implement build fixups here.
70+
# Note that uv2nix is _not_ using Nixpkgs buildPythonPackage.
71+
# It's using https://pyproject-nix.github.io/pyproject.nix/build.html
72+
};
73+
74+
# This example is only using x86_64-linux
75+
pkgs = nixpkgs.legacyPackages.x86_64-linux;
76+
77+
# Use Python 3.12 from nixpkgs
78+
python = pkgs.python312;
79+
80+
# Construct package set
81+
pythonSet =
82+
# Use base package set from pyproject.nix builders
83+
(pkgs.callPackage pyproject-nix.build.packages {
84+
inherit python;
85+
}).overrideScope
86+
(
87+
lib.composeManyExtensions [
88+
pyproject-build-systems.overlays.default
89+
overlay
90+
pyprojectOverrides
91+
]
92+
);
93+
94+
in
95+
{
96+
packages.x86_64-linux.default = fenix.packages.x86_64-linux.stable.toolchain;
97+
98+
# This example provides two different modes of development:
99+
# - Impurely using uv to manage virtual environments
100+
# - Pure development using uv2nix to manage virtual environments
101+
devShells.x86_64-linux = {
102+
# It is of course perfectly OK to keep using an impure virtualenv workflow and only use uv2nix to build packages.
103+
# This devShell simply adds Python and undoes the dependency leakage done by Nixpkgs Python infrastructure.
104+
impure =
105+
pkgs.mkShell.override
106+
{
107+
stdenv = pkgs.stdenvAdapters.useMoldLinker pkgs.clangStdenv;
108+
}
109+
{
110+
packages = [
111+
python
112+
pkgs.uv
113+
114+
fenix.packages.x86_64-linux.stable.toolchain
115+
pkgs.cargo-nextest # test runner
116+
pkgs.cargo-deny
117+
118+
pkgs.mold
119+
pkgs.taplo # toml lsp server
120+
pkgs.awscli2
121+
pkgs.google-cloud-sdk
122+
pkgs.just # script launcher with a make flavor
123+
pkgs.alejandra # nix code formatter
124+
pkgs.markdownlint-cli2
125+
];
126+
127+
env = {
128+
# Prevent uv from managing Python downloads
129+
UV_PYTHON_DOWNLOADS = "never";
130+
# Force uv to use nixpkgs Python interpreter
131+
UV_PYTHON = python.interpreter;
132+
133+
RUSTFLAGS = "-W unreachable-pub -W bare-trait-objects";
134+
}
135+
// lib.optionalAttrs pkgs.stdenv.isLinux {
136+
# Python libraries often load native shared objects using dlopen(3).
137+
# Setting LD_LIBRARY_PATH makes the dynamic library loader aware of libraries without using RPATH for lookup.
138+
LD_LIBRARY_PATH = lib.makeLibraryPath pkgs.pythonManylinuxPackages.manylinux1;
139+
};
140+
shellHook = ''
141+
unset PYTHONPATH
142+
'';
143+
};
144+
145+
# This devShell uses uv2nix to construct a virtual environment purely from Nix, using the same dependency specification as the application.
146+
# The notable difference is that we also apply another overlay here enabling editable mode ( https://setuptools.pypa.io/en/latest/userguide/development_mode.html ).
147+
#
148+
# This means that any changes done to your local files do not require a rebuild.
149+
#
150+
# Note: Editable package support is still unstable and subject to change.
151+
uv2nix =
152+
let
153+
# Create an overlay enabling editable mode for all local dependencies.
154+
editableOverlay = workspace.mkEditablePyprojectOverlay {
155+
# Use environment variable
156+
root = "$REPO_ROOT";
157+
# Optional: Only enable editable for these packages
158+
# members = [ "hello-world" ];
159+
};
160+
161+
# Override previous set with our overridable overlay.
162+
editablePythonSet = pythonSet.overrideScope (
163+
lib.composeManyExtensions [
164+
editableOverlay
165+
166+
# Apply fixups for building an editable package of your workspace packages
167+
(final: prev: {
168+
icechunk = prev.icechunk.overrideAttrs (old: {
169+
# It's a good idea to filter the sources going into an editable build
170+
# so the editable package doesn't have to be rebuilt on every change.
171+
src = lib.fileset.toSource {
172+
root = old.src;
173+
fileset = lib.fileset.unions [
174+
(old.src + "/pyproject.toml")
175+
(old.src + "/README.md")
176+
];
177+
};
178+
179+
# Hatchling (our build system) has a dependency on the `editables` package when building editables.
180+
#
181+
# In normal Python flows this dependency is dynamically handled, and doesn't need to be explicitly declared.
182+
# This behaviour is documented in PEP-660.
183+
#
184+
# With Nix the dependency needs to be explicitly declared.
185+
nativeBuildInputs = old.nativeBuildInputs ++ final.resolveBuildSystem { editables = [ ]; };
186+
});
187+
188+
})
189+
]
190+
);
191+
192+
# Build virtual environment, with local packages being editable.
193+
#
194+
# Enable all optional dependencies for development.
195+
virtualenv = editablePythonSet.mkVirtualEnv "iechunk-dev-env" workspace.deps.all;
196+
197+
in
198+
pkgs.mkShell.override {
199+
packages = [
200+
virtualenv
201+
pkgs.uv
202+
];
203+
204+
env = {
205+
# Don't create venv using uv
206+
UV_NO_SYNC = "1";
207+
208+
# Force uv to use nixpkgs Python interpreter
209+
UV_PYTHON = python.interpreter;
210+
211+
# Prevent uv from downloading managed Python's
212+
UV_PYTHON_DOWNLOADS = "never";
213+
};
214+
215+
shellHook = ''
216+
# Undo dependency propagation by nixpkgs.
217+
unset PYTHONPATH
218+
219+
# Get repository root using git. This is expanded at runtime by the editable `.pth` machinery.
220+
export REPO_ROOT=$(git rev-parse --show-toplevel)
221+
'';
222+
};
223+
};
224+
};
225+
}

0 commit comments

Comments
 (0)