Packaging your code is the most
reliable way to ensure reproducibility and reusability. Instead of
relying on loose scripts, packaging encourages you to organize your
functions, document them, and test them. This vignette demonstrates how
to create packages in R and Python within a Nix environment managed by
{rix}.
We will cover: 1. Creating an R package using {devtools}
and {usethis}. 2. Creating a Python package using
uv. 3. Defining the correct Nix environments to develop and
use these packages.
{usethis} and
{devtools}The R ecosystem provides excellent tools for package development. We
will use {usethis} to set up the package structure and
{devtools} for documentation and testing.
Start by generating a default.nix that includes the
necessary development tools:
library(rix)
rix(
r_ver = "frozen-edge",
r_pkgs = c("devtools", "usethis", "roxygen2", "testthat"),
ide = "rstudio", # or "code", "none"
project_path = ".",
overwrite = TRUE
)Build the environment with nix-shell.
From inside your Nix shell (or RStudio started from Nix), create a new package:
This creates the standard R package directory structure
(R/, DESCRIPTION, NAMESPACE).
Create a function:
r usethis::use_r("clean_names") Add your function to
R/clean_names.R and use roxygen2 comments
(#') for documentation.
Document:
r devtools::document() This generates the
man/*.Rd files and updates NAMESPACE.
Test:
r usethis::use_testthat() usethis::use_test("clean_names")
Write unit tests in tests/testthat/test-clean_names.R, then
run them: r devtools::test()
Check: r devtools::check() This
runs the standard R package check to ensure everything is
correct.
Once your package is hosted on GitHub, you can use it in other
project-specific Nix environments by adding it to the
git_pkgs argument in rix():
uvFor Python, we will use uv to manage the project
configuration (pyproject.toml) and build process, while Nix
manages the actual Python interpreter and environment.
Generate a default.nix with Python, uv, and
your package’s dependencies:
Inside the Nix shell, create a directory and initialize the project components:
uv init --bare creates a pyproject.toml
without creating a virtual environment (since we are using Nix).
pyproject.tomlEdit pyproject.toml to define dependencies and test
configuration. Critical sections include:
src/pyclean/.tests/.bash pytestbash uv build This produces
a wheel in dist/.When developing a Python package locally within a Nix shell, you want
changes to be reflected immediately. Update your
default.nix shellHook to add your source
directory to PYTHONPATH.
rix (>= 0.17.4) makes this easy with the
py_src_dir argument:
rix(
date = "2025-10-07",
py_conf = list(
py_version = "3.12",
py_pkgs = c("pytest", "pandas"),
py_src_dir = "src" # Adds src/ to PYTHONPATH automatically
),
system_pkgs = "uv",
# ...
)This injects export PYTHONPATH=$PWD/src:$PYTHONPATH into
the shell hook, allowing you to import pyclean and run
tests against the live source code.
To use your Python package in another Nix environment (e.g., for an
analysis project), use
pkgs.python3Packages.buildPythonPackage in your
default.nix. You can point to a GitHub repository:
pyclean = pkgs.python313Packages.buildPythonPackage rec {
pname = "pyclean";
version = "0.1.0";
src = pkgs.fetchgit {
url = "https://github.com/yourusername/pyclean";
rev = "commit_hash";
sha256 = "sha256-hash";
};
pyproject = true;
propagatedBuildInputs = [
pkgs.python313Packages.pandas
];
};Then add pyclean to the buildInputs list
alongside your other Python packages.