Loading repository data…
Loading repository data…
sciexp / repository
❄️ nix template for python monorepos +/- rust pyo3 extension modules with flake-parts, uv2nix, and crane 🐍
A transparent discovery signal based on current public GitHub metadata.
This score does not audit code, security, maintainers, documentation quality, or suitability. Verify the repository and its current documentation before adoption.
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
PROJECT_DIRECTORY=pnt-mono && \
PROJECT_SNAKE_CASE=$(echo "$PROJECT_DIRECTORY" | tr '-' '_') && \
PROJECT_CAMEL_CASE=$(echo "$PROJECT_DIRECTORY" | perl -pe 's/-(.)/uc($1)/ge') && \
PARAMS=$(cat <<EOF
{
"package-name-kebab-case": "$PROJECT_DIRECTORY",
"package-name-snake-case": "$PROJECT_SNAKE_CASE",
"package-name-camel-case": "$PROJECT_CAMEL_CASE",
"repo-name": "$PROJECT_DIRECTORY",
"monorepo-package": true,
"pyo3-package": true,
"git-org": "pnt-mono",
"author": "Pnt Mono",
"author-email": "mono@pnt.org",
"project-description": "A Python monorepo project using Nix and uv2nix",
"vscode": true,
"github-ci": true,
"docs": true,
"nix-template": false
}
EOF
) && \
nix --accept-flake-config run github:juspay/omnix/v1.3.2 -- init github:sciexp/python-nix-template/main -o "$PROJECT_DIRECTORY" --non-interactive --params "$PARAMS" && \
(command -v direnv >/dev/null 2>&1 && direnv revoke "./$PROJECT_DIRECTORY/" || true) && \
cd "$PROJECT_DIRECTORY" && \
git init && \
git commit --allow-empty -m "initial commit (empty)" && \
git add . && \
for pkg in packages/*/; do [ -f "$pkg/pyproject.toml" ] && (cd "$pkg" && nix run github:NixOS/nixpkgs/nixos-unstable#uv -- lock); done && \
git add . && \
nix develop --accept-flake-config -c just test-all
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.
PROJECT_DIRECTORY=pnt-new && \
PROJECT_SNAKE_CASE=$(echo "$PROJECT_DIRECTORY" | tr '-' '_') && \
PROJECT_CAMEL_CASE=$(echo "$PROJECT_DIRECTORY" | perl -pe 's/-(.)/uc($1)/ge') && \
PARAMS=$(cat <<EOF
{
"package-name-kebab-case": "$PROJECT_DIRECTORY",
"package-name-snake-case": "$PROJECT_SNAKE_CASE",
"package-name-camel-case": "$PROJECT_CAMEL_CASE",
"repo-name": "$PROJECT_DIRECTORY",
"monorepo-package": false,
"pyo3-package": false,
"git-org": "pnt-new",
"author": "Pnt New",
"author-email": "new@pnt.org",
"project-description": "A Python project using Nix and uv2nix",
"vscode": true,
"github-ci": true,
"docs": true,
"nix-template": false
}
EOF
) && \
nix --accept-flake-config run github:juspay/omnix/v1.3.2 -- init github:sciexp/python-nix-template/main -o "$PROJECT_DIRECTORY" --non-interactive --params "$PARAMS" && \
(command -v direnv >/dev/null 2>&1 && direnv revoke "./$PROJECT_DIRECTORY/" || true) && \
cd "$PROJECT_DIRECTORY" && \
git init && \
git commit --allow-empty -m "initial commit (empty)" && \
git add . && \
for pkg in packages/*/; do [ -f "$pkg/pyproject.toml" ] && (cd "$pkg" && nix run github:NixOS/nixpkgs/nixos-unstable#uv -- lock); done && \
git add . && \
nix develop --accept-flake-config -c just test-all
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/maingithub:sciexp/python-nix-template/v0.1.0github:sciexp/python-nix-template/3289dlagithub:sciexp/python-nix-template/devbranch.The template supports three types of development environments:
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 just test-all to confirm all package tests pass in the devshell environment, or just test <package-name> to test a specific package.
[!NOTE] This template uses an independent-lock pattern where each package under
packages/maintains its ownpyproject.tomlanduv.lock. There is no rootpyproject.tomlor uv workspace. After instantiation, lock each package individually:for pkg in packages/*/; do [ -f "$pkg/pyproject.toml" ] && (cd "$pkg" && uv lock); done
If you choose to modify packages or add dependencies, run just uv-lock <package-name> to update the lock file for that specific package.
Create and sync virtual environment:
just venv
source .venv/bin/activate
Run tests:
just test
Run linting:
just lint
Build package:
just build
pyproject.tomluvnix and uv2nixpixiThe template includes optional packages controlled by omnix template parameters. Both default to false for the single-package variant and can be set to true individually or together.
pnt-functional (monorepo-package parameter) provides a brief illustration of functional programming patterns in Python.
It demonstrates railway-oriented programming with expression for type-safe error handling, effect tracking via monad transformers for composable side effects, runtime type checking with beartype, pure functions and immutable data types, and composition of effectful functions using monadic bind operations.
See packages/pnt-functional for details.
pnt-cli (pyo3-package parameter) is a Rust extension module demonstrating Python-Rust interop via pyo3 and maturin.
It exposes Rust functions callable from Python through a compiled native module (pnt_cli._native).
The Rust side is organized as a Cargo workspace with a core library crate and a pyo3 binding crate under packages/pnt-cli/crates/.
On the Nix side, the build uses crane for incremental Rust compilation caching and crane-maturin for producing maturin-compatible wheels.
The resulting artifact is installed into the uv2nix package set via pyproject-nix's nixpkgsPrebuilt, avoiding duplicate Rust compilation during Nix evaluation.
See packages/pnt-cli and nix/packages/pnt-cli for the implementation.
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:
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.
Available recipes:
default # List all recipes
[CI/CD]
ci-build-category system category # Build a category of nix flake outputs for CI matrix
ci-check package # Run all checks for a package (lint, typecheck, test)
ci-lint package # Run linting for a package
ci-sync package # Sync dependencies for a package via uv
ci-test package # Run tests for a package
ci-typecheck package # Run type checking for a package
gcloud-context # Set gcloud context
gh-docs-build branch=`git branch --show-current` debug="false" # Trigger docs build job remotely on GitHub (requires workflow on main)
gh-docs-cancel run_id="" # Cancel a running docs workflow
gh-docs-logs run_id="" job="" # View logs for a specific docs workflow run
gh-docs-rerun run_id="" failed_only="true" # Re-run a failed docs workflow
gh-docs-watch run_id="" # Watch a specific docs workflow run
gh-workflow-status workflow="deploy-docs.yaml" branch=`git branch --show-current` limit="5" # View recent workflow runs status
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
list-packages-json # Discover packages as JSON array for CI matrix
list-workflows # List available workflows and associated jobs using act
pre-commit # Run prek hooks (see modules/formatting.nix and note the yaml is git-ignored)
scan-secrets # Scan repository for hardcoded secrets
scan-staged # Scan staged files for hardcoded secrets (pre-commit)
test-docs-build branch=`git branch --show-current` # Test build-docs job locally with act
test-docs-deploy branch=`git branch --show-current` # Test full deploy-docs workflow locally with act
[conda]
conda-build package="pnt-core" # Package commands (conda)
conda-check package="pnt-core" # Run all checks in conda environment (lint, type, test)
conda-env package="pnt-core" # Create and sync conda environment with pixi
conda-lint package="pnt-core" # Run linting in conda environment with pixi
conda-lint-fix package="pnt-core" # Run linting and fix errors in conda environment with pixi
conda-lock package="pnt-core" # Update conda environment
conda-test package="pnt-core" # Run tests in conda environment with pixi
conda-type package="pnt-core" # Run type checking in conda environment with pixi
pixi-lock package="pnt-core" # Update pixi lockfile
[containers]
If you have omnix installed you just need om init ... and not nix run ... -- init ↩