Hiding tempdir() and tempfile() Statements in R Documentation

Intro

When writing documentation for an R package that aims to be listed on CRAN, there is a policy about where file output from examples or actual function calls should be written to on a user’s system. The policy states that:

Packages should not write in the user’s home filespace (including clipboards), nor anywhere else on the file system apart from the R session’s temporary directory (or during installation in the location pointed to by TMPDIR: and such usage should be cleaned up). Installing into the system’s R installation (e.g., scripts to its bin directory) is not allowed. Limited exceptions may be allowed in interactive sessions if the package obtains confirmation from the user.

Surprisingly, many examples found within Base R’s documentation didn’t adhere to writing to the established temporary directory or creating tempfiles. On March 2018, Hadley Wickham posted to r-devel highlighting this discrepancy between Base R packages and their counterparts on CRAN. This led to two patches being developed as part of 17403: “Patch to fix examples that write to working directory” by Gabriel Becker (part of the collective behind the ALTREP framework) and Suharto Anggono.

Woe is Documentation

In short, these patches emphasize that documentation should contain references to tempdir() and tempfile(). Unfortunately, this means the user is easily able to be confused by boilerplate code as more complexities are added that detract from just running the function and observing its behavior. Chiefly, the temporary directories – and the temporary files that reside inside of them – are largely hidden from the working directory so side-effects like how a file is written is minimized.

Take for example the parse() function’s help documentation:

fil <- tempfile(fileext = ".Rdmped")
cat("x <- c(1, 4)\n  x ^ 3 -10 ; outer(1:7, 5:9)\n", file = fil)
# parse 3 statements from our temp file
parse(file = fil, n = 3)
unlink(fil)

I would postulate a better example formulation would be:

cat("x <- c(1, 4)\n  x ^ 3 -10 ; outer(1:7, 5:9)\n",
    file = "stored_script.R")
    
# Parse 3 statements from our temp file
parse(file = fil, n = 3)

But, how could we achieve that formulation while still abiding by the temporary directory policy?

Real World Documentation

Let’s make the ideal documentation view a reality. To do so, we’ll use a trick popularized in the patches that hide modifying the working directory to the temporary directory. In short, this relies upon the command \dontshow{}, which allows the documentation to run a command that will be hidden from the user in a manner similar to knitrs echo = FALSE code chunk option.

#' @examples
#' \dontshow{.old_wd <- setwd(tempdir())}
#'
#' cat("x <- c(1, 4)\n  x ^ 3 -10 ; outer(1:7, 5:9)\n",
#'     file = "stored_script.R")
#'     
#' # Parse 3 statements from our temp file
#' parse(file = fil, n = 3)
#'
#' \dontshow{setwd(.old_wd)}

Thus, we hide the boilerplate code to make the example work and instead focus on the behavior of the function. Moreover, we avoid having to explicitly unlink() files as this is taken care of by R’s default behavior of clearing the temporary directory on exit.

Example Templates

The preceeding example uses the inline roxygen2 documentation style. The generic form for easy copying and pasting is:

#' @examples
#' \dontshow{.old_wd <- setwd(tempdir())}
#'
#' # Your code here
#'
#' \dontshow{setwd(.old_wd)}

For those who still prefer manually writing .Rd files, you could use:

\examples{
 \dontshow{.old_wd <- setwd(tempdir())}

 # Your code here

 \dontshow{setwd(.old_wd)}
}

Fin

The temporary working directory trick is useful; however, please do not feel as if you must use it. There are definitely still relevant use cases for directly calling tempfile() and tempdir() in an example.

comments powered by Disqus