Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 39 additions & 62 deletions inst/tutorials/introduction-to-python/tutorial.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,19 @@ description: Tutorial for introducing students to the Python language.
<!-- Do the test cases need to have all the Python commands? Or does the tutorial magically know that import polars . . . has already been run? -->

<!-- Learn about reticulate. -->
```{r setup, include=FALSE}
# -------------------------------------------------------------------
# Global setup: R packages + Python virtualenv for the tutorial
# -------------------------------------------------------------------


```{r early-env, include=FALSE}
# Don't let reticulate autoconfigure arbitrary Pythons
# Don't let reticulate auto-pick some random Python
Sys.setenv(RETICULATE_AUTOCONFIGURE = "FALSE")

is_windows <- identical(.Platform$OS.type, "windows")

# Point to venv Python only if it exists; use OS-appropriate path
py_guess <- if (is_windows) ".venv/Scripts/python.exe" else ".venv/bin/python"
if (file.exists(py_guess)) {
Sys.setenv(RETICULATE_PYTHON = normalizePath(py_guess, winslash = "/", mustWork = FALSE))
}

```

```{r setup, include=FALSE}
# ---- Safe CRAN mirror + interactive-only installs ----
if (is.null(getOption("repos")["CRAN"]) || !nzchar(getOption("repos")["CRAN"])) {
options(repos = c(CRAN = "https://cloud.r-project.org"))
}
needed <- c("learnr","tutorial.helpers","knitr","reticulate")
needed <- c("learnr", "tutorial.helpers", "knitr", "reticulate")
to_install <- setdiff(needed, rownames(installed.packages()))
if (interactive() && length(to_install)) {
install.packages(to_install, quiet = TRUE)
Expand All @@ -57,59 +48,42 @@ learnr::tutorial_options(

# ---- Environment flags ----
is_windows <- identical(.Platform$OS.type, "windows")
in_ci <- !interactive() || identical(tolower(Sys.getenv("CI")), "true")

# ---- Python/reticulate behavior ----
if (in_ci) {
# Don’t execute Python in CI (render code only)
knitr::knit_engines$set(
python = function(options) {
knitr::engine_output(options, code = options$code,
out = "\n<!-- python execution skipped in CI -->\n")
}
)
} else {
# Use local venv if present; do not install anything here
venv <- ".venv"
py_bin <- if (is_windows) file.path(venv, "Scripts", "python.exe") else file.path(venv, "bin", "python")
if (file.exists(py_bin)) {
Sys.setenv(RETICULATE_PYTHON = normalizePath(py_bin, winslash = "/", mustWork = FALSE))
reticulate::use_virtualenv(venv, required = FALSE)
}
# Enable Python engine (reticulate)
knitr::knit_engines$set(python = reticulate::eng_python)
}


```

```{r bootstrap-python-venv, include=FALSE}
# --- Robust, cross-platform venv bootstrap (works locally, safe on CI) ---

is_windows <- identical(.Platform$OS.type, "windows")
venv <- ".venv"
py_bin <- if (is_windows) file.path(venv, "Scripts", "python.exe") else file.path(venv, "bin", "python")

# Detect CI / R CMD check: don't try to build a venv or run Python then.
in_ci <- identical(tolower(Sys.getenv("CI")), "true") ||
identical(tolower(Sys.getenv("R_CMD_CHECK")), "true") ||
!interactive()

venv <- ".venv"
py_bin <- if (is_windows) file.path(venv, "Scripts", "python.exe") else file.path(venv, "bin", "python")

if (in_ci) {
# Render-only: don't execute Python, don't create venv
# -----------------------------------------------------------------
# CI / R CMD check: render-only, no Python execution or venv setup
# -----------------------------------------------------------------
knitr::knit_engines$set(
python = function(options) {
knitr::engine_output(options, code = options$code,
out = "\n<!-- python execution skipped in CI / R CMD check -->\n")
knitr::engine_output(
options,
code = options$code,
out = "\n<!-- python execution skipped in CI / R CMD check -->\n"
)
}
)

} else {
# 1) Pick a system Python
# -----------------------------------------------------------------
# Local use: create/activate .venv in this tutorial directory
# -----------------------------------------------------------------

# 1) Find a system Python
python_exe <- Sys.which(if (is_windows) "python" else "python3")
if (!nzchar(python_exe)) python_exe <- reticulate::py_discover_config()$python
if (!nzchar(python_exe)) stop("No usable system Python found to create .venv.")
if (!nzchar(python_exe)) {
python_exe <- reticulate::py_discover_config()$python
}
if (!nzchar(python_exe)) {
stop("No usable system Python found to create .venv.")
}

# 2) Create .venv if missing
# 2) Create .venv if missing (do NOT delete if it already exists)
if (!dir.exists(venv)) {
message("Creating .venv with: ", python_exe)
reticulate::virtualenv_create(envname = venv, python = python_exe)
Expand All @@ -119,20 +93,23 @@ if (in_ci) {
Sys.setenv(RETICULATE_PYTHON = normalizePath(py_bin, winslash = "/", mustWork = FALSE))
reticulate::use_virtualenv(venv, required = TRUE)

# 4) Upgrade pip tooling (use python -m pip to avoid OS-specific pip paths)
system2(py_bin, c("-m", "pip", "install", "--upgrade", "pip", "setuptools", "wheel"),
stdout = TRUE, stderr = TRUE)
# 4) Upgrade pip tooling via `python -m pip` (portable, no pip.exe assumption)
system2(
py_bin,
c("-m", "pip", "install", "--upgrade", "pip", "setuptools", "wheel"),
stdout = TRUE,
stderr = TRUE
)

# 5) Install required packages (again via python -m pip)
# 5) Install required Python packages into this .venv
req <- c("polars", "pandas", "numpy", "plotnine", "matplotlib", "seaborn")
system2(py_bin, c("-m", "pip", "install", req), stdout = TRUE, stderr = TRUE)

# 6) Ensure knitr uses reticulate's Python engine
# 6) Tell knitr to use reticulate's Python engine
knitr::knit_engines$set(python = reticulate::eng_python)
}
```


```{r copy-code-chunk, child = system.file("child_documents/copy_button.Rmd", package = "tutorial.helpers")}
```

Expand Down