A nix template for python packages managed with uv2nix and flake-parts. The structure mirrors those in the omnix registry to the extent possible with python and its ecosystem.
You can use omnix1 to initialize this template:
nix --accept-flake-config run github:juspay/omnix -- \
init github:sciexp/python-nix-template -o new-python-project
tl;dr
instantiate a monorepo variant of the template
nix --accept-flake-config run github:juspay/omnix -- init github:sciexp/python-nix-template -o pnt-mono --non-interactive --params '{
"package-name-kebab-case": "pnt-mono",
"package-name-snake-case": "pnt_mono",
"monorepo-package": true,
"git-org": "pnt-mono",
"author": "Pnt Mono",
"author-email": "[email protected]",
"vscode": true,
"github-ci": true,
"nix-template": false
}' && \
cd pnt-mono && \
git init && \
git commit --allow-empty -m "initial commit (empty)" && \
git add . && \
nix develop --accept-flake-config -c pytest
You can run direnv allow
to enter the shell environment that contains
development dependencies or nix develop --accept-flake-config
to enter (or add
-c command
to execute individual commands within) the development shell.
instantiate a single-package variant of the template
nix --accept-flake-config run github:juspay/omnix -- init github:sciexp/python-nix-template/main -o pnt-new --non-interactive --params '{
"package-name-kebab-case": "pnt-new",
"package-name-snake-case": "pnt_new",
"monorepo-package": false,
"git-org": "pnt-new",
"author": "Pnt New",
"author-email": "[email protected]",
"vscode": true,
"github-ci": true,
"nix-template": false
}' && \
cd pnt-new && \
git init && \
git commit --allow-empty -m "initial commit (empty)" && \
git add . && \
nix run nixpkgs#uv -- lock && \
nix develop --accept-flake-config -c pytest
except you may want to update the git ref/rev of the template if you need to pin to a particular version:
github:sciexp/python-nix-template/main
github:sciexp/python-nix-template/v0.1.0
github:sciexp/python-nix-template/3289dla
github:sciexp/python-nix-template/devbranch
.
The template supports three types of development environments:
- nix devshell
- python virtualenv via uv
- conda environments via pixi
The intended workflow is to run
make bootstrap
only the very first time you are setting up one of these templates. This will verify you have the nix package manager and direnv installed. Registration of the repository contents requires creating a git repository, for example with
git init && git commit --allow-empty -m "initial commit (empty)" && git add .
but does not require committing. After this running
direnv allow
will ensure you have all development tools on a project directory-specific
version of your PATH variable. These include the just
task runner, which
provides an alternative to using GNU Make
as a task runner. See the task runner section for a listing of
development commands.
You should now be able to run pytest
or just test
to confirm the package
tests pass in the devshell environment.
Warning
uv recognizes all the packages in the workspace monorepo layout but pixi treats each package separately. See #22.
If you choose to modify the monorepo packages such as
packages/pnt-functional then you will need to run
just uv-lock
to update the pyproject.toml and
uv.lock files to include the package and its tests in the
workspace.
-
Create and sync virtual environment:
just venv source .venv/bin/activate
-
Run tests:
just test
-
Run linting:
just lint
-
Build package:
just build
- Modern python packaging with
pyproject.toml
- Fast dependency management with
uv
- Reproducible developer environments and builds with
nix
anduv2nix
- See the optional monorepo workspace package pnt-functional
for a brief illustration of functional programming patterns (disabled by default):
- Railway-oriented programming with
expression
for type-safe error handling - Effect tracking via monad transformers for composable side effects
- Runtime type checking with
beartype
for robust type safety - Pure functions and immutable data types for reliable code
- Composition of effectful functions using monadic bind operations
- Railway-oriented programming with
- conda ecosystem compatibility via
pixi
If you'd like to develop python-nix-template
you'll need the nix package
manager. You can optionally make use of
direnv to automatically activate the environment. The
project includes a Makefile to help bootstrap your development environment.
It provides:
- Installation of the nix package manager using the Determinate Systems installer
- Installation of direnv for automatic environment activation
- Link to instructions for shell configuration
To get started, run:
make bootstrap
Run make
alone for a listing of available targets.
After nix and direnv are installed, you can either run direnv allow
or nix develop
to enter a development shell that will
contain necessary system-level dependencies.
This project uses just
as a task runner, which
is provided in the development shell. List available commands
by running just
alone.
just recipes
default # List all recipes
[CI/CD]
gcloud-context # Set gcloud context
ghsecrets repo="sciexp/python-nix-template" # Update github secrets for repo from environment variables
ghvars repo="sciexp/python-nix-template" # Update github vars for repo from environment variables
pre-commit # Run pre-commit hooks (see pre-commit.nix and note the yaml is git-ignored)
[conda package]
conda-build # Package commands (conda)
conda-check # Run all checks in conda environment (lint, type, test)
conda-env # Create and sync conda environment with pixi
conda-lint # Run linting in conda environment with pixi
conda-lint-fix # Run linting and fix errors in conda environment with pixi
conda-lock # Update conda environment
conda-test # Run tests in conda environment with pixi
conda-type # Run type checking in conda environment with pixi
pixi-lock # Update pixi lockfile
[nix]
ci # Run CI checks locally with `om ci`
container-build # Build production container image
container-build-dev # Build development container image
container-run # Run production container with port 8888 exposed
container-run-dev # Run development container with port 8888 exposed
dev # Enter the Nix development shell
flake-check # Validate the Nix flake configuration
flake-update # Update all flake inputs to their latest versions
[python package]
check # Run all checks (lint, type, test)
lint # Run linting
lint-fix # Run linting and fix errors
test # Run tests
type # Run type checking in uv virtual environment
uv-build # Package commands
uv-lint # Run linting in uv virtual environment
uv-lint-fix # Run linting and fix errors in uv virtual environment
uv-lock # Update lockfile from pyproject.toml
uv-test # Run tests in uv virtual environment
uv-type # Run type checking in uv virtual environment
venv # Sync and enter uv virtual environment
[secrets]
check-secrets # Check secrets are available in teller shell.
create-and-populate-separate-secrets path # Complete process: Create and populate separate secrets for each line in the dotenv file
create-and-populate-single-secret name path # Complete process: Create a secret and populate it with the entire contents of a dotenv file
create-secret name # Create a secret with the given name
export # Export unique secrets to dotenv format
get-secret name # Retrieve the contents of a given secret
populate-separate-secrets path # Populate each line of a dotenv-formatted file as a separate secret
populate-single-secret name path # Populate a single secret with the contents of a dotenv-formatted file
seed-dotenv # Create empty dotenv from template
show # Show existing secrets
[template]
template-init # Initialize new project from template
template-verify # Verify template functionality by creating and checking a test project
See the omnix registry flake
Footnotes
-
If you have omnix installed you just need
om init ...
and notnix run ... -- init
β©