Packaging Your Code in R and Python with rix

Bruno Rodrigues

2026-01-16

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.

Part 1: R Packages with {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.

Setting up the Development Environment

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.

Initializing the Package

From inside your Nix shell (or RStudio started from Nix), create a new package:

usethis::create_package("~/Documents/projects/cleanR")

This creates the standard R package directory structure (R/, DESCRIPTION, NAMESPACE).

Workflow Basics

  1. Create a function:

    usethis::use_r("clean_names")

    Add your function to R/clean_names.R and use roxygen2 comments (#') for documentation.

  2. Document:

    devtools::document()

    This generates the man/*.Rd files and updates NAMESPACE.

  3. Test:

    usethis::use_testthat()
    usethis::use_test("clean_names")

    Write unit tests in tests/testthat/test-clean_names.R, then run them:

    devtools::test()
  4. Check:

    devtools::check()

    This runs the standard R package check to ensure everything is correct.

Using Your Package in a Nix Environment

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():

rix(
  r_ver = "frozen-edge",
  r_pkgs = c("dplyr"),
  git_pkgs = list(
    package_name = "cleanR",
    repo_url = "https://github.com/yourusername/cleanR",
    commit = "commit_hash_here"
  ),
  ide = "rstudio",
  project_path = ".",
  overwrite = TRUE
)

Part 2: Python Packages with uv

For Python, we will use uv to manage the project configuration (pyproject.toml) and build process, while Nix manages the actual Python interpreter and environment.

Setting up the Development Environment

Generate a default.nix with Python, uv, and your package’s dependencies:

library(rix)

rix(
  date = "2025-10-07",
  py_conf = list(
    py_version = "3.12",
    py_pkgs = c("pytest", "pandas", "numpy") # Runtime and test deps
  ),
  system_pkgs = "uv",
  ide = "none",
  project_path = ".",
  overwrite = TRUE
)

Initializing the Package

Inside the Nix shell, create a directory and initialize the project components:

mkdir pyclean
cd pyclean
uv init --bare
mkdir -p src/pyclean tests
touch src/pyclean/__init__.py

uv init --bare creates a pyproject.toml without creating a virtual environment (since we are using Nix).

Configuring pyproject.toml

Edit pyproject.toml to define dependencies and test configuration. Critical sections include:

[project]
name = "pyclean"
version = "0.1.0"
dependencies = [
    "pandas>=2.0.0",
]

[project.optional-dependencies]
test = [
    "pytest",
]

# Essential for running pytest from source layout
[tool.pytest.ini_options]
pythonpath = ["src"]

Development Workflow

  1. Write Code: Add functions to src/pyclean/.

  2. Write Tests: Add tests to tests/.

  3. Run Tests:

    pytest
  4. Build:

    uv build

    This produces a wheel in dist/.

Local Development Loop

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.

Using Your Package in a Nix Environment

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.