Using R to Install Python Packages in a GitHub Actions Workflow

In a post-{reticulate} 1.41 CI World

programming
r
python
github-actions
Author

James Balamuta

Published

May 28, 2025

Abstract

After {reticulate} 1.41, a GitHub Actions workflow started failing with ModuleNotFoundError despite packages being installed via simple a pip install command. The fix? Replace straightforward Python package installation with R-managed virtual environments using reticulate::virtualenv_create(). Welcome to 2025, where we use R to install Python packages.

Nothing quite like dusting off a GitHub Actions workflow that worked perfectly in February 2025, hitting “reuse,” and watching it spectacularly fail. Welcome to my Wednesday morning.

The Problem

The culprit? I tried to reuse a GitHub Actions configuration that worked flawlessly back in February 2025 for a polyglot Quarto extension, only to discover that {reticulate} 1.41’s introduction of py_require() related changes had other plans for my CI pipeline.

Specifically, when rendering the project using the knitr engine, I was greeted with:

Error in `py_call_impl()`:
! ModuleNotFoundError: No module named 'matplotlib'
Run `reticulate::py_last_error()` for details.

The frustrating part? All the usual suspects were in place: the Python environment was set up using requirements.txt and the package was definitely installed in the Python environment without error. Yet {reticulate} couldn’t find it.

The Solution

What used to be a simple runner step before {reticulate} 1.41:

- name: "Install python dependencies"
  run: |
    pip install -r docs/requirements.txt

Now needs to become this delightfully R-flavored Python setup after {reticulate} 1.41:

# Setup Python for reticulate
- uses: actions/setup-python@v5
  with:
    python-version: "3.x"
    
# Install python dependencies for reticulate
- name: Setup r-reticulate venv
  shell: Rscript {0}
  run: |
    path_to_python <- reticulate::virtualenv_create(
      envname = "r-reticulate",
      python = Sys.which("python"),
      requirements = "docs/requirements.txt"
    )
    writeLines(sprintf("RETICULATE_PYTHON=%s", path_to_python),
               Sys.getenv("GITHUB_ENV"))

Nothing says “progress” like using R to manage your Python environment! 🙃

The Details

The key insight is that {reticulate} 1.41 became much more opinionated about Python environment management. Simply having packages installed via pip isn’t enough anymore—{reticulate} wants to control the entire Python environment setup to ensure compatibility.

You can see the full diff of my GitHub Action workflow changes here.

Where to Learn More

This approach is documented in the newly revamped “Using reticulate in an R package” vignette, specifically under the “Using GitHub Actions” section, where it’s described as the preferred method “if you prefer to use a manually managed Python environment.”

Prefer is doing a lot of heavy lifting in that sentence.

What About a Custom venv Outside of R?

You might be thinking, “Can’t we just create the virtual environment directly without R?” Sure! Something like:

- name: Create Python venv
  run: |
    python -m venv r-reticulate
    source r-reticulate/bin/activate
    pip install -r docs/requirements.txt
    echo "RETICULATE_PYTHON=$(pwd)/r-reticulate/bin/python" >> $GITHUB_ENV

This custom approach works – at least for now – but {reticulate} has specific expectations about how virtual environments are created and configured. Using reticulate::virtualenv_create() ensures the environment is set up exactly how {reticulate} expects it, with all the proper paths, configurations, and metadata that make {reticulate} happy.

In the event of future changes to {reticulate}, the R-managed approach is more likely to remain compatible. Sometimes the “simple” solution isn’t the one that works with finicky package dependencies.

Fin

Welcome to 2025, where we use R to install Python packages so that R can use Python. The future is wild!

At least we’re all learning new ways to make our CI workflows more… creative? And hey, if someone asks you to write polyglot code, you can now confidently say you use R to manage Python environments. That’s got to count for something on a resume, right?