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:
in `py_call_impl()`:
Error ! ModuleNotFoundError: No module named 'matplotlib'
`reticulate::py_last_error()` for details. Run
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?