Version: | 2.7.0 |
Date: | 2025-04-10 |
License: | MIT + file LICENSE |
Title: | Get Executing Script's Path |
Description: | Determine the path of the executing script. Compatible with several popular GUIs: 'Rgui', 'RStudio', 'Positron', 'VSCode', 'Jupyter', 'Emacs', and 'Rscript' (shell). Compatible with several functions and packages: 'source()', 'sys.source()', 'debugSource()' in 'RStudio', 'compiler::loadcmp()', 'utils::Sweave()', 'box::use()', 'knitr::knit()', 'plumber::plumb()', 'shiny::runApp()', 'package:targets', and 'testthat::source_file()'. |
Author: | Iris Simmons [aut, cre] |
Maintainer: | Iris Simmons <ikwsimmo@gmail.com> |
Depends: | R (≥ 2.15) |
Suggests: | utils, jsonlite, microbenchmark, rprojroot |
Enhances: | compiler, box, knitr, plumber, shiny, targets, testthat |
URL: | https://github.com/ArcadeAntics/this.path |
BugReports: | https://github.com/ArcadeAntics/this.path/issues |
ByteCompile: | TRUE |
Biarch: | TRUE |
BuildManual: | TRUE |
Type: | Package |
NeedsCompilation: | yes |
Packaged: | 2025-04-10 04:12:43 UTC; iris |
Repository: | CRAN |
Date/Publication: | 2025-04-10 08:50:02 UTC |
Get Script's Path
Description
Determine the path of the executing script.
Compatible with several popular GUIs:
‘Rgui’
‘RStudio’ (including background jobs)
‘Positron’
‘VSCode’ + ‘REditorSupport’
‘Jupyter’
‘
Rscript
’ (shell)
Compatible with several functions and packages:
source()
debugSource
()
in ‘RStudio’box::use()
Details
The most important functions from package:this.path are
this.path()
, this.dir()
, here()
,
and this.proj()
:
this.path()
returns the normalized path of the script in which it is written.this.dir()
returns the directory ofthis.path()
.here()
constructs file paths againstthis.dir()
.this.proj()
constructs file paths against the project root ofthis.dir()
.
New additions include:
LINENO()
returns the line number of the executing expression.shFILE()
looks through the command line arguments, extracting FILE from either of the following: -f FILE or --file=FILEset.sys.path()
implementsthis.path()
for anysource()
-like functions outside of the builtins.with_init.file()
allowsthis.path()
and related to be used in a user profile.
package:this.path also provides functions for constructing and manipulating file paths:
path.join()
,basename2()
, anddirname2()
are drop in replacements forfile.path()
,basename()
, anddirname()
which better handle drives and network shares.splitext()
,removeext()
,ext()
, andext<-()
split a path into root and extension, remove a file extension, get an extension, or set an extension for a file path.path.split()
,path.split.1()
, andpath.unsplit()
split the path to a file into components.relpath()
,rel2here()
, andrel2proj()
turn absolute paths into relative paths.
Note
This package started from a stack overflow posting:
https://stackoverflow.com/questions/1815606/determine-path-of-the-executing-script/64129649#64129649
If you like this package, please consider upvoting my answer so that more
people will see it! If you have an issue with this package, please use
bug.report(package = "this.path")
to
report your issue.
Author(s)
Iris Simmons [aut, cre]
Maintainer: Iris Simmons <ikwsimmo@gmail.com>
Functions That Enhance the Use of Other Packages
Description
These functions improve the user experience of other packages.
Usage
## enchances 'package:box'
with_script_path(expr, file, local = FALSE, n = 0, envir = parent.frame(n + 1),
matchThisEnv = getOption("topLevelEnvironment"),
srcfile = if (n) sys.parent(n) else 0)
## enchances 'package:rprojroot'
make_fix_file(criterion, local = FALSE, n = 0, envir = parent.frame(n + 1),
matchThisEnv = getOption("topLevelEnvironment"),
srcfile = if (n) sys.parent(n) else 0)
Arguments
expr |
an expression to evaluate after setting the current script in
package:box; most commonly a call to
|
file |
a character string giving the pathname of the file. |
criterion |
argument passed to
|
local , n , envir , matchThisEnv , srcfile |
See
|
Details
with_script_path()
improves the experience of package:box; it
sets the current script in package:box to file
or
this.path()
using
box::set_script_path()
, then evaluates its
argument, most commonly a package:box import statement.
make_fix_file()
improves the experience of package:rprojroot; it
looks for a project root starting with this.dir()
, then makes a
function that constructs file paths against said project root.
Value
for with_script_path()
, the result of evaluating expr
.
for make_fix_file()
, a function with formals (..., .. = 0)
that
returns a character vector.
Examples
# this.path::with_script_path(
# box::use(
# <import 1>,
# <import 2>,
# <...>
# )
# )
# ## replace 'rprojroot::is_r_package' with desired criterion
#
# fix_file <- this.path::make_fix_file(rprojroot::is_r_package)
Macros in Package 'this.path'
Description
FILE()
and LINE()
are intended to be used in a similar manner
to the macros __FILE__
and __LINE__
in C. They are useful for
generating a diagnostic message / / warning / / error to about the status of
the program.
Usage
FILE()
LINE()
Examples
FILE.R <- tempfile(fileext = ".R")
writeLines("fun <- function ()
{
message(sprintf('invalid value %d at %s, line %d',
-1, FILE(), LINE()))
}
", FILE.R)
source(FILE.R, verbose = FALSE, keep.source = TRUE)
fun()
unlink(FILE.R)
Line Number of Executing Expression
Description
Get the line number of the executing expression.
Usage
LINENO(n = 0, envir = parent.frame(n + 1),
matchThisEnv = getOption("topLevelEnvironment"),
srcfile = if (n) sys.parent(n) else 0)
Arguments
n , envir , matchThisEnv , srcfile |
See |
Details
LINENO()
only works if the expressions have a
srcref
.
Scripts run with Rscript
do not store their srcref
, even when
getOption("keep.source")
is TRUE
.
For source()
and sys.source()
, make sure to supply argument
keep.source = TRUE
directly, or set options "keep.source"
and
"keep.source.pkgs"
to TRUE
.
For debugSource
()
in ‘RStudio’, it has no argument keep.source
, so set
option "keep.source"
to TRUE
before calling.
For compiler::loadcmp()
, the srcref
is never stored for the compiled
code, there is nothing that can be done.
For utils::Sweave()
, the srcref
is never stored, there is nothing
that can be done.
For knitr::knit()
, the srcref
is never stored, there is nothing that
can be done. I am looking into a fix.
For package:targets, set option "keep.source"
to TRUE
before
calling associated functions.
For box::use()
, plumber::plumb()
, shiny::runApp()
, and
testthat::source_file()
, the srcref
is always stored.
Value
integer; NA_integer_
if the line number is not found.
Note
You can get a more accurate line number by wrapping LINENO()
in
braces:
{ LINENO() }
Examples
FILE.R <- tempfile(fileext = ".R")
writeLines(c("
LINENO()
LINENO()
## LINENO() respects #line directives
#line 15
LINENO()
#line 1218
cat(sprintf('invalid value %d at %s, line %d\\n',
-5, try.this.path(), LINENO()))
"), FILE.R)
if (getRversion() >= "4.3.0") {
source(FILE.R, echo = TRUE, verbose = FALSE,
max.deparse.length = Inf, keep.source = TRUE)
} else {
this.path:::.source(FILE.R, echo = TRUE, verbose = FALSE,
max.deparse.length = Inf, keep.source = TRUE)
}
unlink(FILE.R)
Detect the Operating System Type
Description
OS.type
is a list of TRUE
/ / FALSE
values dependent on
the platform under which this package was built.
Usage
OS.type
Value
A list with at least the following components:
AIX |
Built under IBM AIX. |
HPUX |
Built under Hewlett-Packard HP-UX. |
linux |
Built under some distribution of Linux. |
darwin |
Built under Apple OSX and iOS (Darwin). |
iOS.simulator |
Built under iOS in Xcode simulator. |
iOS |
Built under iOS on iPhone, iPad, etc. |
macOS |
Built under OSX. |
solaris |
Built under Solaris (SunOS). |
cygwin |
Built under Cygwin POSIX under Microsoft Windows. |
windows |
Built under Microsoft Windows. |
win64 |
Built under Microsoft Windows (64-bit). |
win32 |
Built under Microsoft Windows (32-bit). |
UNIX |
Built under a UNIX-style OS. |
Source
Using R From a Shell
Description
How to use R from a shell (including the Windows command-line / / Unix terminal).
Details
For the purpose of running R scripts, there are four ways to do it. Suppose our R script has filename script1.R, we could write any of:
R -f script1.R
R --file=script1.R
R CMD BATCH script1.R
Rscript script1.R
The first two are different ways of writing equivalent statements. The third
statement is the first statement plus options --restore
--save (plus option --no-readline under Unix-alikes), and
it also saves the stdout
and
stderr
in a file of your choosing. The
fourth statement is the second statement plus options --no-echo
--no-restore. You can try:
R --help
R CMD BATCH --help
Rscript --help
for a help message that describes what these options mean. In general,
Rscript
is the one you want to use. It should be noted that
Rscript
has some exclusive
environment variables
(not used by the other
executables) that will make its behaviour different from R
.
For the purpose of making packages, R CMD
is what you will need.
Most commonly, you will use:
R CMD build
R CMD INSTALL
R CMD check
R CMD build
will turn an R package (specified by a directory) into
tarball. This allows for easy sharing of R packages with other people,
including
submitting a package to CRAN.
R CMD INSTALL
will install an R package (specified by a directory
or tarball), and is used by
install.packages()
.
R CMD check
will check an R package (specified by a tarball) for
possible errors in code, documentation, tests, and much more.
If, when you execute one of the previous commands, you see the following error message: “‘R’ is not recognized as an internal or external command, operable program or batch file.”, see section Ease of Use on Windows.
Ease of Use on Windows
Under Unix-alikes, it is easy to invoke an R session from a shell by typing the name of the R executable you wish to run. On Windows, you should see that typing the name of the R executable you wish to run does not run that application, but instead signals an error. Instead, you will have to type the full path of the directory where your R executables are located (see section Where are my R executable files located?), followed by the name of the R executable you wish to run.
This is not very convenient to type everytime something needs to be run from a shell, plus it has another issue of being computer dependent. The solution is to add the path of the directory where your R executables are located to the Path environment variable. The Path environment variable is a list of directories where executable programs are located. When you type the name of an executable program you wish to run, Windows looks for that program through each directory in the Path environment variable. When you add the full path of the directory where your R executables are located to your Path environment variable, you should be able to run any of those executable programs by their basenames (‘R’, ‘Rcmd’, ‘Rscript’, and ‘Rterm’) instead of their full paths.
To add a new path to your Path environment variable:
Open the Windows search bar (press Windows key)
Type Edit environment variables for your account
Click the variable Path
Click the button Edit...
Click the button New
Type (or paste) the full path of the directory where your R executables are located and click the OK button
This will modify your environment variable Path, not the systems. If another user wishes to run R from a shell, they will have to add the directory to their Path environment variable as well.
If you wish to modify the system environment variable Path (you will need admin permissions):
Open the Windows search bar (press Windows key)
Type Edit the system environment variables
Click the button Environment Variables...
Click the variable Path in section System variables
Click the button Edit...
Click the button New
Type (or paste) the full path of the directory where your R executables are located and click the OK button
To check that this worked correctly, open a shell and execute the following commands:
R --help
R --version
You should see that the first prints the usage message for the R executable while the second prints information about the version of R currently being run. If you have multiple versions of R installed, make sure this is the version of R you wish to run.
Where are my R executable files located?
In an R session, you can find the location of your R executable files with the following command:
R.home("bin")
For me, this is:
‘/usr/lib/R/bin
’
‘C:/PROGRA~1/R/R-
4.4.2/bin/x64
’
Set Environment Variables
Description
Sys.putenv()
sets environment variables (for other processes called
from within R or future calls to Sys.getenv()
from this
R process).
Usage
Sys.putenv(x)
Arguments
x |
a character vector, or an object coercible to character. Strings
must be of the form
|
Value
A logical vector, with elements being true if setting the corresponding variable succeeded.
See Also
Examples
Sys.putenv(c("R_TEST=testit", "A+C=123"))
Sys.getenv("R_TEST")
Sys.unsetenv("R_TEST") ## under Unix-alikes may warn and not succeed
Sys.getenv("R_TEST", unset = NA)
Manipulate File Paths
Description
basename2()
removes all of the path up to and including the last path
separator (if any).
dirname2()
returns the part of the path
up to but excluding the
last path separator, or "."
if there is no path separator.
Usage
basename2(path, expand = TRUE)
dirname2(path, expand = TRUE)
Arguments
path |
character vector, containing path names. |
expand |
logical. Should tilde (see |
Details
Trailing path separators are removed before dissecting the path, and for
dirname2()
any trailing file separators are removed from the result.
Value
A character vector of the same length as path
.
Behaviour on Windows
If path
is an empty string, then both dirname2()
and
basename2()
return an emty string.
\
and /
are accepted as
path separators, and dirname2()
does NOT translate the path
separators.
Recall that a network share looks like "//host/share"
and a drive
looks like "d:"
.
For a path which starts with a network share or drive, the path specification
is the portion of the string immediately afterward, e.g.
"/path/to/file"
is the path specification of
"//host/share/path/to/file"
and "d:/path/to/file"
. For a path
which does not start with a network share or drive, the path specification is
the entire string.
The path specification of a network share will always be empty or absolute,
but the path specification of a drive does not have to be, e.g.
"d:file"
is a valid path despite the fact that the path specification
does not start with "/"
.
If the path specification of path
is empty or is "/"
, then
dirname2()
will return path
and basename2()
will return
an empty string.
Behaviour under Unix-alikes
If path
is an empty string, then both dirname2()
and
basename2()
return an emty string.
Recall that a network share looks like "//host/share"
.
For a path which starts with a network share, the path specification is the
portion of the string immediately afterward, e.g. "/path/to/file"
is
the path specification of "//host/share/path/to/file"
. For a path
which does not start with a network share, the path specification is the
entire string.
If the path specification of path
is empty or is "/"
, then
dirname2()
will return path
and basename2()
will return
an empty string.
Examples
path <- c("/usr/lib", "/usr/", "usr", "/", ".", "..")
x <- cbind(path, dirname = dirname2(path), basename = basename2(path))
print(x, quote = FALSE, print.gap = 3)
Check 'this.path()' is Functioning Correctly
Description
Add check.path("path/to/file")
to the start of your script to
initialize this.path()
and check that it is returning the
expected path.
Usage
check.path(...)
check.dir(...)
check.proj(...)
Arguments
... |
further arguments passed to |
Details
check.proj()
is a specialized version of check.path()
that
checks the path up to the project root.
Value
if the expected path / / directory matches this.path()
/ /
this.dir()
, then TRUE
invisibly, otherwise an error is thrown.
Examples
# ## I have a project called 'd_cead'
# ##
# ## Within this project, I have a folder called 'code'
# ## where I place all of my scripts.
# ##
# ## One of these scripts is called 'provrun.R'
# ##
# ## So, at the top of that R script, I could write:
#
#
# this.path::check.path("d_cead", "code", "provrun.R")
#
# ## or:
#
# this.path::check.path("d_cead/code/provrun.R")
File Extensions
Description
splitext()
splits an extension from a path.
removeext()
removes an extension from a path.
ext()
gets the extension of a path.
ext<-()
sets the extension of a path.
Usage
splitext(path, compression = FALSE, expand = TRUE)
removeext(path, compression = FALSE, expand = TRUE)
ext(path, compression = FALSE, expand = TRUE)
ext(path, compression = FALSE, expand = TRUE) <- value
Arguments
path |
character vector, containing path names. |
compression |
should compression extensions |
expand |
logical. Should tilde (see |
value |
a character vector, typically of length
|
Details
Trailing path separators are removed before dissecting the path.
Except for path <- NA_character_
, it will always be true that
path == paste0(removeext(path), ext(path))
.
Value
for splitext()
, a matrix with 2
rows and
length(path)
columns. The first row will be the roots of the paths,
the second row will be the extensions of the paths.
for removeext()
and ext()
, a character vector the same length
as path
.
for ext<-()
, the updated object.
Examples
splitext(character(0))
splitext("")
splitext("file.ext")
path <- c("file.tar.gz", "file.tar.bz2", "file.tar.xz")
splitext(path, compression = FALSE)
splitext(path, compression = TRUE)
path <- "this.path_2.7.0.tar.gz"
ext(path) <- ".png"
path
path <- "this.path_2.7.0.tar.gz"
ext(path, compression = TRUE) <- ".png"
path
Get Initial Working Directory
Description
getinitwd()
returns an absolute filepath representing the working
directory at the time of loading this package.
Usage
getinitwd()
initwd
Value
character string, or NULL
if the initial working directory is not
available.
Examples
cat("\ninitial working directory:\n"); getinitwd()
cat("\ncurrent working directory:\n"); getwd()
Construct Path to File, Starting With Script's Directory
Description
here()
constructs file paths starting with this.dir()
.
this.proj()
constructs file paths starting with the project root of
this.dir()
.
reset.proj()
resets the path cache of this.proj()
. This can be
useful if you create a new project that you would like to be detected without
restarting your R session.
Usage
here(..., local = FALSE, n = 0,
envir = parent.frame(n + 1),
matchThisEnv = getOption("topLevelEnvironment"),
srcfile = if (n) sys.parent(n) else 0, .. = 0)
this.proj(..., local = FALSE, n = 0,
envir = parent.frame(n + 1),
matchThisEnv = getOption("topLevelEnvironment"),
srcfile = if (n) sys.parent(n) else 0)
reset.proj()
## alias for 'here'
ici(..., local = FALSE, n = 0,
envir = parent.frame(n + 1),
matchThisEnv = getOption("topLevelEnvironment"),
srcfile = if (n) sys.parent(n) else 0, .. = 0)
Arguments
... |
further arguments passed to |
local , n , envir , matchThisEnv , srcfile |
See
|
.. |
the number of directories to go back. |
Details
For this.proj()
, the project root has the same criterion as
here::here()
, but unlike here::here()
, this.proj()
supports sub-projects and multiple projects in use at once. Additionally,
this.proj()
is independent of working directory, whereas
here::here()
relies on the working directory being set somewhere
within the project when package:here is loaded. Arguably, this makes it
better than here::here()
.
Value
A character vector of the arguments concatenated term-by-term.
Examples
tmpdir <- tempfile(pattern = "dir")
dir.create(tmpdir)
writeLines("this file signifies that its directory is the project root",
this.path::path.join(tmpdir, ".here"))
FILE.R <- this.path::path.join(tmpdir, "src", "R", "script1.R")
dir.create(this.path::dirname2(FILE.R), recursive = TRUE)
this.path:::.writeCode({
this.path::this.path()
this.path::this.proj()
## use 'here' to run another script located nearby
this.path::here("script2.R")
## or maybe to read input from a file
this.path::here(.. = 2, "input", "data1.csv")
## but sometimes it is easier to use the project root
## this allows you to move the R script up or down
## a directory without changing the .. number
this.path::this.proj("input", "data1.csv")
}, FILE.R)
source(FILE.R, echo = TRUE)
unlink(tmpdir, recursive = TRUE)
Top-Level Code Environment
Description
Determine if a program is the main program, or if a program was run from a shell.
Usage
is.main()
from.shell()
Details
If no scripts are being source()
-ed, then is.main()
will be
TRUE
, and FALSE
otherwise.
If is.main()
is TRUE
and R was invoked with the command-line
arguments -f FILE, --file=FILE, or -e
EXPR, then from.shell()
will be TRUE
, and FALSE
otherwise.
Value
TRUE
or FALSE
.
Examples
FILE.R <- tempfile(fileext = ".R")
this.path:::.writeCode({
this.path:::.withAutoprint({
from.shell()
is.main()
}, spaced = TRUE, verbose = FALSE,
prompt.echo = "FILE.R> ", continue.echo = "FILE.R+ ")
}, FILE.R)
this.path:::.Rscript(c(
"--default-packages=this.path", "--vanilla",
FILE.R
), show.command = FALSE, show.output.on.console = TRUE)
source(FILE.R, verbose = FALSE)
this.path:::.Rscript(c(
"--default-packages=this.path", "--vanilla",
"-e", "cat(\"\n> from.shell()\\n\")",
"-e", "from.shell()",
"-e", "cat(\"\n> is.main()\\n\")",
"-e", "is.main()",
"-e", "cat(\"\n> source(commandArgs(trailingOnly = TRUE))\\n\")",
"-e", "source(commandArgs(trailingOnly = TRUE))", FILE.R
), show.command = FALSE, show.output.on.console = TRUE)
unlink(FILE.R)
Constructs Path Functions Similar to 'this.path()'
Description
make_fix_funs()
accepts a pathname and constructs a set of
path-related functions, similar to this.path()
and associated.
Usage
make_fix_funs(file, delayed = FALSE, local = FALSE, n = 0,
envir = parent.frame(n + 1),
matchThisEnv = getOption("topLevelEnvironment"),
srcfile = if (n) sys.parent(n) else 0)
## alias for 'make_fix_funs'
path.functions(file, delayed = FALSE, local = FALSE, n = 0,
envir = parent.frame(n + 1),
matchThisEnv = getOption("topLevelEnvironment"),
srcfile = if (n) sys.parent(n) else 0)
Arguments
file |
a character string giving the pathname of the file or URL. |
delayed |
|
local , n , envir , matchThisEnv , srcfile |
See |
Value
An environment with at least the following bindings:
this.path |
Function with formals |
this.dir |
Function with no formals which returns the directory of the
|
here , ici |
Function with formals |
this.proj |
Function with formals |
rel2here , rel2proj |
Functions with formals |
LINENO |
Function with no formals which returns the line number of the executing
expression in |
Construct Path to File
Description
Construct the path to a file from components / / paths in a platform-DEPENDENT way.
Usage
path.join(...)
Arguments
... |
character vectors. |
Details
When constructing a path to a file, the last absolute path is selected and
all trailing components are appended. This is different from
file.path()
where all trailing paths are treated as
components.
Value
A character vector of the arguments concatenated term-by-term and separated
by "/"
.
Examples
path.join("/", "test1")
path.join("/path/to/file1", "/path/to/file2")
path.join("//host-name/share-name/path/to/file1", "/path/to/file2")
path.join("testing", "/testing", "~", "~/testing", "//host",
"//host/share", "//host/share/path/to/file", "not-an-abs-path")
path.join("/test1", "test2", "test3")
path.join("test1", "/test2", "test3", "//host/share/test4", "test5",
"/test6", "test7", "test8", "test9")
Split File Path Into Individual Components
Description
Split the path to a file into components in a platform-DEPENDENT way.
Usage
path.split(path)
path.split.1(path)
path.unsplit(...)
Arguments
path |
character vector. |
... |
character vectors, or one list of character vectors. |
Value
for path.split()
, a list of character vectors.
for path.split.1()
and path.unsplit()
, a character vector.
Note
path.unsplit()
is NOT the same as path.join()
.
Examples
path <- c(
NA,
"",
paste0("https://raw.githubusercontent.com/ArcadeAntics/PACKAGES/",
"src/contrib/Archive/this.path/this.path_1.0.0.tar.gz"),
"//host/share/path/to/file",
"//host/share/",
"//host//share",
"/path/to/file",
"~/path/to/file",
"path/to/file",
## paths with character encodings
`Encoding<-`("path/to/fil\xe9", "latin1"),
"/home/iris/Documents/\u03b4.R"
)
print(x <- path.split(path))
print(path.unsplit(x))
Printing "ThisPathDocumentContext" Objects
Description
Print a "ThisPathDocumentContext"
object.
Usage
## S3 method for class 'ThisPathDocumentContext'
print(x, ..., quote = TRUE)
## S3 method for class 'ThisPathDocumentContext'
format(x, ...)
## S3 method for class 'ThisPathDocumentContext'
as.character(x, ...)
Arguments
x |
object of class |
... |
unused. |
quote |
logical, indicating whether or not strings should be printed with surrounding quotes. |
Details
An object of class "ThisPathDocumentContext"
is generated upon calling
set.sys.path()
, wrap.source()
,
sys.path()
, env.path()
, or
src.path()
, and by extension this.path()
. It
contains information about the path of the executing / / current script.
These objects are not usually user-visible.
Value
for print.ThisPathDocumentContext()
, x
invisibly.
for format.ThisPathDocumentContext()
, a character vector of lines.
for as.character.ThisPathDocumentContext()
, a character string of
concatenated lines.
Examples
fun <- function (file)
{
set.sys.path(file, Function = "fun")
`.this.path::document.context`
}
fun()
fun("clipboard")
fun(paste0("https://raw.githubusercontent.com/ArcadeAntics/",
"this.path/main/tests/sys-path-with-urls.R"))
FILE.R <- tempfile(fileext = ".R"); file.create(FILE.R)
x <- fun(FILE.R)
print(x)
print(x, quote = FALSE)
format(x)
as.character(x)
unlink(FILE.R)
Providing Arguments to a Script
Description
withArgs()
allows you to source()
an R script while providing
arguments. As opposed to running with Rscript
, the code will be
evaluated in the same session in an environment of your choosing.
progArgs()
is a generalized version of
commandArgs(trailingOnly = TRUE)
, allowing you to access
the program's arguments whether it was source()
-ed or run from a shell.
asArgs()
coerces R objects into a character vector, for use with
command line applications and withArgs()
.
Usage
asArgs(...)
progArgs(ifnotfound = character())
withArgs(...)
Arguments
... |
R objects to turn into script arguments; typically
for |
ifnotfound |
any R object, e.g., |
Details
progArgs()
will return the arguments associated with the executing
script, or commandArgs(trailingOnly = TRUE)
when there is no executing
script.
asArgs()
coerces objects into command-line arguments. ...
is
first put into a list, and then each non-list element is converted to
character. They are converted as follows:
- Factors (class
"factor"
) -
using
as.character.factor()
- Date-Times (class
"POSIXct"
and"POSIXlt"
) -
using format
"%Y-%m-%d %H:%M:%OS6"
(retains as much precision as possible) - Numbers (class
"numeric"
and"complex"
) -
with 17 significant digits (retains as much precision as possible) and
"."
as the decimal point character. - Raw Bytes (class
"raw"
) -
using
sprintf("0x%02x", )
(for easy conversion back to raw withas.raw()
oras.vector(, "raw")
)
All others will be converted to character using
as.character()
and its methods.
The arguments will then be unlisted, and all attributes will be removed.
Arguments that are NA_character_
after conversion will be converted to
"NA"
(since the command-line arguments also never have missing
strings).
Value
for asArgs()
and progArgs()
, a character vector.
for withArgs()
, the result of evaluating the first argument.
Examples
this.path::asArgs(NULL, c(TRUE, FALSE, NA), 1:5, pi, exp(6i),
letters[1:5], as.raw(0:4), Sys.Date(), Sys.time(),
list(list(list("lists are recursed"))))
FILE.R <- tempfile(fileext = ".R")
this.path:::.writeCode({
this.path:::.withAutoprint({
this.path::sys.path()
this.path::progArgs()
}, spaced = TRUE, verbose = FALSE, width.cutoff = 60L)
}, FILE.R)
## wrap your source call with a call to withArgs()
this.path::withArgs(
source(FILE.R, local = TRUE, verbose = FALSE),
letters[6:10], pi, exp(1)
)
this.path:::.Rscript(c("--default-packages=NULL", "--vanilla", FILE.R,
this.path::asArgs(letters[16:20], pi, Sys.time())))
this.path:::.Rscript(c("--default-packages=NULL", "--vanilla",
rbind("-e", readLines(FILE.R)[-2L]),
this.path::asArgs(letters[16:20], pi, Sys.time())))
# ## with R >= 4.1.0, use the forward pipe operator '|>' to
# ## make calls to withArgs() more intuitive:
# source(FILE.R, local = TRUE, verbose = FALSE) |> this.path::withArgs(
# letters[6:10], pi, exp(1))
## withArgs() also works with set.sys.path()
sourcelike <- function (file, envir = parent.frame())
{
file <- set.sys.path(file)
envir <- as.environment(envir)
exprs <- parse(n = -1, file = file)
for (i in seq_along(exprs)) eval(exprs[i], envir)
}
this.path::withArgs(sourcelike(FILE.R), letters[21:26])
unlink(FILE.R)
Make a Path Relative to Another
Description
When working with this.path, you will be dealing with a lot of absolute
paths. These paths are not portable for saving within files nor tables, so
convert them to relative paths with relpath()
.
Usage
relpath(path, relative.to = getwd())
rel2here(path, local = FALSE, n = 0, envir = parent.frame(n + 1),
matchThisEnv = getOption("topLevelEnvironment"),
srcfile = if (n) sys.parent(n) else 0)
rel2proj(path, local = FALSE, n = 0,
envir = parent.frame(n + 1),
matchThisEnv = getOption("topLevelEnvironment"),
srcfile = if (n) sys.parent(n) else 0)
Arguments
path |
character vector of file / / URL pathnames. |
relative.to |
character string; the file / / URL pathname to make
|
local , n , envir , matchThisEnv , srcfile |
See
|
Details
Tilde-expansion (see ?path.expand()
) is first done on
path
and relative.to
.
If path
and relative.to
are equivalent, "."
will be
returned. If path
and relative.to
have no base in common, the
normalized
path
will be returned.
Value
character vector of the same length as path
.
Examples
## Not run:
relpath(
c(
## paths which are equivalent will return "."
"/home/effective_user/Documents/this.path/man",
## paths which have no base in common return as themselves
paste0("https://raw.githubusercontent.com/ArcadeAntics/",
"this.path/main/tests/sys-path-with-urls.R"),
"//host-name/share-name/path/to/file",
"/home/effective_user/Documents/testing",
"/home/effective_user",
"/home/effective_user/Documents/R/thispath.R"
),
relative.to = "/home/effective_user/Documents/this.path/man"
)
## End(Not run)
Declare GUI's Active Document
Description
set.gui.path()
can be used to implement this.path()
for
arbitrary GUIs.
Usage
set.gui.path(...)
thisPathNotExistsError(..., call. = TRUE, domain = NULL,
call = .getCurrentCall())
thisPathNotFoundError(..., call. = TRUE, domain = NULL,
call = .getCurrentCall())
Arguments
... , call. , domain , call |
See details. |
Details
thisPathNotExistsError()
and thisPathNotFoundError()
are
provided for use inside set.gui.path()
, and should not be used
elsewhere.
If no arguments are passed to set.gui.path()
, the default behaviour
will be restored.
If one argument is passed to set.gui.path()
, it must be a function
that returns the path of the active document in your GUI. It must accept the
following arguments: (verbose, original, for.msg, contents)
(default
values are unnecessary). This makes sense for a GUI which can edit and run R
code from several different documents such as RGui, RStudio, Positron,
VSCode + REditorSupport, and Emacs + ESS.
If two or three arguments are passed to set.gui.path()
, they must be
the name of the GUI, the path of the active document, and optionally a
function to get the contents of the document. If provided, the function must
accept at least one argument which will be the normalized path of the
document. This makes sense for a GUI which can edit and run R code from only
one document such as Jupyter and shell.
It is best to call this function as a user hook.
setHook(packageEvent("this.path"), function(pkgname, pkgpath) { this.path::set.gui.path(<...>) }, action = "prepend")
An example for a GUI which can run code from multiple documents:
evalq(envir = new.env(parent = .BaseNamespaceEnv), { .guiname <- "myGui" .custom_gui_path <- function(verbose, original, for.msg, contents) { if (verbose) cat("Source: document in", .guiname, "\n") ## your GUI needs to know which document is active ## and some way to retrieve that document from R doc <- <.myGui_activeDocument()> ## if no documents are open, 'doc' should be NULL ## or some other object to represent no documents open if (is.null(doc)) { if (for.msg) NA_character_ else stop(this.path::thisPathNotExistsError( "R is running from ", .guiname, " with no documents open\n", " (or document has no path)")) } else if (contents) { ## somehow, get and return the contents of the document <doc$contents> } else { ## somehow, get the path of the document path <- <doc$path> if (nzchar(path)) { ## if the path is not normalized, this will normalize it if (isFALSE(original)) normalizePath(path, "/", TRUE) else path # ## otherwise, you could just do: # path } else if (for.msg) ## return "Untitled" possibly translated gettext("Untitled", domain = "RGui", trim = FALSE) else stop(this.path::thisPathNotFoundError( "document in ", .guiname, " does not exist")) } } ## recommended to prevent tampering lockEnvironment(environment(), bindings = TRUE) setHook(packageEvent("this.path"), function(pkgname, pkgpath) { this.path::set.gui.path(.custom_gui_path) }, action = "prepend") })
An example for a GUI which can run code from only one document:
evalq(envir = new.env(parent = .BaseNamespaceEnv), { .guiname <- "myGui" .path <- "~/example.R" .custom_get_contents <- function(path) { ## get the contents of the document readLines(path, warn = FALSE) } ## recommended to prevent tampering lockEnvironment(environment(), bindings = TRUE) setHook(packageEvent("this.path"), function(pkgname, pkgpath) { this.path::set.gui.path(.guiname, .path, .custom_get_contents) }, action = "prepend") # ## if your GUI does not have/need a .custom_get_contents # ## function, then this works just as well: # setHook(packageEvent("this.path"), function(pkgname, pkgpath) { # this.path::set.gui.path(.guiname, .path) # }, action = "prepend") })
Value
a list of the previous settings for set.gui.path()
, similar to
options()
.
Declare Executing 'Jupyter' Notebook's Filename
Description
this.path()
does some guess work to determine the path of the
executing notebook in ‘Jupyter’. This involves listing all the files
in the initial working directory, filtering those which are R notebooks,
then filtering those with contents matching the top-level expression.
This could possibly select the wrong file if the same top-level expression is
found in another file. As such, you can use set.jupyter.path()
to
declare the executing ‘Jupyter’ notebook's filename.
Usage
set.jupyter.path(...)
Arguments
... |
further arguments passed to |
Details
This function may only be called from a top-level context in ‘Jupyter’. It is recommended that you do NOT provide an absolute path. Instead, provide just the basename and the directory will be determined by the initial working directory.
Value
character string, invisibly; the declared path for ‘Jupyter’.
Examples
# ## if you opened the file "~/file50b816a24ec1.ipynb", the initial
# ## working directory should be "~". You can write:
#
# set.jupyter.path("file50b816a24ec1.ipynb")
#
# ## and then this.path() will return "~/file50b816a24ec1.ipynb"
Implement 'this.path()' For Arbitrary 'source()'-Like Functions
Description
sys.path()
is implemented to work with these functions and
packages:
source()
debugSource
()
in ‘RStudio’box::use()
set.sys.path()
can be used to implement sys.path()
for any
other source()
-like functions.
set.env.path()
and set.src.path()
can be used alongside
set.sys.path()
to implement env.path()
and
src.path()
, thereby fully implementing
this.path()
. Note that set.env.path()
only makes sense
if the code is being modularized, see Examples.
unset.sys.path()
will undo a call to set.sys.path()
. You will
need to use this if you wish to call set.sys.path()
multiple times
within a function.
set.sys.path.function()
is a special variant of set.sys.path()
to be called within callr::r()
on a function with an appropriate
srcref
.
with_sys.path()
is a convenient way to evaluate code within the
context of a file. Whereas set.sys.path()
can only be used within a
function, with_sys.path()
can only be used outside a function.
See ?sys.path(local = TRUE)
which returns the path of the
executing script, confining the search to the local environment in which
set.sys.path()
was called.
wrap.source()
should not be used, save for one specific use-case. See
details.
Usage
set.sys.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, ofile, delayed = FALSE)
set.env.path(envir, matchThisEnv = getOption("topLevelEnvironment"))
set.src.path(srcfile)
unset.sys.path()
set.sys.path.function(fun)
with_sys.path(file, expr, ...)
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)
Arguments
expr |
for for |
file |
a
|
path.only |
must |
character.only |
must |
file.only |
must |
conv2utf8 |
if |
allow.blank.string |
may |
allow.clipboard |
may |
allow.stdin |
may |
allow.url |
may |
allow.file.uri |
may |
allow.unz , allow.pipe , allow.terminal , allow.textConnection , allow.rawConnection , allow.sockconn , allow.servsockconn |
may
|
allow.customConnection |
may |
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 |
ofile |
a
|
delayed |
|
envir , matchThisEnv |
arguments passed to
|
srcfile |
source file in which to assign a pathname. |
fun |
function with a |
... |
further arguments passed to |
Details
set.sys.path()
should be added to the body of your source()
-like
function before reading / / evaluating the expressions.
wrap.source()
, unlike set.sys.path()
, 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 most
source()
-like functions. 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 set.sys.path()
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
set.sys.path()
instead.
Value
for set.sys.path()
, if file
is a path, then the normalized
path with the same attributes, otherwise file
itself. The return value
of set.sys.path()
should be assigned to a variable before use,
something like:
{ file <- set.sys.path(file, ...) sourcelike(file) }
for set.env.path()
, envir
invisibly.
for set.src.path()
, srcfile
invisibly.
for unset.sys.path()
and set.sys.path.function()
, NULL
invisibly.
for with_sys.path()
and wrap.source()
, the result of evaluating
expr
.
Using 'ofile'
ofile
can be used when the file
argument supplied to
set.sys.path()
is not the same as the file
argument supplied to
the source()
-like function:
sourcelike <- function (file) { ofile <- file if (!is.character(ofile) || length(ofile) != 1) stop(gettextf("'%s' must be a character string", "file")) ## if the file exists, do nothing if (file.exists(file)) { } ## look for the file in the home directory ## if it exists, do nothing else if (file.exists(file <- this.path::path.join("~", ofile))) { } ## you could add other directories to look in, ## but this is good enough for an example else stop(gettextf("'%s' is not an existing file", ofile)) file <- this.path::set.sys.path(file, ofile = ofile) exprs <- parse(n = -1, file = file) for (i in seq_along(exprs)) eval(exprs[i], envir) invisible() }
Examples
FILE.R <- tempfile(fileext = ".R")
this.path:::.writeCode({
this.path::sys.path(verbose = TRUE)
try(this.path::env.path(verbose = TRUE))
this.path::src.path(verbose = TRUE)
this.path::this.path(verbose = TRUE)
}, FILE.R)
## 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())
{
ofile <- file
file <- set.sys.path(file, Function = "sourcelike")
lines <- readLines(file, warn = FALSE)
filename <- sys.path(local = TRUE, for.msg = TRUE)
isFile <- !is.na(filename)
if (isFile) {
timestamp <- file.mtime(filename)[1]
## in case 'ofile' is a URL pathname / / 'unz' connection
if (is.na(timestamp))
timestamp <- Sys.time()
}
else {
filename <- if (is.character(ofile)) ofile else "<connection>"
timestamp <- Sys.time()
}
srcfile <- srcfilecopy(filename, lines, timestamp, isFile)
set.src.path(srcfile)
exprs <- parse(text = lines, srcfile = srcfile, keep.source = FALSE)
invisible(source.exprs(exprs, evaluated = TRUE, envir = envir))
}
sourcelike(FILE.R)
sourcelike(conn <- file(FILE.R)); close(conn)
## 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)
lines <- readLines(pathname, warn = FALSE)
srcfile <- srcfilecopy(pathname, lines, isFile = TRUE)
exprs <- parse(text = lines, srcfile = srcfile, keep.source = FALSE)
invisible(source.exprs(exprs, evaluated = TRUE, envir = envir))
}
## 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 'sys.path()' within 'FILE.R':
wrap.source(sourcelike2(FILE.R), path.only = TRUE)
# ## with R >= 4.1.0, use the forward pipe operator '|>' to
# ## make calls to 'wrap.source' more intuitive:
# sourcelike2(FILE.R) |> 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.R),
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.R)
## however, this is preferable:
sourcelike4 <- function (pathname, ...)
{
## pathname is now normalized
pathname <- set.sys.path(pathname, path.only = TRUE)
sourcelike2(pathname = pathname, ...)
}
sourcelike4(FILE.R)
## perhaps you wish to run several scripts in the same function
fun <- function (paths, ...)
{
for (pathname in paths) {
pathname <- set.sys.path(pathname, path.only = TRUE)
sourcelike2(pathname = pathname, ...)
unset.sys.path(pathname)
}
}
## here we have a source-like function which modularizes its code
sourcelike5 <- function (file)
{
ofile <- file
file <- set.sys.path(file, Function = "sourcelike5")
lines <- readLines(file, warn = FALSE)
filename <- sys.path(local = TRUE, for.msg = TRUE)
isFile <- !is.na(filename)
if (isFile) {
timestamp <- file.mtime(filename)[1]
## in case 'ofile' is a URL pathname / / 'unz' connection
if (is.na(timestamp))
timestamp <- Sys.time()
}
else {
filename <- if (is.character(ofile)) ofile else "<connection>"
timestamp <- Sys.time()
}
srcfile <- srcfilecopy(filename, lines, timestamp, isFile)
set.src.path(srcfile)
envir <- new.env(hash = TRUE, parent = .BaseNamespaceEnv)
envir$.packageName <- filename
oopt <- options(topLevelEnvironment = envir)
on.exit(options(oopt))
set.env.path(envir)
exprs <- parse(text = lines, srcfile = srcfile, keep.source = FALSE)
source.exprs(exprs, evaluated = TRUE, envir = envir)
envir
}
sourcelike5(FILE.R)
## the code can be made much simpler in some cases
sourcelike6 <- function (file)
{
## we expect a character string refering to a file
ofile <- file
filename <- set.sys.path(file, path.only = TRUE, ignore.all = TRUE,
Function = "sourcelike6")
lines <- readLines(filename, warn = FALSE)
timestamp <- file.mtime(filename)[1]
srcfile <- srcfilecopy(filename, lines, timestamp, isFile = TRUE)
set.src.path(srcfile)
envir <- new.env(hash = TRUE, parent = .BaseNamespaceEnv)
envir$.packageName <- filename
oopt <- options(topLevelEnvironment = envir)
on.exit(options(oopt))
set.env.path(envir)
exprs <- parse(text = lines, srcfile = srcfile, keep.source = FALSE)
source.exprs(exprs, evaluated = TRUE, envir = envir)
envir
}
sourcelike6(FILE.R)
unlink(FILE.R)
Get 'FILE' Provided to R by a Shell
Description
Look through the command line arguments, extracting FILE from either of the following: -f FILE or --file=FILE
Usage
shFILE(original = FALSE, for.msg = FALSE, default, else.)
Arguments
original |
|
for.msg |
|
default |
if FILE is not found, this value is returned. |
else. |
missing or a function to apply if FILE is found. See
|
Value
character string, or default
if FILE was not found.
Note
The original and the normalized path are saved; this makes them faster when called subsequent times.
On Windows, the normalized path will use /
as the
file separator.
See Also
Examples
FILE.R <- tempfile(fileext = ".R")
this.path:::.writeCode({
this.path:::.withAutoprint({
shFILE(original = TRUE)
shFILE()
shFILE(default = {
stop("since 'FILE.R' will be found,\n",
"this error will not be thrown")
})
}, spaced = TRUE, verbose = FALSE, width.cutoff = 60L)
}, FILE.R)
this.path:::.Rscript(
c("--default-packages=this.path", "--vanilla", FILE.R)
)
unlink(FILE.R)
for (expr in c("shFILE(original = TRUE)",
"shFILE(original = TRUE, default = NULL)",
"shFILE()",
"shFILE(default = NULL)"))
{
cat("\n\n")
this.path:::.Rscript(
c("--default-packages=this.path", "--vanilla", "-e", expr)
)
}
Evaluate and Print Expressions
Description
source.exprs()
evaluates and auto-prints expressions as if in a
toplevel context.
Usage
source.exprs(exprs, evaluated = FALSE, envir = parent.frame(),
echo = TRUE, print.eval = TRUE)
Arguments
exprs , evaluated , envir , echo , print.eval |
See
|
Files Related to Initialization of the R Session
Description
site.file()
and init.file()
return the
normalized
paths
of the site-wide startup profile file and the user profile that were run at
startup
.
with_init.file()
declares that the current script is the user profile
then evaluates and auto-prints the sub-expressions of its argument.
Usage
site.file(original = FALSE, for.msg = FALSE, default, else.)
init.file(original = FALSE, for.msg = FALSE, default, else.)
with_site.file(expr)
with_init.file(expr)
Arguments
original , for.msg , default , else. |
Same as |
expr |
a braced expression, the sub-expressions of which to evaluate and auto-print. |
Value
for site.file()
and init.file()
, a character string, or
default
if it was not found.
for with_site.file()
and with_init.file()
, NULL
invisibly.
Note
with_site.file()
is unneeded now that the site-wide startup profile
file can be automatically detected.
Examples
## if you wish to use this.path() in a user profile,
## instead of writing:
##
## <expr 1>
## <expr 2>
## <...>
##
## write this:
##
## this.path::with_init.file({
## <expr 1>
## <expr 2>
## <...>
## })
Call Stack Inspection
Description
sys.srcref()
is the R-level version of the function that implements
this.path()
. It retrieves the
srcref
of the call n
generations back from
itself.
sys.whiches()
is the R-level version of the function that implements
sys.srcref()
. It returns the frame numbers of all calls with a common
parent frame n
generations back from itself.
Usage
sys.srcref(n = 1, which = if (n) sys.parent(n) else 0)
sys.whiches(n = 1, which = if (n) sys.parent(n) else 0)
Arguments
n |
See |
which |
frame number to inspect. An alternative to specifying |
Value
for sys.srcref()
, a srcref
object or NULL
.
for sys.whiches()
, an integer vector.
Examples
## this example will not work with 'Run examples'
## which uses 'package:knitr' since knitted
## documents do not store source references
fun <- function ()
{
list(
`sys.srcref()` = this.path::sys.srcref(),
## while this might seem like a simpler alternative,
## you will see it does not work in a couple cases below
`attr(sys.call(sys.parent()), "srcref")` =
attr(sys.call(sys.parent()), "srcref")
)
}
## the braces are unnecessary when using example("sys.srcref"),
## but are needed when copied into the R Console
{ fun() }
{ print(fun()) }
{ try(print(fun())) }
fun2 <- function ()
{
list(
## if copied directly into the R Console,
## should be 1 6 7
## 1 is the frame number associated with 'try'
## 6 'force'
## 7 'fun2'
this.path::sys.whiches(),
## if copied directly into the R Console,
## should be 8 12 13
## 8 is the frame number associated with 'tryCatch'
## 12 'identity'
## 13 'sys.whiches'
tryCatch(identity(this.path::sys.whiches(0)),
error = function(e) NULL)
)
}
try(force(fun2()))
Variants of Core Functions in Package 'this.path'
Description
this.path()
is composed of three sections:
examining argument
srcfile
.examining arguments
envir
andmatchThisEnv
.examining the call stack and the GUI in use.
src.path()
, env.path()
, and sys.path()
can be used to
perform any of those sections individually.
The other functions listed here are similar such variants of the core
functions this.dir()
, here()
,
this.proj()
, rel2here()
,
rel2proj()
, LINENO()
, and
try.this.path()
.
Usage
src.path(verbose = getOption("verbose"), original = FALSE,
for.msg = FALSE, contents = FALSE, n = 0,
srcfile = if (n) sys.parent(n) else 0,
default, else.)
src.dir(verbose = getOption("verbose"), n = 0,
srcfile = if (n) sys.parent(n) else 0,
default, else.)
src.here(..., n = 0,
srcfile = if (n) sys.parent(n) else 0, .. = 0)
src.proj(..., n = 0,
srcfile = if (n) sys.parent(n) else 0)
rel2src.dir(path, n = 0,
srcfile = if (n) sys.parent(n) else 0)
rel2src.proj(path, n = 0,
srcfile = if (n) sys.parent(n) else 0)
src.LINENO(n = 0, srcfile = if (n) sys.parent(n) else 0)
try.src.path(contents = FALSE, n = 0,
srcfile = if (n) sys.parent(n) else 0)
env.path(verbose = getOption("verbose"), original = FALSE,
for.msg = FALSE, contents = FALSE, n = 0,
envir = parent.frame(n + 1),
matchThisEnv = getOption("topLevelEnvironment"),
default, else.)
env.dir(verbose = getOption("verbose"), n = 0,
envir = parent.frame(n + 1),
matchThisEnv = getOption("topLevelEnvironment"),
default, else.)
env.here(..., n = 0, envir = parent.frame(n + 1),
matchThisEnv = getOption("topLevelEnvironment"), .. = 0)
env.proj(..., n = 0, envir = parent.frame(n + 1),
matchThisEnv = getOption("topLevelEnvironment"))
rel2env.dir(path, n = 0, envir = parent.frame(n + 1),
matchThisEnv = getOption("topLevelEnvironment"))
rel2env.proj(path, n = 0, envir = parent.frame(n + 1),
matchThisEnv = getOption("topLevelEnvironment"))
env.LINENO(n = 0, envir = parent.frame(n + 1),
matchThisEnv = getOption("topLevelEnvironment"))
try.env.path(contents = FALSE, n = 0,
envir = parent.frame(n + 1),
matchThisEnv = getOption("topLevelEnvironment"))
sys.path(verbose = getOption("verbose"), original = FALSE,
for.msg = FALSE, contents = FALSE, local = FALSE,
default, else.)
sys.dir(verbose = getOption("verbose"), local = FALSE,
default, else.)
sys.here(..., local = FALSE, .. = 0)
sys.proj(..., local = FALSE)
rel2sys.dir(path, local = FALSE)
rel2sys.proj(path, local = FALSE)
sys.LINENO()
try.sys.path(contents = FALSE, local = FALSE)
Details
Before this.path 2.0.0, this.path()
did not have arguments
srcfile
, envir
, nor matchThisEnv
. It only examined the
call stack and the GUI in use. In the major update, the original
implementation of this.path()
was renamed to sys.path()
.
sys.path()
and related are available for backwards compatibility.
However, they are unlikely to be useful compared to the advancements made in
this.path()
.
env.path()
and src.path()
and related are unlikely to be useful
when called with no arguments.
Examples
## the important difference between 'this.path()' and 'sys.path()'
FILE1.R <- tempfile("FILE1-", fileext = ".R")
this.path:::.writeCode({
fun <- function() {
cat("\n> this.path()\n")
print(this.path::this.path(verbose = TRUE))
cat("\n> sys.path()\n")
print(this.path::sys.path(verbose = TRUE))
}
## 'this.path()' and 'sys.path()' should be identical because the
## executing script is the same as the script of the source file
fun()
}, FILE1.R)
source(FILE1.R, keep.source = TRUE)
FILE2.R <- tempfile("FILE2-", fileext = ".R")
this.path:::.writeCode({
## 'this.path()' and 'sys.path()' should no longer be identical
## since FILE2.R is now the executing script, and FILE1.R is not
fun()
}, FILE2.R)
source(FILE2.R)
unlink(c(FILE1.R, FILE2.R))
Determine Script's Filename
Description
this.path()
returns the
normalized
path of
the script in which it was written.
this.dir()
returns the directory of this.path()
.
Usage
this.path(verbose = getOption("verbose"), original = FALSE,
for.msg = FALSE, contents = FALSE, local = FALSE,
n = 0, envir = parent.frame(n + 1),
matchThisEnv = getOption("topLevelEnvironment"),
srcfile = if (n) sys.parent(n) else 0,
default, else.)
this.dir(verbose = getOption("verbose"), local = FALSE,
n = 0, envir = parent.frame(n + 1),
matchThisEnv = getOption("topLevelEnvironment"),
srcfile = if (n) sys.parent(n) else 0,
default, else.)
Arguments
verbose |
|
original |
|
for.msg |
|
contents |
In ‘Jupyter’, a list of character vectors will be returned, the
contents separated into cells. Otherwise, a character vector will be
returned. If the executing script cannot be determined and You could use
This is intended for logging purposes. This is useful in ‘Rgui’, ‘RStudio’, ‘VSCode’, and ‘Emacs’ when the source document has contents but no path. |
local |
|
n |
the number of additional generations to go back. By default,
|
envir , matchThisEnv |
arguments passed to
|
srcfile |
source file in which to search for a pathname, or an object containing a source file. This includes a source reference, a call, an expression object, or a closure. |
default |
this value is returned if there is no executing script. |
else. |
function to apply if there is an executing script. See
|
Details
this.path()
starts by examining argument srcfile
. It looks at
the bindings filename
and wd
to determine the associated file
path. A source file of class "srcfilecopy"
in which binding
isFile
is FALSE
will be ignored. A source file of class
"srcfilealias"
will use the aliased filename
in determining the
associated path. Filenames such as ""
, "clipboard"
, and
"stdin"
will be ignored since they do not refer to files.
If it does not find a path associated with srcfile
, it will next
examine arguments envir
and matchThisEnv
. Specifically, it
calculates topenv(envir, matchThisEnv)
then
looks for an associated path. It will find a path associated with the top
level environment in two ways:
from a package:box module's namespace
from an attribute
"path"
If it does not find an associated path with envir
and
matchThisEnv
, it will next examine the call stack looking for a source
call; a call to one of these functions:
source()
debugSource
()
in ‘RStudio’box::use()
targets::tar_callr_inner_try()
targets::tar_load_globals()
targets::tar_source()
targets::tar_workspace()
If a source call is
found, the file argument is returned from the function's evaluation
environment. If you have your own source()
-like function that you would
like to be recognized by this.path()
, please use
set.sys.path()
or contact the package maintainer so that it can
be implemented.
If no source call is found up the calling stack, it will next examine the GUI in use. If R is running from:
- a shell, such as the Windows command-line / / Unix terminal
-
then the shell arguments are searched for -f FILE or --file=FILE (the two methods of taking input from FILE) (-f - and --file=- are ignored). The last FILE is extracted and returned. If no arguments of either type are supplied, an error is thrown.
If R is running from a shell under a Unix-alike with -g Tk or --gui=Tk, an error is thrown. ‘Tk’ does not make use of its -f FILE, --file=FILE arguments.
- ‘Rgui’
-
then the source document's filename (the document most recently interacted with) is returned (at the time of evaluation). Please note that minimized documents WILL be included when looking for the most recently used document. It is important to not leave the current document (either by closing the document or interacting with another document) while any calls to
this.path()
have yet to be evaluated in the run selection. If no documents are open or the source document does not exist (not saved anywhere), an error is thrown. - ‘RStudio’
-
then the active document's filename (the document in which the cursor is active) is returned (at the time of evaluation). If the active document is the R console, the source document's filename (the document open in the current tab) is returned (at the time of evaluation). Please note that the source document will NEVER be a document open in another window (with the Show in new window button). Please also note that an active document open in another window can sometimes lose focus and become inactive, thus returning the incorrect path. It is best NOT to not run R code from a document open in another window. It is important to not leave the current tab (either by closing or switching tabs) while any calls to
this.path()
have yet to be evaluated in the run selection. If no documents are open or the source document does not exist (not saved anywhere), an error is thrown. - ‘Positron’
-
then the source document's filename is returned (at the time of evaluation). It is important to not leave the current tab (either by closing or switching tabs) while any calls to
this.path()
have yet to be evaluated in the run selection. If no documents are open or the source document does not exist (not saved anywhere), an error is thrown. - ‘VSCode’ + ‘REditorSupport’
-
then the source document's filename is returned (at the time of evaluation). It is important to not leave the current tab (either by closing or switching tabs) while any calls to
this.path()
have yet to be evaluated in the run selection. If no documents are open or the source document does not exist (not saved anywhere), an error is thrown. - ‘Jupyter’
-
then the source document's filename is guessed by looking for R notebooks in the initial working directory, then searching the contents of those files for an expression matching the top-level expression. Please be sure to save your notebook before using
this.path()
, or explicitly useset.jupyter.path()
. - ‘Emacs’ + ‘ESS’
-
then the source document's filename is returned (at the time of evaluation). ‘Emacs’ must be running as a server, either by running
(server-start)
(consider adding to your ‘~/.emacs’ file) or typingM-x server-start
. It is important to not leave the current window (either by closing or switching buffers) while any calls tothis.path()
have yet to be evaluated in the run selection. If multiple frames are active,this.path()
will pick the first frame containing the corresponding R session.If multiple ‘Emacs’ sessions are active,
this.path()
will only work in the primary session due to limitations in‘emacsclient’. If you want to run multiple R sessions, it is better to run one ‘Emacs’ session with multiple frames, one R session per frame. Use
M-x make-frame
to make a new frame, orC-x 5 f
to visit a file in a new frame.Additionally, never use
C-c C-b
to send the current buffer to the R process. This copies the buffer contents to a new file which is thensource()
-ed. The source references now point to the wrong file. Instead, useC-x h
to select the entire buffer thenC-c C-r
to evaluate the selection. - ‘AQUA’
-
then the executing script's path cannot be determined. Until such a time that there is a method for requesting the path of an open document, consider using ‘RStudio’, ‘Positron’, ‘VSCode’, or ‘Emacs’.
If R is running in another manner, an error is thrown.
If your GUI of choice is not implemented with this.path()
, please
contact the package maintainer so that it can be implemented.
Value
default
if there is no executing script.
If contents
is TRUE
, there are a variety of return values. If a
custom GUI is implemented with set.gui.path()
, any R object.
If the executing script cannot be determined and for.msg
is
TRUE
, then NULL
. In Jupyter, a list of character vectors, the
contents separated into cells. Otherwise, a character vector.
Otherwise, a character string.
Argument 'n'
By default, this.path()
will look for a path based on the
srcref
of the call to this.path()
and the environment in which
this.path()
was called. For example:
{ #line 1 "file1.R" fun <- function() this.path::this.path(original = TRUE) fun() } { #line 1 "file2.R" fun() }
Both of these will return "file1.R"
because that is where the call to
this.path()
is written.
But suppose we do not care to know where this.path()
is called, but
instead want to know where fun()
is called. Pass argument n = 1
;
this.path()
will inspect the call and the calling environment one
generation up the stack:
{ #line 1 "file1.R" fun <- function() this.path::this.path(original = TRUE, n = 1) fun() } { #line 1 "file2.R" fun() }
These will return "file1.R"
and "file2.R"
, respectively,
because those are where the calls to fun()
are written.
But now suppose we wish to make a second function that uses fun()
. We
do not care to know where fun()
is called, but instead want to know
where fun2()
is called. Add a formal argument n = 0
to each
function and pass n = n + 1
to each sub-function:
{ #line 1 "file1.R" fun <- function(n = 0) { this.path::this.path(original = TRUE, n = n + 1) } fun() } { #line 1 "file2.R" fun2 <- function(n = 0) fun(n = n + 1) list(fun = fun(), fun2 = fun2()) } { #line 1 "file3.R" fun3 <- function(n = 0) fun2(n = n + 1) list(fun = fun(), fun2 = fun2(), fun3 = fun3()) }
Within each file, all these functions will return the path in which they are
called, regardless of how deep this.path()
is called.
Note
If you need to use this.path()
inside a user profile, please use
with_init.file()
. i.e. instead of writing:
<expr 1> <expr 2> <...>
write this:
this.path::with_init.file({ <expr 1> <expr 2> <...> })
See Also
shFILE()
Examples
FILE1.R <- tempfile(fileext = ".R")
writeLines("writeLines(sQuote(this.path::this.path()))", FILE1.R)
## 'this.path()' works with 'source()'
source(FILE1.R)
## 'this.path()' works with 'sys.source()'
sys.source(FILE1.R, envir = environment())
## 'this.path()' works with 'debugSource()' in 'RStudio'
if (.Platform$GUI == "RStudio")
get("debugSource", "tools:rstudio", inherits = FALSE)(FILE1.R)
## 'this.path()' works with 'testthat::source_file()'
if (requireNamespace("testthat"))
testthat::source_file(FILE1.R, chdir = FALSE, wrap = FALSE)
## 'this.path()' works with 'compiler::loadcmp()'
if (requireNamespace("compiler")) {
FILE2.Rc <- tempfile(fileext = ".Rc")
compiler::cmpfile(FILE1.R, FILE2.Rc)
compiler::loadcmp(FILE2.Rc)
unlink(FILE2.Rc)
}
## 'this.path()' works with 'Rscript'
this.path:::.Rscript(c("--default-packages=NULL", "--vanilla", FILE1.R))
## 'this.path()' also works when 'source()'-ing a URL
## (included tryCatch in case an internet connection is not available)
tryCatch({
source(paste0("https://raw.githubusercontent.com/ArcadeAntics/",
"this.path/main/tests/sys-path-with-urls.R"))
}, condition = this.path:::.cat_condition)
unlink(FILE1.R)
Defunct Functions in Package 'this.path'
Description
The functions or variables listed here are no longer part of this.path as they are no longer needed.
Usage
# Defunct in this.path 1.1.0 (2022-12-01)
this.path2(...)
this.dir2(...)
this.dir3(...)
normalized.shFILE(default, else.)
# Defunct in this.path 1.3.0 (2023-04-09)
as.rel.path(path)
as.relative.path(path)
# Defunct in this.path 1.4.0 (2023-04-18)
local.path(verbose = getOption("verbose"), original = FALSE,
for.msg = FALSE, contents = FALSE, default, else.)
# Defunct in this.path 2.0.0 (2023-08-09)
reset.this.proj()
set.this.path.jupyter(...)
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, ofile)
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, ofile)
unset.this.path()
# Defunct in this.path 2.4.0 (2024-02-16)
set.sys.path.jupyter(...)
fileArgs()
Details
this.path2()
, this.dir2()
, and this.dir3()
were used
before this.path()
and this.dir()
were given an
argument default
. Should be replaced by
this.path(default = NULL)
,
this.dir(default = NULL)
, and
this.dir(default = getwd())
,
respectively.
normalized.shFILE()
was used before shFILE()
was given
an argument original
. Should be replaced by shFILE()
.
as.rel.path()
and as.relative.path()
were poorly chosen names
given their functionality. Should be replaced by rel2here()
.
local.path()
was used when it called a different C function than
this.path()
. Should be replaced by
this.path(local = TRUE)
.
reset.this.proj()
was used when this.proj()
was the only
project related function. Now that src.proj()
,
env.proj()
, and sys.proj()
exist, the function
was renamed to reset.proj()
.
In this.path 2.0.0, the original implementation of
this.path()
was renamed to sys.path()
, so
set.this.path.jupyter()
, set.this.path()
, and
unset.this.path()
were accordingly renamed to
set.jupyter.path()
, set.sys.path()
, and
unset.sys.path()
. Since set.this.path()
was already
being made defunct, inside.source()
was also made defunct.
set.sys.path.jupyter()
was renamed to
set.jupyter.path()
.
fileArgs()
should be replaced by progArgs()
.
See Also
Attempt to Determine Script's Filename
Description
try.this.path()
attempts to return this.path()
,
returning this.path(original = TRUE)
if that fails, returning
NA_character_
if that fails as well.
Usage
try.this.path(contents = FALSE, local = FALSE, n = 0,
envir = parent.frame(n + 1),
matchThisEnv = getOption("topLevelEnvironment"),
srcfile = if (n) sys.parent(n) else 0)
try.shFILE()
Arguments
contents , local , n , envir , matchThisEnv , srcfile |
See
|
Details
This should NOT be used to construct file paths against the script's directory. This should exclusively be used for diagnostic messages / / warnings / / errors / / logging. The returned path may not exist, may be relative instead of absolute, or may be undefined.
Value
character string.
Examples
try.shFILE()
try.this.path()
try.this.path(contents = TRUE)
Condition Handling and Recovery
Description
Variants of tryCatch()
that accept an
else.
argument, similar to try except
in ‘Python’.
last.condition
will be the last thrown and caught condition in
tryCatch3()
.
Usage
tryCatch2(expr, ..., else., finally)
tryCatch3(expr, ..., else., finally)
last.condition
Arguments
expr |
expression to be evaluated. |
... |
for Arguments which are missing will use the next non-missing argument. If
there is no next non-missing argument, |
else. |
expression to be evaluated if evaluating |
finally |
expression to be evaluated before returning or exiting. |
Details
The use of the else.
argument is better than adding additional code to
expr
because it avoids accidentally catching a condition that was not
being protected by the tryCatch()
call.
Examples
FILES <- tempfile(c("existent-file_", "non-existent-file_"))
writeLines("line1\nline2", FILES[[1L]])
for (FILE in FILES) {
conn <- file(FILE)
tryCatch2({
open(conn, "r")
}, condition = function(cond) {
cat("cannot open", FILE, "\n")
}, else. = {
cat(FILE, "has", length(readLines(conn)), "lines\n")
}, finally = {
close(conn)
})
# ## or more naturely with tryCatch3:
# tryCatch3({
# open(conn, "r")
# }, condition = {
# cat("cannot open", FILE, "\n")
# }, else. = {
# cat(FILE, "has", length(readLines(conn)), "lines\n")
# }, finally = {
# close(conn)
# })
}
unlink(FILES)