Skip to contents

Each script's referenced data paths are rewritten to their Psych-DS target (via the per-file repro_rewrite_paths() result) so that, run from root, the reads/writes resolve against the materialised tree. The rewritten script text is written to root/<script target_path> (or, when the plan has no target for the script itself, to root/<basename> at the top level so it is still runnable). A reference left ambiguous or unmatched is written unchanged — the static report already flagged it.

Usage

repro_write_scripts(
  code_text_list,
  rewrite_list,
  plan,
  root,
  inject_libs = NULL,
  lang = "R",
  script_paths = NULL,
  writes_in_place = FALSE,
  replace_env_calls = FALSE,
  run_dirs = NULL,
  prepend = NULL
)

Arguments

code_text_list

named list of per-file code text (names are file_names)

rewrite_list

named list of repro_rewrite_paths() results, same order

plan

the psychds_check table (to find each script's own target_path)

root

the materialised layout root

inject_libs

optional named character vector/list (names are file_names already present in code_text_list) — one or more package names to inject as library(<pkg>) calls at the very top of that script's text before it is written. Used by the module's corrective re-run for a could not find function "X" error (see .repro_missing_fn_pkgs() for which packages) — reusing this function's existing text-rewrite plumbing rather than a separate rewrite path. NULL (default) injects nothing, unchanged from before this parameter existed. R only: there is no Python corrective re-run (the module's missing-dependency/undefined-name recovery is namespace-export-based — see .repro_find_export_pkg() — with no equivalent built for Python's import statements), so this is ignored for a file whose lang is "Python".

lang

the language each element of code_text_list is in: a single string applied to every file (default "R"), or a named character vector keyed by file_name for a mixed-language set (same shape as repro_file_io()'s own lang). Selects the write-redirect function names (.repro_redirect_writes()) and the working-directory override that is replaced with a statement that does nothing (setwd() becomes invisible(NULL) in R, os.chdir() becomes pass in Python). Python's font-substitution analogue is deliberately NOT built: an unavailable font is a matplotlib WARNING with a silent DejaVu Sans fallback, not the hard "invalid font type" failure family =/base_family = causes in R's graphics devices (the actual reason this transform exists for R) — rewriting Python's many rcParams/plt.rc()/fontdict= spellings would be solving a failure mode Python's own ecosystem does not have here.

script_paths

optional character vector, one per element of code_text_list: the path (relative to root) to write that script to, as the package layout (.repro_package_plan()) needs. NULL (default), or an NA element, places the script by its basename in plan, as before.

writes_in_place

if TRUE (the package layout), a write stays where the code puts it, so a later script finds the file where it expects it; only a write to an absolute path or out of the package is redirected to output/. A quoted relative path whose folder exists from the script's own folder (or its .Rproj folder) but not from the top is first rewritten to its path from the top, for reads and writes alike. FALSE (default) redirects every write, as before.

replace_env_calls

if TRUE, the calls to groundhog, checkpoint and renv in R code are replaced, since packages are installed before the run, and such a call would need the network (see .repro_neutralise_env_calls()). FALSE (default) leaves them.

run_dirs

optional folder each script runs from, from root (.repro_run_folders()), which becomes its run_dir. NULL (default) runs every script from root.

prepend

optional named list, by file name: lines to put at the top of a script, such as loading another script first (.repro_run_after_lines()).

Value

a data frame with file_name, script_path (absolute path written), run_dir (the working directory a run should use — always root), setwd_removed (count of setwd()/os.chdir() calls replaced with a statement that does nothing), env_calls_replaced (count of groundhog, checkpoint and renv calls replaced), slashes_fixed (the quoted Windows paths given /, as they were, joined by "; "; see .repro_slash_paths()), setwd_folder_removed (the paths made relative to the working folder because they lay under the folder of a removed setwd(), as "before -> after", joined by "; "; see .repro_strip_setwd_folder()), setwd_paths (comma-joined paths those calls named, for the warning), family_replaced (count of family =/base_family = named-font arguments replaced with "sans", so a plot call does not fail with "invalid font type" for a font not registered on the machine running the sandbox), family_detail (semicolon-joined <old> -> "sans" text for each replacement), and library_injected (the package name(s) injected via inject_libs for that file, comma-joined, or NA when none was).

Details

A setwd() call in analysis code is bad practice: it hardcodes an assumption about where the code runs (often an absolute path on the author's own machine, e.g. setwd("D:/Dropbox/...")), and it overrides the working directory the runner sets to the materialised layout — so the script's relative reads then resolve against a directory that does not exist here, and the reproduction fails for a reason that has nothing to do with the analysis. To run the code we therefore comment out every setwd() call (recorded per file in setwd_removed and flagged in the report). The transform is in the same spirit as the path rewriting: it makes the author's script runnable in its new location without changing the analysis. A script that only reproduces after a setwd() is removed is reported as "reproducible after ignoring a setwd() that should not have been in the code and needs to be fixed".

Examples

root <- file.path(tempdir(), "scripts_demo")
plan <- data.frame(file_name = c("analysis.R", "x.csv"),
                   target_path = c("analysis/analysis.R", "data/study-x_data.csv"))
code <- list(analysis.R = 'a <- read.csv("x.csv")')
rw <- list(analysis.R = repro_rewrite_paths(code$analysis.R, "analysis.R", plan))
out <- repro_write_scripts(code, rw, plan, root)
#> [repro]   rewriting 'analysis.R' (22 bytes) ...
readLines(out$script_path)
#> [1] "a <- read.csv(\"data/study-x_data.csv\")"