# Packaging Your Code in R and Python with Rix

#### Bruno Rodrigues

#### 2026-08-26

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. To avoid manual edits, consider updating `gen-env.R` to generate these lines automatically (for example, by using an LLM to help write the necessary R code).