This vignette is the reference for running Python
scripts as targets steps with reticulate: every
tar_target_py() argument, how to choose the Python
environment, and the common use cases. For a gentler tour start with
vignette("get_started"); for Julia see
vignette("julia").
Code blocks are illustrative and not executed when the vignette builds.
The two constructors
-
tar_target_py(): barename(and unquotedpattern), for direct use in_targets.R. -
tar_target_py_raw(): stringname, for use inside targets factories.
Both return a single targets target and forward
every targets::tar_target_raw() argument,
so anything an ordinary target supports (pattern,
format, iteration, deployment,
resources, cue, memory,
error, priority, description, …)
works exactly as usual.
Arguments
Scripts
| Argument | Meaning |
|---|---|
script |
Path to the Python script to run (required). |
pre_script |
Optional R script run before the Python script.
Assign a named list to_py to push objects into Python’s
__main__. |
post_script |
Optional R script run after the Python script.
py and py_get() are available; its last
expression is the value (object mode) or file paths (file mode). |
Each of the three accepts either a literal path
string (untracked: editing the file does not invalidate the
target) or a tar_target_path("name") reference to an
upstream format = "file" target (tracked: the step re-runs
when the file changes). See “Tracking scripts” below.
Wiring in data and reading it back
| Argument | Meaning |
|---|---|
inputs |
Named character vector mapping the name seen in the step to an
upstream target, e.g. c(x = "prepared_x"). Each becomes a
real dependency (and is sliced under dynamic branching). |
output |
"object" (default) returns a converted R object;
"file" returns a character vector of paths and defaults
format to "file". |
retrieve |
Character vector of Python variable names to return when there is no post-script (object mode). One name returns that object; several return a named list. |
files |
Character vector of paths to return when there is no post-script (file mode). |
Choosing the interpreter / environment
| Argument | Meaning |
|---|---|
python |
Path to a specific interpreter executable. |
env + env_manager
|
Point at an existing environment built by a known tool. |
python_version |
A version string (e.g. "3.12"); reticulate provisions
it via an ephemeral uv environment. |
These are alternatives: normally set only one. If
several are given, the first in this precedence wins:
python >
env/env_manager >
python_version > nothing. They are covered in
detail in the next section.
The three-script model
upstream targets ─► pre_script (R) ─► script (.py) ─► post_script (R) ─► target value
builds `to_py` computes reads `py$name` /
`result` `py_get("name")`
-
script: the Python file. Runs in the__main__module. -
pre_script: theinputsare already bound by name. Assign a named listto_py; each element is pushed as a top-level variable into__main__using reticulate’s conversion rules. -
post_script:py(the reticulate__main__proxy) andpy_get(name)(an explicitreticulate::py_to_r()shortcut) are available. Read a variable withpy$resultorpy_get("result").
Object output
py/compute.py:
# `x` was pushed from R by the pre-script.
seq = list(x) if hasattr(x, "__iter__") else [x]
result = {"sum": float(sum(seq)), "n": len(seq), "mean": float(sum(seq)) / len(seq)}R/pre_push.R:
to_py <- list(x = x) # `x` came from inputs = c(x = "prepared_x")R/post_result.R (last expression becomes the value):
res <- py_get("result")
data.frame(sum = res$sum, n = res$n, mean = res$mean)_targets.R:
list(
tar_target(prepared_x, c(1, 2, 3, 4)),
# (a) return a Python variable directly with `retrieve` (no post-script)
tar_target_py(
name = py_direct,
script = "py/compute.py",
inputs = c(x = "prepared_x"),
pre_script = "R/pre_push.R",
retrieve = "result"
),
# (b) reshape the result in a post-script
tar_target_py(
name = py_prepost,
script = "py/compute.py",
inputs = c(x = "prepared_x"),
pre_script = "R/pre_push.R",
post_script = "R/post_result.R"
)
)File output
Set output = "file" when the script writes to disk;
return the path(s) from the post-script (or via files when
there is no post-script). The target format becomes
"file" automatically.
tar_target_py(
name = py_file,
script = "py/write_csv.py", # writes a CSV, stores its path in `out_path`
inputs = c(x = "prepared_x"),
pre_script = "R/pre_push.R",
post_script = "R/post_files.R", # returns py_get("out_path")
output = "file"
)Dynamic branching
inputs are real dependencies, so pattern
branches them:
list(
tar_target(chunks, list(10, 20, 30)),
tar_target_py(
name = py_branch,
script = "py/compute.py",
inputs = c(x = "chunks"),
pre_script = "R/pre_push.R",
retrieve = "result",
pattern = map(chunks) # 3 branches: x = 10, 20, 30
)
)Run the branches in parallel by setting a crew
controller (see vignette("get_started"), the “Parallelism
and isolation with crew” section).
Choosing the Python environment
The four selection arguments and when to reach for each:
-
python: a path to a specific interpreter executable (e.g.".venv/Scripts/python.exe"). Use when you already have the exact interpreter and want no ambiguity. -
env+env_manager: an existing environment built by a known tool (env_managersays how to readenv; see the table). Recommended for reproducible pipelines: install the packages once, every run reuses them. -
python_version(e.g."3.12"), not an environment, just an interpreter version: reticulate provisions it through an ephemeral, uv-managed environment (py_require()), resolving packages on the fly. Use when only the version matters. - nothing set: reticulate’s default discovery (the computer’s Python, or an ephemeral uv-managed one on recent reticulate). Fine for exploration; pin an environment for reproducibility.
The key contrast is reproducible vs throwaway:
env/python reuse an environment you
built and pinned; python_version lets reticulate build a
fresh one keyed only on a version. One more difference:
python and env/env_manager
override an ambient RETICULATE_PYTHON
(e.g. an RStudio project Python inherited by crew workers),
while python_version and the default
respect it.
env_manager |
what env is |
reticulate call |
|---|---|---|
"system" (default) |
none (optionally set python) |
use_python(python) or default |
"venv" |
a python -m venv directory path |
use_virtualenv(env) |
"virtualenv" |
same as venv | use_virtualenv(env) |
"conda" |
conda environment name | use_condaenv(env) |
"uv" |
a uv-created venv directory path | use_virtualenv(env) |
"poetry" |
a poetry project directory path |
poetry env info -p, then
use_virtualenv()
|
"virtualenv", "venv", and "uv"
all point at a standard Python virtualenv and behave identically: this
includes one created by renv::use_python() (use
env_manager = "venv" with the path under
renv/python/virtualenvs/). For these managers, a
relative env that is an existing directory
is expanded to an absolute path, so env = ".venv" works; a
bare name that is not a directory is passed through unchanged, so a
genuine named environment still resolves. Setting env while
env_manager = "system" is an error.
Use case 1: global / system default
Set nothing; reticulate uses the computer’s default Python (on recent versions, an ephemeral uv-managed one provisioned on first use). Convenient for exploration; pin a real environment for reproducibility.
tar_target_py(
name = probe,
script = "py/probe.py",
retrieve = "result"
# no env / env_manager / python -> computer default Python
)Use case 2: a pinned uv virtualenv (recommended)
Create it once, then point to it. Reproducible: the exact package set is reused every run.
tar_target_py(
name = fit,
script = "py/fit.py",
env_manager = "uv",
env = ".venv",
retrieve = "model"
)Use case 3: a poetry project
Give the project directory (the folder with
pyproject.toml); its virtualenv is resolved for you with
poetry env info -p.
tar_target_py(
name = embed,
script = "py/embed.py",
env_manager = "poetry",
env = "path/to/poetry_project",
retrieve = "vectors"
)If the poetry on PATH is not the one you
want, select a specific executable with
options(tarpolyglot.poetry = "/path/to/poetry").
Use case 4: a plain venv or a conda env
# python -m venv .venv
tar_target_py(
name = score,
script = "py/score.py",
env_manager = "venv",
env = ".venv",
retrieve = "result"
)
# a named conda environment
tar_target_py(
name = classify,
script = "py/classify.py",
env_manager = "conda",
env = "my_conda_env",
retrieve = "labels"
)Use case 5: pin just the version
To fix the interpreter version without naming an
environment, use python_version (leaves the environment at
the default, uv-managed one):
tar_target_py(
name = probe_ver,
script = "py/probe.py",
python_version = "3.12", # reticulate provisions 3.12 via uv
retrieve = "result"
)One interpreter per session. reticulate binds a single Python per R session, so all Python targets that run in the same session share one environment. To use different environments in one pipeline, run those targets on separate
crewworkers (seevignette("get_started")).
Tracking scripts as dependencies
By default script / pre_script /
post_script are untracked literal paths. To have the step
re-run when a script changes, track it with a
format = "file" target and reference it with
tar_target_path():
list(
tar_target(fit_py, "py/fit.py", format = "file"),
tar_target_py(
name = fit,
script = tar_target_path("fit_py"), # re-runs when py/fit.py changes
pre_script = "R/pre_fit.R", # still an untracked literal path
inputs = c(x = "data"),
retrieve = "model"
)
)Conversion caveats
- A bare number in R is a double; use
L(e.g.42L) when Python needs an int. - Python is 0-indexed.
- For anything that does not round-trip cleanly, use file mode.
See ?tar_target_py and ?run_py_step for the
full argument reference.