Learn R Programming

this.path (version 1.4.0)

wrap.source: Implement 'this.path()' For Arbitrary 'source()'-Like Functions

Description

A source()-like function is any function which evaluates code from a file.

Currently, this.path() is implemented to work with source(), sys.source(), debugSource() in ‘RStudio’, testthat::source_file(), knitr::knit(), compiler::loadcmp(), and box::use().

wrap.source() and inside.source() can be used to implement this.path() for any other source()-like functions.

set.this.path() is just an alias for inside.source().

See ?this.path(local = TRUE) which returns the path of the executing script, confining the search to the local environment in which inside.source() / / set.this.path() was called.

unset.this.path() will undo a call to set.this.path(). You will need to use this if you wish to call set.this.path() multiple times within a function.

Usage

wrap.source(expr,
    path.only = FALSE,
    character.only = path.only,
    file.only = path.only,
    conv2utf8 = FALSE,
    allow.blank.string = FALSE,
    allow.clipboard = !file.only,
    allow.stdin = !file.only,
    allow.url = !file.only,
    allow.file.uri = !path.only,
    allow.unz = !path.only,
    allow.pipe = !file.only,
    allow.terminal = !file.only,
    allow.textConnection = !file.only,
    allow.rawConnection = !file.only,
    allow.sockconn = !file.only,
    allow.servsockconn = !file.only,
    allow.customConnection = !file.only,
    ignore.all = FALSE,
    ignore.blank.string = ignore.all,
    ignore.clipboard = ignore.all,
    ignore.stdin = ignore.all,
    ignore.url = ignore.all,
    ignore.file.uri = ignore.all)

inside.source(file, path.only = FALSE, character.only = path.only, file.only = path.only, conv2utf8 = FALSE, allow.blank.string = FALSE, allow.clipboard = !file.only, allow.stdin = !file.only, allow.url = !file.only, allow.file.uri = !path.only, allow.unz = !path.only, allow.pipe = !file.only, allow.terminal = !file.only, allow.textConnection = !file.only, allow.rawConnection = !file.only, allow.sockconn = !file.only, allow.servsockconn = !file.only, allow.customConnection = !file.only, ignore.all = FALSE, ignore.blank.string = ignore.all, ignore.clipboard = ignore.all, ignore.stdin = ignore.all, ignore.url = ignore.all, ignore.file.uri = ignore.all, Function = NULL)

set.this.path(file, path.only = FALSE, character.only = path.only, file.only = path.only, conv2utf8 = FALSE, allow.blank.string = FALSE, allow.clipboard = !file.only, allow.stdin = !file.only, allow.url = !file.only, allow.file.uri = !path.only, allow.unz = !path.only, allow.pipe = !file.only, allow.terminal = !file.only, allow.textConnection = !file.only, allow.rawConnection = !file.only, allow.sockconn = !file.only, allow.servsockconn = !file.only, allow.customConnection = !file.only, ignore.all = FALSE, ignore.blank.string = ignore.all, ignore.clipboard = ignore.all, ignore.stdin = ignore.all, ignore.url = ignore.all, ignore.file.uri = ignore.all, Function = NULL)

unset.this.path()

Value

for wrap.source(), the result of evaluating expr.

for inside.source(), if file is a path, then the normalized path with the same attributes, otherwise file itself. The return value of inside.source() should be assigned to a variable before use, something like:

{
    file <- inside.source(file, ...)
    sourcelike(file)
}

Arguments

expr

an (unevaluated) call to a source()-like function.

file

a connection or a character string giving the pathname of the file or URL to read from.

path.only

must file be an existing path? This implies character.only and file.only are TRUE and implies allow.file.uri and allow.unz are FALSE, though these can be manually changed.

character.only

must file be a character string?

file.only

must file refer to an existing file?

conv2utf8

if file is a character string, should it be converted to UTF-8?

allow.blank.string

may file be a blank string, i.e. ""?

allow.clipboard

may file be "clipboard" or a clipboard connection?

allow.stdin

may file be "stdin"? Note that "stdin" refers to the C-level ‘standard input’ of the process, differing from stdin() which refers to the R-level ‘standard input’.

allow.url

may file be a URL pathname or a connection of class "url-libcurl" / / "url-wininet"?

allow.file.uri

may file be a file:// URI?

allow.unz, allow.pipe, allow.terminal, allow.textConnection, allow.rawConnection, allow.sockconn, allow.servsockconn

may file be a connection of class "unz" / / "pipe" / / "terminal" / / "textConnection" / / "rawConnection" / / "sockconn" / / "servsockconn"?

allow.customConnection

may file be a custom connection?

ignore.all, ignore.blank.string, ignore.clipboard, ignore.stdin, ignore.url, ignore.file.uri

ignore the special meaning of these types of strings, treating it as a path instead?

Function

character vector of length 1 or 2; the name of the function and package in which inside.source() / / set.this.path() is called.

Details

inside.source() should be added to the body of your source()-like function before reading / / evaluating the expressions.

wrap.source(), unlike inside.source(), does not accept an argument file. Instead, an attempt is made to extract the file from expr, after which expr is evaluated. It is assumed that the file is the first argument of the function, as is the case with source(), sys.source(), debugSource() in ‘RStudio’, testthat::source_file(), knitr::knit(), and compiler::loadcmp(). The function of the call is evaluated, its formals() are retrieved, and then the arguments of expr are searched for a name matching the name of the first formal argument. If a match cannot be found by name, the first unnamed argument is taken instead. If no such argument exists, the file is assumed missing.

wrap.source() does non-standard evaluation and does some guess work to determine the file. As such, it is less desirable than inside.source() when the option is available. I can think of exactly one scenario in which wrap.source() might be preferable: suppose there is a source()-like function sourcelike() in a foreign package (a package for which you do not have write permission). Suppose that you write your own function in which the formals are (...) to wrap sourcelike():

wrapper <- function (...)
{
    ## possibly more args to wrap.source()
    wrap.source(sourcelike(...))
}

This is the only scenario in which wrap.source() is preferable, since extracting the file from the ... list would be a pain. Then again, you could simply change the formals of wrapper() from (...) to (file, ...). If this does not describe your exact scenario, use inside.source() instead.

Examples

Run this code
FILE <- tempfile(fileext = ".R")
this.path:::write.code({
    this.path::this.path(verbose = TRUE)
}, FILE)


## here we have a source-like function, suppose this
## function is in a package for which you have write permission
sourcelike <- function (file, envir = parent.frame())
{
    file <- inside.source(file)
    envir <- as.environment(envir)
    exprs <- parse(n = -1, file = file)
    ## this prints nicely
    this.path:::withAutoprint(exprs = exprs, evaluated = TRUE,
        local = envir, spaced = TRUE, verbose = FALSE,
        width.cutoff = 60L)
    # ## you could alternatively do:
    # for (i in seq_along(exprs)) eval(exprs[i], envir)
    # ## which does no pretty printing
}


sourcelike(FILE)
sourcelike(con <- file(FILE)); close(con)


## here we have another source-like function, suppose this function
## is in a foreign package for which you do not have write permission
sourcelike2 <- function (pathname, envir = globalenv())
{
    if (!(is.character(pathname) && file.exists(pathname)))
        stop(gettextf("'%s' is not an existing file",
             pathname, domain = "R-base"))
    envir <- as.environment(envir)
    exprs <- parse(n = -1, file = pathname)
    this.path:::withAutoprint(exprs = exprs, evaluated = TRUE,
        local = envir, spaced = TRUE, verbose = FALSE,
        width.cutoff = 60L)
}


## the above function is similar to sys.source(), and it
## expects a character string referring to an existing file
##
## with the following, you should be able
## to use 'this.path()' within 'FILE':
wrap.source(sourcelike2(FILE), path.only = TRUE)


# ## with R >= 4.1.0, use the forward pipe operator '|>' to
# ## make calls to 'wrap.source' more intuitive:
# sourcelike2(FILE) |> wrap.source(path.only = TRUE)


## 'wrap.source' can recognize arguments by name, so they
## do not need to appear in the same order as the formals
wrap.source(sourcelike2(envir = new.env(), pathname = FILE),
    path.only = TRUE)


## it it much easier to define a new function to do this
sourcelike3 <- function (...)
wrap.source(sourcelike2(...), path.only = TRUE)


## the same as before
sourcelike3(FILE)


## however, this is preferable:
sourcelike4 <- function (pathname, ...)
{
    ## pathname is now normalized
    pathname <- inside.source(pathname, path.only = TRUE)
    sourcelike2(pathname = pathname, ...)
}
sourcelike4(FILE)


## perhaps you wish to run several scripts in the same function
fun <- function (paths, ...)
{
    for (pathname in paths) {
        pathname <- set.this.path(pathname, path.only = TRUE)
        sourcelike2(pathname = pathname, ...)
        unset.this.path(pathname)
    }
}
unlink(FILE)

Run the code above in your browser using DataLab