Title: | Supporting Functions for Packages Maintained by 'Yihui Xie' |
---|---|
Description: | Miscellaneous functions commonly used in other packages maintained by 'Yihui Xie'. |
Authors: | Yihui Xie [aut, cre, cph] , Wush Wu [ctb], Daijiang Li [ctb], Xianying Tan [ctb], Salim Brüggemann [ctb] , Christophe Dervieux [ctb] |
Maintainer: | Yihui Xie <[email protected]> |
License: | MIT + file LICENSE |
Version: | 0.49.12 |
Built: | 2024-12-24 06:28:03 UTC |
Source: | https://github.com/yihui/xfun |
Substitute certain (by default, non-alphanumeric) characters with dashes and remove extra dashes at both ends to generate ID strings. This function is intended for generating IDs for HTML elements, so HTML tags in the input text will be removed first.
alnum_id(x, exclude = "[^[:alnum:]]+")
alnum_id(x, exclude = "[^[:alnum:]]+")
x |
A character vector. |
exclude |
A (Perl) regular expression to detect characters to be replaced by dashes. By default, non-alphanumeric characters are replaced. |
A character vector of IDs.
x = c("Hello world 123!", "a &b*^##c 456") xfun::alnum_id(x) xfun::alnum_id(x, "[^[:alpha:]]+") # only keep alphabetical chars # when text contains HTML tags xfun::alnum_id("<h1>Hello <strong>world</strong>!")
x = c("Hello world 123!", "a &b*^##c 456") xfun::alnum_id(x) xfun::alnum_id(x, "[^[:alpha:]]+") # only keep alphabetical chars # when text contains HTML tags xfun::alnum_id("<h1>Hello <strong>world</strong>!")
An abbreviation of base::attr
(exact = TRUE)
.
attr(...)
attr(...)
... |
Passed to |
z = structure(list(a = 1), foo = 2) base::attr(z, "f") # 2 xfun::attr(z, "f") # NULL xfun::attr(z, "foo") # 2
z = structure(list(a = 1), foo = 2) base::attr(z, "f") # 2 xfun::attr(z, "f") # NULL xfun::attr(z, "foo") # 2
Return base R package names.
base_pkgs()
base_pkgs()
A character vector of base R package names.
xfun::base_pkgs()
xfun::base_pkgs()
The function base64_encode()
encodes a file or a raw vector into the
base64 encoding. The function base64_decode()
decodes data from the
base64 encoding.
base64_encode(x) base64_decode(x, from = NA)
base64_encode(x) base64_decode(x, from = NA)
x |
For |
from |
If provided (and |
base64_encode()
returns a character string.
base64_decode()
returns a raw vector.
xfun::base64_encode(as.raw(1:10)) logo = xfun:::R_logo() xfun::base64_encode(logo) xfun::base64_decode("AQIDBAUGBwgJCg==")
xfun::base64_encode(as.raw(1:10)) logo = xfun:::R_logo() xfun::base64_encode(logo) xfun::base64_decode("AQIDBAUGBwgJCg==")
Encode the file in the base64 encoding, and add the media type. The data URI
can be used to embed data in HTML documents, e.g., in the src
attribute of
the <img />
tag.
base64_uri(x, type = mime_type(x))
base64_uri(x, type = mime_type(x))
x |
A file path. |
type |
The MIME type of the file, e.g., |
A string of the form data:<media type>;base64,<data>
.
logo = xfun:::R_logo() img = xfun::html_tag("img", src = xfun::base64_uri(logo), alt = "R logo") if (interactive()) xfun::html_view(img)
logo = xfun:::R_logo() img = xfun::html_tag("img", src = xfun::base64_uri(logo), alt = "R logo") if (interactive()) xfun::html_view(img)
Start a background process using the PowerShell cmdlet
Start-Process-PassThru
on Windows or the ampersand &
on
Unix, and return the process ID.
bg_process( command, args = character(), verbose = getOption("xfun.bg_process.verbose", FALSE) )
bg_process( command, args = character(), verbose = getOption("xfun.bg_process.verbose", FALSE) )
command , args
|
The system command and its arguments. They do not need to
be quoted, since they will be quoted via |
verbose |
If |
The process ID as a character string.
On Windows, if PowerShell is not available, try to use
system2
(wait = FALSE)
to start the background process instead. The
process ID will be identified from the output of the command
tasklist
. This method of looking for the process ID may not be
reliable. If the search is not successful in 30 seconds, it will throw an
error (timeout). If a longer time is needed, you may set
options(xfun.bg_process.timeout)
to a larger value, but it should be very
rare that a process cannot be started in 30 seconds. When you reach the
timeout, it is more likely that the command actually failed.
proc_kill()
to kill a process.
If a package is broken (i.e., not loadable()
), reinstall it.
broken_packages(reinstall = TRUE)
broken_packages(reinstall = TRUE)
reinstall |
Whether to reinstall the broken packages, or only list their names. |
Installed R packages could be broken for several reasons. One common reason
is that you have upgraded R to a newer x.y
version, e.g., from 4.0.5
to
4.1.0
, in which case you need to reinstall previously installed packages.
A character vector of names of broken package.
Increase the last digit of version numbers, e.g., from 0.1
to
0.2
, or 7.23.9
to 7.23.10
.
bump_version(x)
bump_version(x)
x |
A vector of version numbers (of the class |
A vector of new version numbers.
xfun::bump_version(c("0.1", "91.2.14"))
xfun::bump_version(c("0.1", "91.2.14"))
Caching is based on the assumption that if the input does not change, the output will not change. After an expression is executed for the first time, its result will be saved (either in memory or on disk). The next run will be skipped and the previously saved result will be loaded directly if all external inputs of the expression remain the same, otherwise the cache will be invalidated and the expression will be re-executed.
cache_exec(expr, path = "cache/", id = NULL, ...)
cache_exec(expr, path = "cache/", id = NULL, ...)
expr |
An R expression to be cached. |
path |
The path to save the cache. The special value |
id |
A stable and unique string identifier for the expression to be used
to identify a unique copy of cache for the current expression from all
cache files (or in-memory elements). If not provided, an MD5 digest of the
deparsed expression will be used, which means if the expression does not
change (changes in comments or white spaces do not matter), the |
... |
More arguments to control the behavior of caching (see ‘Details’). |
Arguments supported in ...
include:
vars
: Names of local variables (which are created inside the expression).
By default, local variables are automatically detected from the expression
via find_locals()
. Locally created variables are cached along with the
value of the expression.
hash
and extra
: R objects to be used to determine if cache should be
loaded or invalidated. If (the MD5 hash of) the objects is not changed, the
cache is loaded, otherwise the cache is invalidated and rebuilt. By default,
hash
is a list of values of global variables in the expression (i.e.,
variables created outside the expression). Global variables are automatically
detected by find_globals()
. You can provide a vector of names to override
the automatic detection if you want some specific global variables to affect
caching, or the automatic detection is not reliable. You can also provide
additional information via the extra
argument. For example, if the
expression reads an external file foo.csv
, and you want the cache to be
invalidated after the file is modified, you may use extra = file.mtime("foo.csv")
.
keep
: By default, only one copy of the cache corresponding to an id
under path
is kept, and all other copies for this id
is automatically
purged. If TRUE
, all copies of the cache are kept. If FALSE
, all copies
are removed, which means the cache is always invalidated, and can be useful
to force re-executing the expression.
rw
: A list of functions to read/write the cache files. The list is of the
form list(name = 'xxx', load = function(file) {}, save = function(x, file) {})
. By default, readRDS()
and saveRDS()
are used. This argument can
also take a character string to use some built-in read/write methods.
Currently available methods include rds
(the default), raw
(using
serialize()
and unserialize()
), and qs
(using qs::qread()
and
qs::qsave()
). The rds
and raw
methods only use base R functions (the
rds
method generates smaller files because it uses compression, but is
often slower than the raw
method, which does not use compression). The qs
method requires the qs package, which can be much faster than base R
methods and also supports compression.
If the cache is found, the cached value of the expression will be loaded and returned (other local variables will also be lazy-loaded into the current environment as a side-effect). If cache does not exist, the expression is executed and its value is returned.
# the first run takes about 1 second y1 = xfun::cache_exec({ x = rnorm(1e+05) Sys.sleep(1) x }, path = ":memory:", id = "sim-norm") # the second run takes almost no time y2 = xfun::cache_exec({ # comments won't affect caching x = rnorm(1e+05) Sys.sleep(1) x }, path = ":memory:", id = "sim-norm") # y1, y2, and x should be identical stopifnot(identical(y1, y2), identical(y1, x))
# the first run takes about 1 second y1 = xfun::cache_exec({ x = rnorm(1e+05) Sys.sleep(1) x }, path = ":memory:", id = "sim-norm") # the second run takes almost no time y2 = xfun::cache_exec({ # comments won't affect caching x = rnorm(1e+05) Sys.sleep(1) x }, path = ":memory:", id = "sim-norm") # y1, y2, and x should be identical stopifnot(identical(y1, y2), identical(y1, x))
Save the value of an expression to a cache file (of the RDS format). Next time the value is loaded from the file if it exists.
cache_rds( expr = { }, rerun = FALSE, file = "cache.rds", dir = "cache/", hash = NULL, clean = getOption("xfun.cache_rds.clean", TRUE), ... )
cache_rds( expr = { }, rerun = FALSE, file = "cache.rds", dir = "cache/", hash = NULL, clean = getOption("xfun.cache_rds.clean", TRUE), ... )
expr |
An R expression. |
rerun |
Whether to delete the RDS file, rerun the expression, and save the result again (i.e., invalidate the cache if it exists). |
file |
The base (see Details) cache filename under the directory
specified by the |
dir |
The path of the RDS file is partially determined by |
hash |
A |
clean |
Whether to clean up the old cache files automatically when
|
... |
Other arguments to be passed to |
Note that the file
argument does not provide the full cache filename. The
actual name of the cache file is of the form ‘BASENAME_HASH.rds’, where
‘BASENAME’ is the base name provided via the ‘file’ argument (e.g.,
if file = 'foo.rds'
, BASENAME
would be ‘foo’), and ‘HASH’ is
the MD5 hash (also called the ‘checksum’) calculated from the R code
provided to the expr
argument and the value of the hash
argument, which
means when the code or the hash
argument changes, the ‘HASH’ string
may also change, and the old cache will be invalidated (if it exists). If you
want to find the cache file, look for ‘.rds’ files that contain 32
hexadecimal digits (consisting of 0-9 and a-z) at the end of the filename.
The possible ways to invalidate the cache are: 1) change the code in expr
argument; 2) delete the cache file manually or automatically through the
argument rerun = TRUE
; and 3) change the value of the hash
argument. The
first two ways should be obvious. For the third way, it makes it possible to
automatically invalidate the cache based on changes in certain R objects. For
example, when you run cache_rds({ x + y })
, you may want to invalidate the
cache to rerun { x + y }
when the value of x
or y
has been changed, and
you can tell cache_rds()
to do so by cache_rds({ x + y }, hash = list(x, y))
. The value of the argument hash
is expected to be a list, but it can
also take a special value, "auto"
, which means cache_rds(expr)
will try
to automatically figure out the global variables in expr
, return a list of
their values, and use this list as the actual value of hash
. This behavior
is most likely to be what you really want: if the code in expr
uses an
external global variable, you may want to invalidate the cache if the value
of the global variable has changed. Here a “global variable” means a
variable not created locally in expr
, e.g., for cache_rds({ x <- 1; x + y })
, x
is a local variable, and y
is (most likely to be) a global
variable, so changes in y
should invalidate the cache. However, you know
your own code the best. If you want to be completely sure when to invalidate
the cache, you can always provide a list of objects explicitly rather than
relying on hash = "auto"
.
By default (the argument clean = TRUE
), old cache files will be
automatically cleaned up. Sometimes you may want to use clean = FALSE
(set
the R global option options(xfun.cache_rds.clean = FALSE)
if you want
FALSE
to be the default). For example, you may not have decided which
version of code to use, and you can keep the cache of both versions with
clean = FALSE
, so when you switch between the two versions of code, it will
still be fast to run the code.
If the cache file does not exist, run the expression and save the result to the file, otherwise read the cache file and return the value.
Changes in the code in the expr
argument do not necessarily always
invalidate the cache, if the changed code is parse
d
to the same
expression as the previous version of the code. For example, if you have
run cache_rds({Sys.sleep(5);1+1})
before, running cache_rds({ Sys.sleep( 5 ) ; 1 + 1 })
will use the cache, because the two expressions are
essentially the same (they only differ in white spaces). Usually you can
add/delete white spaces or comments to your code in expr
without
invalidating the cache. See the package vignette vignette('xfun', package = 'xfun')
for more examples.
When this function is called in a code chunk of a knitr document, you may not want to provide the filename or directory of the cache file, because they have reasonable defaults.
Side-effects (such as plots or printed output) will not be cached. The
cache only stores the last value of the expression in expr
.
cache_exec()
, which is more flexible (e.g., it supports in-memory
caching and different read/write methods for cache files).
f = tempfile() # the cache file compute = function(...) { res = xfun::cache_rds({ Sys.sleep(1) 1:10 }, file = f, dir = "", ...) res } compute() # takes one second compute() # returns 1:10 immediately compute() # fast again compute(rerun = TRUE) # one second to rerun compute() unlink(paste0(f, "_*.rds"))
f = tempfile() # the cache file compute = function(...) { res = xfun::cache_rds({ Sys.sleep(1) 1:10 }, file = f, dir = "", ...) res } compute() # takes one second compute() # returns 1:10 immediately compute() # fast again compute(rerun = TRUE) # one second to rerun compute() unlink(paste0(f, "_*.rds"))
Check the reverse dependencies of a package using the crandalf service: https://github.com/yihui/crandalf. If the number of reverse dependencies is large, they will be split into batches and pushed to crandalf one by one.
crandalf_check(pkg, size = 400, jobs = Inf, which = "all") crandalf_results(pkg, repo = NA, limit = 200, wait = 5 * 60)
crandalf_check(pkg, size = 400, jobs = Inf, which = "all") crandalf_results(pkg, repo = NA, limit = 200, wait = 5 * 60)
pkg |
The package name of which the reverse dependencies are to be checked. |
size |
The number of reverse dependencies to be checked in each job. |
jobs |
The number of jobs to run in GitHub Actions (by default, all jobs are submitted, but you can choose to submit the first few jobs). |
which |
The type of dependencies (see |
repo |
The crandalf repo on GitHub (of the form |
limit |
The maximum of records for |
wait |
Number of seconds to wait if not all jobs have been completed on
GitHub. By default, this function checks the status every 5 minutes until
all jobs are completed. Set |
Due to the time limit of a single job on GitHub Actions (6 hours), you will
have to split the large number of reverse dependencies into batches and check
them sequentially on GitHub (at most 5 jobs in parallel). The function
crandalf_check()
does this automatically when necessary. It requires
the git
command to be available.
The function crandalf_results()
fetches check results from GitHub
after all checks are completed, merge the results, and show a full summary of
check results. It requires gh
(GitHub CLI:
https://cli.github.com/manual/) to be installed and you also need to
authenticate with your GitHub account beforehand.
For knitr and R Markdown documents, code chunk options can be written
using the comma-separated syntax (e.g., opt1=value1, opt2=value2
). This
function parses these options and returns a list. If an option is not named,
it will be treated as the chunk label.
csv_options(x)
csv_options(x)
x |
The chunk options as a string. |
A list of chunk options.
xfun::csv_options("foo, eval=TRUE, fig.width=5, echo=if (TRUE) FALSE")
xfun::csv_options("foo, eval=TRUE, fig.width=5, echo=if (TRUE) FALSE")
Sometimes it is necessary to use the dot character as the decimal separator.
In R, this could be affected by two settings: the global option
options(OutDec)
and the LC_NUMERIC
locale. This function sets the former
to .
and the latter to C
before evaluating an expression, such as
coercing a number to character.
decimal_dot(x)
decimal_dot(x)
x |
An expression. |
The value of x
.
opts = options(OutDec = ",") as.character(1.234) # using ',' as the decimal separator print(1.234) # same xfun::decimal_dot(as.character(1.234)) # using dot xfun::decimal_dot(print(1.234)) # using dot options(opts)
opts = options(OutDec = ",") as.character(1.234) # using ',' as the decimal separator print(1.234) # same xfun::decimal_dot(as.character(1.234)) # using dot xfun::decimal_dot(print(1.234)) # using dot options(opts)
Use list.file()
to check if there are any files or subdirectories
under a directory. If not, delete this empty directory.
del_empty_dir(dir)
del_empty_dir(dir)
dir |
Path to a directory. If |
First check if a directory exists. If it does, return TRUE
, otherwise
create it with dir.create
(recursive = TRUE)
by default.
dir_create(x, recursive = TRUE, ...)
dir_create(x, recursive = TRUE, ...)
x |
A path name. |
recursive |
Whether to create all directory components in the path. |
... |
Other arguments to be passed to |
A logical value indicating if the directory either exists or is successfully created.
These are wrapper functions of [utils::file_test()]
to test the
existence of directories and files. Note that file_exists()
only tests
files but not directories, which is the main difference between
file.exists()
in base R. If you use are using the R version
3.2.0 or above, dir_exists()
is the same as dir.exists()
in base R.
dir_exists(x) file_exists(x)
dir_exists(x) file_exists(x)
x |
A vector of paths. |
A logical vector.
Chunk options can be written in special comments (e.g., after #|
for R code
chunks) inside a code chunk. This function partitions these options from the
chunk body.
divide_chunk(engine, code)
divide_chunk(engine, code)
engine |
The name of the language engine (to determine the appropriate comment character). |
code |
A character vector (lines of code). |
A list with the following items:
options
: The parsed options (if there are any) as a list.
src
: The part of the input that contains the options.
code
: The part of the input that contains the code.
Chunk options must be written on continuous lines (i.e., all lines
must start with the special comment prefix such as #|
) at the beginning
of the chunk body.
# parse yaml-like items yaml_like = c("#| label: mine", "#| echo: true", "#| fig.width: 8", "#| foo: bar", "1 + 1") writeLines(yaml_like) xfun::divide_chunk("r", yaml_like) # parse CSV syntax csv_like = c("#| mine, echo = TRUE, fig.width = 8, foo = 'bar'", "1 + 1") writeLines(csv_like) xfun::divide_chunk("r", csv_like)
# parse yaml-like items yaml_like = c("#| label: mine", "#| echo: true", "#| fig.width: 8", "#| foo: bar", "1 + 1") writeLines(yaml_like) xfun::divide_chunk("r", yaml_like) # parse CSV syntax csv_like = c("#| mine, echo = TRUE, fig.width = 8, foo = 'bar'", "1 + 1") writeLines(csv_like) xfun::divide_chunk("r", csv_like)
Perform a task once in an R session, e.g., emit a message or warning. Then give users an optional hint on how not to perform this task at all.
do_once( task, option, hint = c("You will not see this message again in this R session.", "If you never want to see this message,", sprintf("you may set options(%s = FALSE) in your .Rprofile.", option)) )
do_once( task, option, hint = c("You will not see this message again in this R session.", "If you never want to see this message,", sprintf("you may set options(%s = FALSE) in your .Rprofile.", option)) )
task |
Any R code expression to be evaluated once to perform a task,
e.g., |
option |
An R option name. This name should be as unique as possible in
|
hint |
A character vector to provide a hint to users on how not to
perform the task or see the message again in the current R session. Set
|
The value returned by the task
, invisibly.
do_once(message("Today's date is ", Sys.Date()), "xfun.date.reminder") # if you run it again, it will not emit the message again do_once(message("Today's date is ", Sys.Date()), "xfun.date.reminder") do_once({ Sys.sleep(2) 1 + 1 }, "xfun.task.1plus1") do_once({ Sys.sleep(2) 1 + 1 }, "xfun.task.1plus1")
do_once(message("Today's date is ", Sys.Date()), "xfun.date.reminder") # if you run it again, it will not emit the message again do_once(message("Today's date is ", Sys.Date()), "xfun.date.reminder") do_once({ Sys.sleep(2) 1 + 1 }, "xfun.task.1plus1") do_once({ Sys.sleep(2) 1 + 1 }, "xfun.task.1plus1")
This object provides methods to download files and cache them on disk.
download_cache
download_cache
A list of methods:
$get(url, type, handler)
downloads a URL, caches it, and returns the file
content according to the value of type
(possible values: "text"
means
the text content; "base64"
means the base64 encoded data; "raw"
means
the raw binary content; "auto"
is the default and means the type is
determined by the content type in the URL headers). Optionally a handler
function can be applied to the content.
$list()
gives the list of cache files.
$summary()
gives a summary of existing cache files.
$remove(url, type)
removes a single cache file.
$purge()
deletes all cache files.
# the first time it may take a few seconds x1 = xfun::download_cache$get("https://www.r-project.org/") head(x1) # now you can get the cached content x2 = xfun::download_cache$get("https://www.r-project.org/") identical(x1, x2) # TRUE # a binary file x3 = xfun::download_cache$get("https://yihui.org/images/logo.png", "raw") length(x3) # show a summary xfun::download_cache$summary() # remove a specific cache file xfun::download_cache$remove("https://yihui.org/images/logo.png", "raw") # remove all cache files xfun::download_cache$purge()
# the first time it may take a few seconds x1 = xfun::download_cache$get("https://www.r-project.org/") head(x1) # now you can get the cached content x2 = xfun::download_cache$get("https://www.r-project.org/") identical(x1, x2) # TRUE # a binary file x3 = xfun::download_cache$get("https://yihui.org/images/logo.png", "raw") length(x3) # show a summary xfun::download_cache$summary() # remove a specific cache file xfun::download_cache$remove("https://yihui.org/images/logo.png", "raw") # remove all cache files xfun::download_cache$purge()
Try all possible methods in download.file()
(e.g.,
libcurl
, curl
, wget
, and wininet
) and see if any
method can succeed. The reason to enumerate all methods is that sometimes the
default method does not work, e.g.,
https://stat.ethz.ch/pipermail/r-devel/2016-June/072852.html.
download_file( url, output = url_filename(url), ..., .error = "No download method works (auto/wininet/wget/curl/lynx)" )
download_file( url, output = url_filename(url), ..., .error = "No download method works (auto/wininet/wget/curl/lynx)" )
url |
The URL of the file. |
output |
Path to the output file. By default, it is determined by
|
... |
Other arguments to be passed to |
.error |
An error message to signal when the download fails. |
The integer code 0
for success, or an error if none of the
methods work.
To allow downloading large files, the timeout
option in
options()
will be temporarily set to one hour (3600 seconds)
inside this function when this option has the default value of 60 seconds.
If you want a different timeout
value, you may set it via
options(timeout = N)
, where N
is the number of seconds (not
60).
For a file, first encode it into base64 data (a character string). Then
generate a hyperlink of the form ‘<a href="base64 data"
download="filename">Download filename</a>’. The file can be downloaded when
the link is clicked in modern web browsers. For a directory, it will be
compressed as a zip archive first, and the zip file is passed to
embed_file()
. For multiple files, they are also compressed to a zip file
first.
embed_file(path, name = basename(path), text = paste("Download", name), ...) embed_dir(path, name = paste0(normalize_path(path), ".zip"), ...) embed_files(path, name = with_ext(basename(path[1]), ".zip"), ...)
embed_file(path, name = basename(path), text = paste("Download", name), ...) embed_dir(path, name = paste0(normalize_path(path), ".zip"), ...) embed_files(path, name = with_ext(basename(path[1]), ".zip"), ...)
path |
Path to the file(s) or directory. |
name |
The default filename to use when downloading the file. Note that
for |
text |
The text for the hyperlink. |
... |
For |
These functions can be called in R code chunks in R Markdown documents with HTML output formats. You may embed an arbitrary file or directory in the HTML output file, so that readers of the HTML page can download it from the browser. A common use case is to embed data files for readers to download.
An HTML tag ‘<a>’ with the appropriate attributes.
Windows users may need to install Rtools to obtain the zip
command to use embed_dir()
and embed_files()
.
Internet Explorer does not support downloading embedded files. Chrome has a 2MB limit on the file size.
logo = xfun:::R_logo() link = xfun::embed_file(logo, text = "Download R logo") link if (interactive()) xfun::html_view(link)
logo = xfun:::R_logo() link = xfun::embed_file(logo, text = "Download R logo") link if (interactive()) xfun::html_view(link)
options()
and environment variablesIf the option exists in options()
, use its value. If not, query the
environment variable with the name R_NAME
where NAME
is the capitalized
option name with dots substituted by underscores. For example, for an option
xfun.foo
, first we try getOption('xfun.foo')
; if it does not exist, we
check the environment variable R_XFUN_FOO
.
env_option(name, default = NULL)
env_option(name, default = NULL)
name |
The option name. |
default |
The default value if the option is not found in |
This provides two possible ways, whichever is more convenient, for users to set an option. For example, global options can be set in the .Rprofile file, and environment variables can be set in the .Renviron file.
The option value.
xfun::env_option("xfun.test.option") # NULL Sys.setenv(R_XFUN_TEST_OPTION = "1234") xfun::env_option("xfun.test.option") # 1234 options(xfun.test.option = TRUE) xfun::env_option("xfun.test.option") # TRUE (from options()) options(xfun.test.option = NULL) # reset the option xfun::env_option("xfun.test.option") # 1234 (from env var) Sys.unsetenv("R_XFUN_TEST_OPTION") xfun::env_option("xfun.test.option") # NULL again xfun::env_option("xfun.test.option", FALSE) # use default
xfun::env_option("xfun.test.option") # NULL Sys.setenv(R_XFUN_TEST_OPTION = "1234") xfun::env_option("xfun.test.option") # 1234 options(xfun.test.option = TRUE) xfun::env_option("xfun.test.option") # TRUE (from options()) options(xfun.test.option = NULL) # reset the option xfun::env_option("xfun.test.option") # 1234 (from env var) Sys.unsetenv("R_XFUN_TEST_OPTION") xfun::env_option("xfun.test.option") # NULL again xfun::env_option("xfun.test.option", FALSE) # use default
This is a shorthand of x[file.exists(x)]
, and optionally returns the
first existing file path.
existing_files(x, first = FALSE, error = TRUE)
existing_files(x, first = FALSE, error = TRUE)
x |
A vector of file paths. |
first |
Whether to return the first existing path. If |
error |
Whether to throw an error when |
A vector of existing file paths.
xfun::existing_files(c("foo.txt", system.file("DESCRIPTION", package = "xfun")))
xfun::existing_files(c("foo.txt", system.file("DESCRIPTION", package = "xfun")))
on.exit()
in a parent functionThe function on.exit()
is often used to perform tasks when the
current function exits. This exit_call()
function allows calling a
function when a parent function exits (thinking of it as inserting an
on.exit()
call into the parent function).
exit_call(fun, n = 2, ...)
exit_call(fun, n = 2, ...)
fun |
A function to be called when the parent function exits. |
n |
The parent frame number. For |
... |
Other arguments to be passed to |
This function was inspired by Kevin Ushey: https://yihui.org/en/2017/12/on-exit-parent/
f = function(x) { print(x) xfun::exit_call(function() print("The parent function is exiting!")) } g = function(y) { f(y) print("f() has been called!") } g("An argument of g()!")
f = function(x) { print(x) xfun::exit_call(function() print("The parent function is exiting!")) } g = function(y) { f(y) print("f() has been called!") } g("An argument of g()!")
Wrap content with fence delimiters such as backticks (code blocks) or colons
(fenced Div). Optionally the fenced block can have attributes. The function
fenced_div()
is a shorthand of fenced_block(char = ':')
.
fenced_block(x, attrs = NULL, fence = make_fence(x, char), char = "`") fenced_div(...) make_fence(x, char = "`", start = 3)
fenced_block(x, attrs = NULL, fence = make_fence(x, char), char = "`") fenced_div(...) make_fence(x, char = "`", start = 3)
x |
A character vector of the block content. |
attrs |
A vector of block attributes. |
fence |
The fence string, e.g., |
char |
The fence character to be used to generate the fence string by default. |
... |
Arguments to be passed to |
start |
The number of characters to start searching |
fenced_block()
returns a character vector that contains both the
fences and content.
make_fence()
returns a character string. If the block content
contains N
fence characters (e.g., backticks), use N + 1
characters as
the fence.
# code block with class 'r' and ID 'foo' xfun::fenced_block("1+1", c(".r", "#foo")) # fenced Div xfun::fenced_block("This is a **Div**.", char = ":") # three backticks by default xfun::make_fence("1+1") # needs five backticks for the fences because content has four xfun::make_fence(c("````r", "1+1", "````"))
# code block with class 'r' and ID 'foo' xfun::fenced_block("1+1", c(".r", "#foo")) # fenced Div xfun::fenced_block("This is a **Div**.", char = ":") # three backticks by default xfun::make_fence("1+1") # needs five backticks for the fences because content has four xfun::make_fence(c("````r", "1+1", "````"))
Functions to obtain (file_ext()
), remove (sans_ext()
), and
change (with_ext()
) extensions in filenames.
file_ext(x, extra = "") sans_ext(x, extra = "") with_ext(x, ext, extra = "")
file_ext(x, extra = "") sans_ext(x, extra = "") with_ext(x, ext, extra = "")
x |
A character of file paths. |
extra |
Extra characters to be allowed in the extensions. By default,
only alphanumeric characters are allowed (and also some special cases in
‘Details’). If other characters should be allowed, they can be
specified in a character string, e.g., |
ext |
A vector of new extensions. It must be either of length 1, or the
same length as |
file_ext()
is similar to tools::file_ext()
, and
sans_ext()
is similar to tools::file_path_sans_ext()
.
The main differences are that they treat tar.(gz|bz2|xz)
and
nb.html
as extensions (but functions in the tools package
doesn't allow double extensions by default), and allow characters ~
and #
to be present at the end of a filename.
A character vector of the same length as x
.
library(xfun) p = c("abc.doc", "def123.tex", "path/to/foo.Rmd", "backup.ppt~", "pkg.tar.xz") file_ext(p) sans_ext(p) with_ext(p, ".txt") with_ext(p, c(".ppt", ".sty", ".Rnw", "doc", "zip")) with_ext(p, "html") # allow for more characters in extensions p = c("a.c++", "b.c--", "c.e##") file_ext(p) # -/+/# not recognized by default file_ext(p, extra = "-+#")
library(xfun) p = c("abc.doc", "def123.tex", "path/to/foo.Rmd", "backup.ppt~", "pkg.tar.xz") file_ext(p) sans_ext(p) with_ext(p, ".txt") with_ext(p, c(".ppt", ".sty", ".Rnw", "doc", "zip")) with_ext(p, "html") # allow for more characters in extensions p = c("a.c++", "b.c--", "c.e##") file_ext(p) # -/+/# not recognized by default file_ext(p, extra = "-+#")
First try file.rename()
. If it fails (e.g., renaming a file from one volume
to another on disk is likely to fail), try file.copy()
instead, and clean
up the original files if the copy succeeds.
file_rename(from, to)
file_rename(from, to)
from , to
|
Original and target paths, respectively. |
A logical vector (TRUE
for success and FALSE
for failure).
'\n'
The source code of this function should be self-explanatory.
file_string(file)
file_string(file)
file |
Path to a text file (should be encoded in UTF-8). |
A character string of text lines concatenated by '\n'
.
xfun::file_string(system.file("DESCRIPTION", package = "xfun"))
xfun::file_string(system.file("DESCRIPTION", package = "xfun"))
Use codetools::findGlobals()
and codetools::findLocalsList()
to find
global and local variables in a piece of code. Global variables are defined
outside the code, and local variables are created inside the code.
find_globals(code, envir = parent.frame()) find_locals(code)
find_globals(code, envir = parent.frame()) find_locals(code)
code |
Either a character vector of R source code, or an R expression. |
envir |
The global environment in which global variables are to be found. |
A character vector of the variable names. If the source code contains syntax errors, an empty character vector will be returned.
Due to the flexibility of creating and getting variables in R, these
functions are not guaranteed to find all possible variables in the code
(e.g., when the code is hidden behind eval()
).
x = 2 xfun::find_globals("y = x + 1") xfun::find_globals("y = get('x') + 1") # x is not recognized xfun::find_globals("y = zzz + 1") # zzz doesn't exist xfun::find_locals("y = x + 1") xfun::find_locals("assign('y', x + 1)") # it works xfun::find_locals("assign('y', x + 1, new.env())") # still smart xfun::find_locals("eval(parse(text = 'y = x + 1'))") # no way
x = 2 xfun::find_globals("y = x + 1") xfun::find_globals("y = get('x') + 1") # x is not recognized xfun::find_globals("y = zzz + 1") # zzz doesn't exist xfun::find_locals("y = x + 1") xfun::find_locals("assign('y', x + 1)") # it works xfun::find_locals("assign('y', x + 1, new.env())") # still smart xfun::find_locals("eval(parse(text = 'y = x + 1'))") # no way
Call the S3 method format.object_size()
to format numbers of bytes.
format_bytes(x, units = "auto", ...)
format_bytes(x, units = "auto", ...)
x |
A numeric vector (each element represents a number of bytes). |
units , ...
|
Passed to |
A character vector.
xfun::format_bytes(c(1, 1024, 2000, 1e+06, 2e+08)) xfun::format_bytes(c(1, 1024, 2000, 1e+06, 2e+08), units = "KB")
xfun::format_bytes(c(1, 1024, 2000, 1e+06, 2e+08)) xfun::format_bytes(c(1, 1024, 2000, 1e+06, 2e+08), units = "KB")
First compose an absolute path using the project root directory and the
relative path components, i.e., file.path
(root, ...)
. Then
convert it to a relative path with relative_path()
, which is
relative to the current working directory.
from_root(..., root = proj_root(), error = TRUE)
from_root(..., root = proj_root(), error = TRUE)
... |
A character vector of path components relative to the root directory of the project. |
root |
The root directory of the project. |
error |
Whether to signal an error if the path cannot be converted to a relative path. |
This function was inspired by here::here()
, and the major difference
is that it returns a relative path by default, which is more portable.
A relative path, or an error when the project root directory cannot
be determined or the conversion failed and error = TRUE
.
## Not run: xfun::from_root("data", "mtcars.csv") ## End(Not run)
## Not run: xfun::from_root("data", "mtcars.csv") ## End(Not run)
Use the GitHub API (github_api()
) to obtain the tags of the
releases.
github_releases( repo, tag = "", pattern = "v[0-9.]+", use_jsonlite = loadable("jsonlite") )
github_releases( repo, tag = "", pattern = "v[0-9.]+", use_jsonlite = loadable("jsonlite") )
repo |
The repository name of the form |
tag |
A tag as a character string. If provided, it will be returned if
the tag exists. If |
pattern |
A regular expression to match the tags. |
use_jsonlite |
Whether to use jsonlite to parse the releases info. |
A character vector of (GIT) tags.
xfun::github_releases("yihui/xfun") xfun::github_releases("gohugoio/hugo")
xfun::github_releases("yihui/xfun") xfun::github_releases("gohugoio/hugo")
gsub()
on elements matched from grep()
This function is a shorthand of gsub(pattern, replacement, grep(pattern, x, value = TRUE))
.
grep_sub(pattern, replacement, x, ...)
grep_sub(pattern, replacement, x, ...)
pattern , replacement , x , ...
|
Passed to |
A character vector.
# find elements that matches 'a[b]+c' and capitalize 'b' with perl regex xfun::grep_sub("a([b]+)c", "a\\U\\1c", c("abc", "abbbc", "addc", "123"), perl = TRUE)
# find elements that matches 'a[b]+c' and capitalize 'b' with perl regex xfun::grep_sub("a([b]+)c", "a\\U\\1c", c("abc", "abbbc", "addc", "123"), perl = TRUE)
These functions provide the "file" version of gsub()
, i.e.,
they perform searching and replacement in files via gsub()
.
gsub_file(file, ..., rw_error = TRUE) gsub_files(files, ...) gsub_dir(..., dir = ".", recursive = TRUE, ext = NULL, mimetype = ".*") gsub_ext(ext, ..., dir = ".", recursive = TRUE)
gsub_file(file, ..., rw_error = TRUE) gsub_files(files, ...) gsub_dir(..., dir = ".", recursive = TRUE, ext = NULL, mimetype = ".*") gsub_ext(ext, ..., dir = ".", recursive = TRUE)
file |
Path of a single file. |
... |
For |
rw_error |
Whether to signal an error if the file cannot be read or
written. If |
files |
A vector of file paths. |
dir |
Path to a directory (all files under this directory will be replaced). |
recursive |
Whether to find files recursively under a directory. |
ext |
A vector of filename extensions (without the leading periods). |
mimetype |
A regular expression to filter files based on their MIME
types, e.g., |
These functions perform in-place replacement, i.e., the files will be overwritten. Make sure you backup your files in advance, or use version control!
library(xfun) f = tempfile() writeLines(c("hello", "world"), f) gsub_file(f, "world", "woRld", fixed = TRUE) readLines(f)
library(xfun) f = tempfile() writeLines(c("hello", "world"), f) gsub_file(f, "world", "woRld", fixed = TRUE) readLines(f)
Given a tag name, generate an HTML tag with optional attributes and content.
html_tag()
can be viewed as a simplified version of htmltools::tags
,
html_value()
adds classes on the value so that it will be treated as raw
HTML (not escaped by html_tag()
), html_escape()
escapes special
characters in HTML, and html_view()
launches a browser or viewer to view
the HTML content.
html_tag(.name, .content = NULL, .attrs = NULL, ...) html_value(x) html_escape(x, attr = FALSE) html_view(x, ...)
html_tag(.name, .content = NULL, .attrs = NULL, ...) html_value(x) html_escape(x, attr = FALSE) html_view(x, ...)
.name |
The tag name. |
.content |
The content between opening and closing tags. Ignored for
void tags such as |
.attrs |
A named list of attributes. |
... |
For |
x |
A character vector to be treated as raw HTML content for
|
attr |
Whether to escape |
A character string.
xfun::html_tag("a", "<R Project>", href = "https://www.r-project.org", target = "_blank") xfun::html_tag("br") xfun::html_tag("a", xfun::html_tag("strong", "R Project"), href = "#") xfun::html_tag("a", list("<text>", xfun::html_tag("b", "R Project")), href = "#") xfun::html_escape("\" quotes \" & brackets < >") xfun::html_escape("\" & < > \r \n", attr = TRUE)
xfun::html_tag("a", "<R Project>", href = "https://www.r-project.org", target = "_blank") xfun::html_tag("br") xfun::html_tag("a", xfun::html_tag("strong", "R Project"), href = "#") xfun::html_tag("a", list("<text>", xfun::html_tag("b", "R Project")), href = "#") xfun::html_escape("\" quotes \" & brackets < >") xfun::html_escape("\" & < > \r \n", attr = TRUE)
Change the working directory, evaluate the expression, and restore the working directory.
in_dir(dir, expr)
in_dir(dir, expr)
dir |
Path to a directory. |
expr |
An R expression. |
library(xfun) in_dir(tempdir(), { print(getwd()) list.files() })
library(xfun) in_dir(tempdir(), { print(getwd()) list.files() })
Run R CMD build
to build a tarball from a source directory, and run
R CMD INSTALL
to install it.
install_dir(pkg, build = TRUE, build_opts = NULL, install_opts = NULL)
install_dir(pkg, build = TRUE, build_opts = NULL, install_opts = NULL)
pkg |
The package source directory. |
build |
Whether to build a tarball from the source directory. If
|
build_opts |
The options for |
install_opts |
The options for |
Invisible status from R CMD INSTALL
.
remotes::install_github()
This alias is to make autocomplete faster via xfun::install_github
, because
most remotes::install_*
functions are never what I want. I only use
install_github
and it is inconvenient to autocomplete it, e.g.
install_git
always comes before install_github
, but I never use it. In
RStudio, I only need to type xfun::ig
to get xfun::install_github
.
install_github(...)
install_github(...)
... |
Arguments to be passed to |
On Unix, check if the paths start with ‘/’ or ‘~’ (if they do, they
are absolute paths). On Windows, check if a path remains the same (via
same_path()
) if it is prepended with ‘./’ (if it does, it is a
relative path).
is_abs_path(x) is_rel_path(x)
is_abs_path(x) is_rel_path(x)
x |
A vector of paths. |
A logical vector.
xfun::is_abs_path(c("C:/foo", "foo.txt", "/Users/john/", tempdir())) xfun::is_rel_path(c("C:/foo", "foo.txt", "/Users/john/", tempdir()))
xfun::is_abs_path(c("C:/foo", "foo.txt", "/Users/john/", tempdir())) xfun::is_rel_path(c("C:/foo", "foo.txt", "/Users/john/", tempdir()))
Converts the encoding of a character vector to 'ascii'
, and check if
the result is NA
.
is_ascii(x)
is_ascii(x)
x |
A character vector. |
A logical vector indicating whether each element of the character vector is ASCII.
library(xfun) is_ascii(letters) # yes is_ascii(intToUtf8(8212)) # no
library(xfun) is_ascii(letters) # yes is_ascii(intToUtf8(8212)) # no
Return a logical vector indicating if elements of a character vector are blank (white spaces or empty strings).
is_blank(x)
is_blank(x)
x |
A character vector. |
TRUE
for blank elements, or FALSE
otherwise.
xfun::is_blank("") xfun::is_blank("abc") xfun::is_blank(c("", " ", "\n\t")) xfun::is_blank(c("", " ", "abc"))
xfun::is_blank("") xfun::is_blank("abc") xfun::is_blank(c("", " ", "\n\t")) xfun::is_blank(c("", " ", "abc"))
Check if the path starts with the dir path.
is_sub_path(x, dir, n = nchar(dir))
is_sub_path(x, dir, n = nchar(dir))
x |
A vector of paths. |
dir |
A vector of directory paths. |
n |
The length of |
A logical vector.
You may want to normalize the values of the x
and dir
arguments
first (with normalize_path()
), to make sure the path separators
are consistent.
xfun::is_sub_path("a/b/c.txt", "a/b") # TRUE xfun::is_sub_path("a/b/c.txt", "d/b") # FALSE xfun::is_sub_path("a/b/c.txt", "a\\b") # FALSE (even on Windows)
xfun::is_sub_path("a/b/c.txt", "a/b") # TRUE xfun::is_sub_path("a/b/c.txt", "d/b") # FALSE xfun::is_sub_path("a/b/c.txt", "a\\b") # FALSE (even on Windows)
Check if a path starts with ‘http://’ or ‘https://’ or ‘ftp://’ or ‘ftps://’.
is_web_path(x)
is_web_path(x)
x |
A vector of paths. |
A logical vector.
xfun::is_web_path("https://www.r-project.org") # TRUE xfun::is_web_path("www.r-project.org") # FALSE
xfun::is_web_path("https://www.r-project.org") # TRUE xfun::is_web_path("www.r-project.org") # FALSE
Functions based on .Platform$OS.type
and Sys.info()
to test if
the current operating system is Windows, macOS, Unix, or Linux.
is_windows() is_unix() is_macos() is_linux() is_arm64()
is_windows() is_unix() is_macos() is_linux() is_arm64()
library(xfun) # only one of the following statements should be true is_windows() is_unix() && is_macos() is_linux() # In newer Macs, CPU can be either Intel or Apple is_arm64() # TRUE on Apple silicone machines
library(xfun) # only one of the following statements should be true is_windows() is_unix() && is_macos() is_linux() # In newer Macs, CPU can be either Intel or Apple is_arm64() # TRUE on Apple silicone machines
If words
is of length 2, the first word and second word are joined by the
and
string; if and
is blank, sep
is used. When the length is greater
than 2, sep
is used to separate all words, and the and
string is
prepended to the last word.
join_words( words, sep = ", ", and = " and ", before = "", after = before, oxford_comma = TRUE )
join_words( words, sep = ", ", and = " and ", before = "", after = before, oxford_comma = TRUE )
words |
A character vector. |
sep |
Separator to be inserted between words. |
and |
Character string to be prepended to the last word. |
before , after
|
A character string to be added before/after each word. |
oxford_comma |
Whether to insert the separator between the last two elements in the list. |
A character string marked by raw_string()
.
join_words("a") join_words(c("a", "b")) join_words(c("a", "b", "c")) join_words(c("a", "b", "c"), sep = " / ", and = "") join_words(c("a", "b", "c"), and = "") join_words(c("a", "b", "c"), before = "\"", after = "\"") join_words(c("a", "b", "c"), before = "\"", after = "\"", oxford_comma = FALSE)
join_words("a") join_words(c("a", "b")) join_words(c("a", "b", "c")) join_words(c("a", "b", "c"), sep = " / ", and = "") join_words(c("a", "b", "c"), and = "") join_words(c("a", "b", "c"), before = "\"", after = "\"") join_words(c("a", "b", "c"), before = "\"", after = "\"", oxford_comma = FALSE)
The function lazy_save()
saves objects to files with incremental integer
names (e.g., the first object is saved to 1.rds
, and the second object is
saved to 2.rds
, etc.). The function lazy_load()
lazy-load objects from
files saved via lazy_save()
, i.e., a file will not be read until the object
is used.
lazy_save(list = NULL, path = "./", method = "auto", envir = parent.frame()) lazy_load(path = "./", method = "auto", envir = parent.frame())
lazy_save(list = NULL, path = "./", method = "auto", envir = parent.frame()) lazy_load(path = "./", method = "auto", envir = parent.frame())
list |
A character vector of object names. This list will be written to
an index file with |
path |
The path to write files to / read files from. |
method |
The file save/load method. It can be a string (e.g., |
envir |
lazy_save()
returns invisible NULL
; lazy_load()
returns the
object names invisibly.
Given a path, try to find it recursively under a root directory. The input
path can be an incomplete path, e.g., it can be a base filename, and
magic_path()
will try to find this file under subdirectories.
magic_path( ..., root = proj_root(), relative = TRUE, error = TRUE, message = getOption("xfun.magic_path.message", TRUE), n_dirs = getOption("xfun.magic_path.n_dirs", 10000) )
magic_path( ..., root = proj_root(), relative = TRUE, error = TRUE, message = getOption("xfun.magic_path.message", TRUE), n_dirs = getOption("xfun.magic_path.n_dirs", 10000) )
... |
A character vector of path components. |
root |
The root directory under which to search for the path. If
|
relative |
Whether to return a relative path. |
error |
Whether to signal an error if the path is not found, or multiple paths are found. |
message |
Whether to emit a message when multiple paths are found and
|
n_dirs |
The number of subdirectories to recursively search. The
recursive search may be time-consuming when there are a large number of
subdirectories under the root directory. If you really want to search for
all subdirectories, you may try |
The path found under the root directory, or an error when error = TRUE
and the path is not found (or multiple paths are found).
## Not run: xfun::magic_path("mtcars.csv") # find any file that has the base name mtcars.csv ## End(Not run)
## Not run: xfun::magic_path("mtcars.csv") # find any file that has the base name mtcars.csv ## End(Not run)
Add a trailing backlash to a file path if this is a directory. This is useful in messages to the console for example to quickly identify directories from files.
mark_dirs(x)
mark_dirs(x)
x |
Character vector of paths to files and directories. |
If x
is a vector of relative paths, directory test is done with path
relative to the current working dir. Use in_dir()
or use absolute
paths.
mark_dirs(list.files(find.package("xfun"), full.names = TRUE))
mark_dirs(list.files(find.package("xfun"), full.names = TRUE))
A minimal Markdown table generator using the pipe |
as column separators.
md_table(x, digits = NULL, na = NULL, newline = NULL, limit = NULL)
md_table(x, digits = NULL, na = NULL, newline = NULL, limit = NULL)
x |
A 2-dimensional object (e.g., a matrix or data frame). |
digits |
The number of decimal places to be passed to |
na |
A character string to represent |
newline |
A character string to substitute |
limit |
The maximum number of rows to show in the table. If it is smaller than the number of rows, the data in the middle will be omitted. If it is of length 2, the second number will be used to limit the number of columns. Zero and negative values are ignored. |
The default argument values can be set via global options with the prefix
xfun.md_table.
, e.g., options(xfun.md_table.digits 2, xfun.md_table.na = 'n/a')
.
A character vector.
knitr::kable()
(which supports more features)
xfun::md_table(head(iris)) xfun::md_table(mtcars, limit = c(10, 6))
xfun::md_table(head(iris)) xfun::md_table(mtcars, limit = c(10, 6))
Serialize an object and calculate the checksum via
tools::md5sum()
. If tools::md5sum()
does not have the argument bytes
,
the object will be first serialized to a temporary file, which will be
deleted after the checksum is calculated, otherwise the raw bytes of the
object will be passed to the bytes
argument directly (which will be
faster than writing to a temporary file).
md5(...)
md5(...)
... |
Any number of R objects. |
A character vector of the checksums of objects passed to md5()
. If
the arguments are named, the results will also be named.
x1 = 1 x2 = 1:10 x3 = seq(1, 10) x4 = iris x5 = paste (m = xfun::md5(x1, x2, x3, x4, x5)) stopifnot(m[2] == m[3]) # x2 and x3 should be identical xfun::md5(x1 = x1, x2 = x2) # named arguments
x1 = 1 x2 = 1:10 x3 = seq(1, 10) x4 = iris x5 = paste (m = xfun::md5(x1, x2, x3, x4, x5)) stopifnot(m[2] == m[3]) # x2 and x3 should be identical xfun::md5(x1 = x1, x2 = x2) # named arguments
If the mime package is installed, call mime::guess_type()
, otherwise
use the system command file --mime-type
to obtain the MIME type of a file.
Typically, the file
command exists on *nix. On Windows, the command should
exist if Cygwin or Rtools is installed. If it is not found, .NET's
MimeMapping
class will be used instead (which requires the .NET framework).
mime_type(x, use_mime = loadable("mime"), empty = "text/plain")
mime_type(x, use_mime = loadable("mime"), empty = "text/plain")
x |
A vector of file paths. |
use_mime |
Whether to use the mime package. |
empty |
The MIME type for files without extensions (e.g., |
A character vector of MIME types.
When querying the MIME type via the system command, the result will be
cached to xfun:::cache_dir()
. This will make future queries much faster,
since running the command in real time can be a little slow.
f = list.files(R.home("doc"), full.names = TRUE) mime_type(f) mime_type(f, FALSE) # don't use mime mime_type(f, FALSE, NA) # run command for files without extension
f = list.files(R.home("doc"), full.names = TRUE) mime_type(f) mime_type(f, FALSE) # don't use mime mime_type(f, FALSE, NA) # run command for files without extension
cat()
This function is similar to message()
, and the difference is
that msg_cat()
uses cat()
to write out the message,
which is sent to stdout()
instead of stderr()
. The
message can be suppressed by suppressMessages()
.
msg_cat(...)
msg_cat(...)
... |
Character strings of messages, which will be concatenated into one
string via |
Invisible NULL
, with the side-effect of printing the message.
By default, a newline will not be appended to the message. If you need a newline, you have to explicitly add it to the message (see ‘Examples’).
This function was inspired by rlang::inform()
.
{ # a message without a newline at the end xfun::msg_cat("Hello world!") # add a newline at the end xfun::msg_cat(" This message appears right after the previous one.\n") } suppressMessages(xfun::msg_cat("Hello world!"))
{ # a message without a newline at the end xfun::msg_cat("Hello world!") # add a newline at the end xfun::msg_cat(" This message appears right after the previous one.\n") } suppressMessages(xfun::msg_cat("Hello world!"))
Apply enc2native()
to the character vector, and check if enc2utf8()
can
convert it back without a loss. If it does, return enc2native(x)
, otherwise
return the original vector with a warning.
native_encode(x)
native_encode(x)
x |
A character vector. |
On platforms that supports UTF-8 as the native encoding
(l10n_info()
[['UTF-8']]
returns TRUE
), the conversion will be
skipped.
library(xfun) s = intToUtf8(c(20320, 22909)) Encoding(s) s2 = native_encode(s) Encoding(s2)
library(xfun) s = intToUtf8(c(20320, 22909)) Encoding(s) s2 = native_encode(s) Encoding(s2)
An experimental function to create a local web application based on R's
internal httpd
server (which is primarily for running R's dynamic help
system).
new_app(name, handler, open = interactive(), ports = 4321 + 1:10)
new_app(name, handler, open = interactive(), ports = 4321 + 1:10)
name |
The app name (a character string, and each app should have a unique name). |
handler |
A function that takes the HTTP request information (the first argument is the requested path) and returns a response. |
open |
Whether to open the app, or a function to open the app URL. |
ports |
A vector of ports to try for starting the server. |
The app URL of the form http://127.0.0.1:port/custom/name/
.
This function is not based on base R's public API, and is possible to break in the future, which is also why the documentation here is terse. Please avoid creating public-facing web apps with it. You may consider packages like httpuv and Rserve for production web apps.
Read the package news with news()
, convert the result to
Markdown, and write to an output file (e.g., ‘NEWS.md’). Each package
version appears in a first-level header, each category (e.g., ‘NEW
FEATURES’ or ‘BUG FIXES’) is in a second-level header, and the news
items are written into bullet lists.
news2md(package, ..., output = "NEWS.md", category = TRUE)
news2md(package, ..., output = "NEWS.md", category = TRUE)
package , ...
|
Arguments to be passed to |
output |
The output file path. |
category |
Whether to keep the category names. |
If output = NA
, returns the Markdown content as a character
vector, otherwise the content is written to the output file.
# news for the current version of R xfun::news2md("R", Version == getRversion(), output = NA)
# news for the current version of R xfun::news2md("R", Version == getRversion(), output = NA)
A wrapper function of normalizePath()
with different defaults.
normalize_path(x, winslash = "/", must_work = FALSE, resolve_symlink = TRUE)
normalize_path(x, winslash = "/", must_work = FALSE, resolve_symlink = TRUE)
x , winslash , must_work
|
Arguments passed to
|
resolve_symlink |
Whether to resolve symbolic links. |
library(xfun) normalize_path("~")
library(xfun) normalize_path("~")
This can be helpful when writing reports with knitr/rmarkdown if
we want to print numbers as English words in the output. The function n2w()
is an alias of numbers_to_words()
.
numbers_to_words(x, cap = FALSE, hyphen = TRUE, and = FALSE) n2w(x, cap = FALSE, hyphen = TRUE, and = FALSE)
numbers_to_words(x, cap = FALSE, hyphen = TRUE, and = FALSE) n2w(x, cap = FALSE, hyphen = TRUE, and = FALSE)
x |
A numeric vector. The absolute values should be less than |
cap |
Whether to capitalize the first letter of the word. This can be
useful when the word is at the beginning of a sentence. Default is |
hyphen |
Whether to insert hyphen (-) when the number is between 21 and 99 (except 30, 40, etc.). |
and |
Whether to insert |
A character vector.
Daijiang Li
library(xfun) n2w(0, cap = TRUE) n2w(0:121, and = TRUE) n2w(1e+06) n2w(1e+11 + 12345678) n2w(-987654321) n2w(1e+15 - 1) n2w(123.456) n2w(123.45678901) n2w(123.456789098765)
library(xfun) n2w(0, cap = TRUE) n2w(0:121, and = TRUE) n2w(1e+06) n2w(1e+11 + 12345678) n2w(-987654321) n2w(1e+15 - 1) n2w(123.456) n2w(123.45678901) n2w(123.456789098765)
Call the command optipng
via system2()
to optimize all PNG
files under a directory.
optipng(dir = ".", files = all_files("[.]png$", dir), ...)
optipng(dir = ".", files = all_files("[.]png$", dir), ...)
dir |
Path to a directory. |
files |
Alternatively, you can choose the specific files to optimize. |
... |
Arguments to be passed to |
OptiPNG: https://optipng.sourceforge.net.
An abbreviation of parse(keep.source = FALSE)
.
parse_only(code)
parse_only(code)
code |
A character vector of the R source code. |
R expression()
s.
library(xfun) parse_only("1+1") parse_only(c("y~x", "1:5 # a comment")) parse_only(character(0))
library(xfun) parse_only("1+1") parse_only(c("y~x", "1:5 # a comment")) parse_only(character(0))
pkg_attach()
is a vectorized version of library()
over
the package
argument to attach multiple packages in a single function
call. pkg_load()
is a vectorized version of
requireNamespace()
to load packages (without attaching them).
The functions pkg_attach2()
and pkg_load2()
are wrappers of
pkg_attach(install = TRUE)
and pkg_load(install = TRUE)
,
respectively. loadable()
is an abbreviation of
requireNamespace(quietly = TRUE)
. pkg_available()
tests if a
package with a minimal version is available.
pkg_attach( ..., install = FALSE, message = getOption("xfun.pkg_attach.message", TRUE) ) pkg_load(..., error = TRUE, install = FALSE) loadable(pkg, strict = TRUE, new_session = FALSE) pkg_available(pkg, version = NULL) pkg_attach2(...) pkg_load2(...)
pkg_attach( ..., install = FALSE, message = getOption("xfun.pkg_attach.message", TRUE) ) pkg_load(..., error = TRUE, install = FALSE) loadable(pkg, strict = TRUE, new_session = FALSE) pkg_available(pkg, version = NULL) pkg_attach2(...) pkg_load2(...)
... |
Package names (character vectors, and must always be quoted). |
install |
Whether to automatically install packages that are not
available using |
message |
Whether to show the package startup messages (if any startup messages are provided in a package). |
error |
Whether to signal an error when certain packages cannot be loaded. |
pkg |
A single package name. |
strict |
If |
new_session |
Whether to test if a package is loadable in a new R
session. Note that |
version |
A minimal version number. If |
These are convenience functions that aim to solve these common problems: (1)
We often need to attach or load multiple packages, and it is tedious to type
several library()
calls; (2) We are likely to want to install the
packages when attaching/loading them but they have not been installed.
pkg_attach()
returns NULL
invisibly. pkg_load()
returns a logical vector, indicating whether the packages can be loaded.
pkg_attach2()
is similar to pacman::p_load()
, but does
not allow non-standard evaluation (NSE) of the ...
argument, i.e.,
you must pass a real character vector of package names to it, and all names
must be quoted. Allowing NSE adds too much complexity with too little gain
(the only gain is that it saves your effort in typing two quotes).
library(xfun) pkg_attach("stats", "graphics") # pkg_attach2('servr') # automatically install servr if it is not installed (pkg_load("stats", "graphics"))
library(xfun) pkg_attach("stats", "graphics") # pkg_attach2('servr') # automatically install servr if it is not installed (pkg_load("stats", "graphics"))
Call utils::citation()
and utils::toBibtex()
to create bib entries for R
packages and write them in a file. It can facilitate the auto-generation of
bibliography databases for R packages, and it is easy to regenerate all the
citations after updating R packages.
pkg_bib( x = .packages(), file = "", tweak = TRUE, width = NULL, prefix = getOption("xfun.bib.prefix", "R-"), lib.loc = NULL, packageURL = TRUE )
pkg_bib( x = .packages(), file = "", tweak = TRUE, width = NULL, prefix = getOption("xfun.bib.prefix", "R-"), lib.loc = NULL, packageURL = TRUE )
x |
Package names. Packages which are not installed are ignored. |
file |
The (‘.bib’) file to write. By default, or if |
tweak |
Whether to fix some known problems in the citations, especially non-standard format of author names. |
width |
Width of lines in bibliography entries. If |
prefix |
Prefix string for keys in BibTeX entries; by default, it is
|
lib.loc |
A vector of path names of R libraries. |
packageURL |
Use the |
For a package, the keyword R-pkgname
is used for its bib item, where
pkgname
is the name of the package. Citation entries specified in the
‘CITATION’ file of the package are also included. The main purpose of
this function is to automate the generation of the package citation
information because it often changes (e.g., the author, year, package
version, and so on).
There are at least two different uses for the URL in a reference list. You
might want to tell users where to go for more information. In that case, use
the default packageURL = TRUE
, and the first URL listed in the
‘DESCRIPTION’ file will be used. Be careful: some authors don't put the
most relevant URL first. Alternatively, you might want to identify exactly
which version of the package was used in the document. If it was installed
from CRAN or some other repositories, the version number identifies it, and
packageURL = FALSE
will use the repository URL (as used by
utils::citation()
).
A list containing the citations. Citations are also written to the
file
as a side effect.
Some packages on CRAN do not have standard bib entries, which was once
reported by Michael Friendly at
https://stat.ethz.ch/pipermail/r-devel/2010-November/058977.html. I
find this a pain, and there are no easy solutions except contacting package
authors to modify their DESCRIPTION files. The argument tweak
has
provided hacks to deal with known packages with non-standard bib entries;
tweak = TRUE
is by no means intended to hide or modify the original
citation information. It is just due to the loose requirements on package
authors for the DESCRIPTION file. On one hand, I apologize if it really
mangles the information about certain packages; on the other, I strongly
recommend package authors to consider the Authors@R
field (see the
manual Writing R Extensions) to make it easier for other people to cite R
packages.
Yihui Xie and Michael Friendly
pkg_bib(c("base", "MASS", "xfun")) pkg_bib("cluster", prefix = "R-pkg-") # a different prefix
pkg_bib(c("base", "MASS", "xfun")) pkg_bib("cluster", prefix = "R-pkg-") # a different prefix
Run the command taskkill /f /pid
on Windows and kill
on
Unix, respectively, to kill a process.
proc_kill(pid, recursive = TRUE, ...)
proc_kill(pid, recursive = TRUE, ...)
pid |
The process ID. |
recursive |
Whether to kill the child processes of the process. |
... |
Arguments to be passed to |
The status code returned from system2()
.
Read a text file with the UTF-8 encoding, apply a function to the text, and write back to the original file if the processed text is different with the original input.
process_file(file, fun = identity, x = read_utf8(file)) sort_file(..., fun = sort)
process_file(file, fun = identity, x = read_utf8(file)) sort_file(..., fun = sort)
file |
Path to a text file. |
fun |
A function to process the text. |
x |
The content of the file. |
... |
Arguments to be passed to |
sort_file()
is an application of process_file()
, with the processing
function being sort()
, i.e., it sorts the text lines in a file and write
back the sorted text.
If file
is provided, invisible NULL
(the file is updated as a
side effect), otherwise the processed content (as a character vector).
f = tempfile() xfun::write_utf8("Hello World", f) xfun::process_file(f, function(x) gsub("World", "woRld", x)) xfun::read_utf8(f) # see if it has been updated file.remove(f)
f = tempfile() xfun::write_utf8("Hello World", f) xfun::process_file(f, function(x) gsub("World", "woRld", x)) xfun::read_utf8(f) # see if it has been updated file.remove(f)
Given a path of a file (or dir) in a potential project (e.g., an R package or an RStudio project), return the path to the project root directory.
proj_root(path = "./", rules = root_rules) root_rules
proj_root(path = "./", rules = root_rules) root_rules
path |
The initial path to start the search. If it is a file path, its parent directory will be used. |
rules |
A matrix of character strings of two columns: the first column contains regular expressions to look for filenames that match the patterns, and the second column contains regular expressions to match the content of the matched files. The regular expression can be an empty string, meaning that it will match anything. |
An object of class matrix
(inherits from array
) with 2 rows and 2 columns.
The search for the root directory is performed by a series of tests,
currently including looking for a ‘DESCRIPTION’ file that contains
Package: *
(which usually indicates an R package), and a
‘*.Rproj’ file that contains Version: *
(which usually indicates
an RStudio project). If files with the expected patterns are not found in the
initial directory, the search will be performed recursively in upper-level
directories.
Path to the root directory if found, otherwise NULL
.
This function was inspired by the rprojroot package, but is much less sophisticated. It is a rather simple function designed to be used in some of packages that I maintain, and may not meet the need of general users until this note is removed in the future (which should be unlikely). If you are sure that you are working on the types of projects mentioned in the ‘Details’ section, this function may be helpful to you, otherwise please consider using rprojroot instead.
Filter out the indices of lines between code block fences such as ```
(could be three or four or more backticks).
prose_index(x, warn = TRUE)
prose_index(x, warn = TRUE)
x |
A character vector of text in Markdown. |
warn |
Whether to emit a warning when code fences are not balanced. |
An integer vector of indices of lines that are prose in Markdown.
If the code fences are not balanced (e.g., a starting fence without an ending fence), this function will treat all lines as prose.
library(xfun) prose_index(c("a", "```", "b", "```", "c")) prose_index(c("a", "````", "```r", "1+1", "```", "````", "c"))
library(xfun) prose_index(c("a", "```", "b", "```", "c")) prose_index(c("a", "````", "```r", "1+1", "```", "````", "c"))
For Markdown renderers that do not support LaTeX math, we need to protect math expressions as verbatim code (in a pair of backticks), because some characters in the math expressions may be interpreted as Markdown syntax (e.g., a pair of underscores may make text italic). This function detects math expressions in Markdown (by heuristics), and wrap them in backticks.
protect_math(x, token = "", use_block = FALSE)
protect_math(x, token = "", use_block = FALSE)
x |
A character vector of text in Markdown. |
token |
A character string to wrap math expressions at both ends. This can be a unique token so that math expressions can be reliably identified and restored after the Markdown text is converted. |
use_block |
Whether to use code blocks ( |
Expressions in pairs of dollar signs or double dollar signs are treated as
math, if there are no spaces after the starting dollar sign, or before the
ending dollar sign. There should be a space or (
before the starting dollar
sign, unless the math expression starts from the very beginning of a line.
For a pair of single dollar signs, the ending dollar sign should not be
followed by a number, and the inner math expression should not be wrapped in
backticks. With these assumptions, there should not be too many false
positives when detecing math expressions.
Besides, LaTeX environments (\begin{*}
and \end{*}
) are also
protected in backticks.
A character vector with math expressions in backticks.
If you are using Pandoc or the rmarkdown package, there is no need to use this function, because Pandoc's Markdown can recognize math expressions.
library(xfun) protect_math(c("hi $a+b$", "hello $$\\alpha$$", "no math here: $x is $10 dollars")) protect_math(c("hi $$", "\\begin{equation}", "x + y = z", "\\end{equation}")) protect_math("$a+b$", "===")
library(xfun) protect_math(c("hi $a+b$", "hello $$\\alpha$$", "no math here: $x is $10 dollars")) protect_math(c("hi $$", "\\begin{equation}", "x + y = z", "\\end{equation}")) protect_math("$a+b$", "===")
The function raw_string()
assigns the class xfun_raw_string
to
the character vector, and the corresponding printing function
print.xfun_raw_string()
uses cat(x, sep = '\n')
to write the
character vector to the console, which will suppress the leading indices
(such as [1]
) and double quotes, and it may be easier to read the
characters in the raw form (especially when there are escape sequences).
raw_string(x) ## S3 method for class 'xfun_raw_string' print(x, ...)
raw_string(x) ## S3 method for class 'xfun_raw_string' print(x, ...)
x |
For |
... |
Other arguments (currently ignored). |
library(xfun) raw_string(head(LETTERS)) raw_string(c("a \"b\"", "hello\tworld!"))
library(xfun) raw_string(head(LETTERS)) raw_string(c("a \"b\"", "hello\tworld!"))
Read files one by one, and optionally add text before/after the content. Then combine all content into one character vector.
read_all(files, before = function(f, x) NULL, after = function(f, x) NULL)
read_all(files, before = function(f, x) NULL, after = function(f, x) NULL)
files |
A vector of file paths. |
before , after
|
A function that takes one file path and its content as the input and returns values to be added before or after the content of the file. Alternatively, they can be constant values to be added. |
A character vector.
# two files in this package fs = system.file("scripts", c("call-fun.R", "child-pids.sh"), package = "xfun") xfun::read_all(fs) # add file paths before file content and an empty line after content xfun::read_all(fs, before = function(f) paste("#-----", f, "-----"), after = "") # add constants xfun::read_all(fs, before = "/*", after = c("*/", ""))
# two files in this package fs = system.file("scripts", c("call-fun.R", "child-pids.sh"), package = "xfun") xfun::read_all(fs) # add file paths before file content and an empty line after content xfun::read_all(fs, before = function(f) paste("#-----", f, "-----"), after = "") # add constants xfun::read_all(fs, before = "/*", after = c("*/", ""))
This is a wrapper function of readBin()
with default arguments
what = "raw"
and n =
file.size
(file)
, which means it
will read the full content of a binary file as a raw vector by default.
read_bin(file, what = "raw", n = file.info(file)$size, ...)
read_bin(file, what = "raw", n = file.info(file)$size, ...)
file , what , n , ...
|
Arguments to be passed to |
A vector returned from readBin()
.
f = tempfile() cat("abc", file = f) xfun::read_bin(f) unlink(f)
f = tempfile() cat("abc", file = f) xfun::read_bin(f) unlink(f)
Read or write files, assuming they are encoded in UTF-8. read_utf8()
is
roughly readLines(encoding = 'UTF-8')
(a warning will be issued if non-UTF8
lines are found), and write_utf8()
calls writeLines(enc2utf8(text), useBytes = TRUE)
.
read_utf8(con, error = FALSE) write_utf8(text, con, ...) append_utf8(text, con, sort = TRUE) append_unique(text, con, sort = function(x) base::sort(unique(x)))
read_utf8(con, error = FALSE) write_utf8(text, con, ...) append_utf8(text, con, sort = TRUE) append_unique(text, con, sort = function(x) base::sort(unique(x)))
con |
A connection or a file path. |
error |
Whether to signal an error when non-UTF8 characters are detected
(if |
text |
A character vector (will be converted to UTF-8 via |
... |
Other arguments passed to |
sort |
Logical ( |
The function append_utf8()
appends UTF-8 content to a file or connection
based on read_utf8()
and write_utf8()
, and optionally sort the content.
The function append_unique()
appends unique lines to a file or connection.
read_utf8()
returns a character vector of the file content;
write_utf8()
returns the con
argument (invisibly).
Run R code and capture various types of output, including text output, plots, messages, warnings, and errors.
record( code = NULL, dev = "png", dev.path = "xfun-record", dev.ext = dev_ext(dev), dev.args = list(), message = TRUE, warning = TRUE, error = NA, cache = list(), print = record_print, print.args = list(), verbose = getOption("xfun.record.verbose", 0), envir = parent.frame() ) ## S3 method for class 'xfun_record_results' format( x, to = c("text", "markdown", "html"), encode = FALSE, template = FALSE, ... ) ## S3 method for class 'xfun_record_results' print( x, browse = interactive(), to = if (browse) "html" else "text", template = TRUE, ... )
record( code = NULL, dev = "png", dev.path = "xfun-record", dev.ext = dev_ext(dev), dev.args = list(), message = TRUE, warning = TRUE, error = NA, cache = list(), print = record_print, print.args = list(), verbose = getOption("xfun.record.verbose", 0), envir = parent.frame() ) ## S3 method for class 'xfun_record_results' format( x, to = c("text", "markdown", "html"), encode = FALSE, template = FALSE, ... ) ## S3 method for class 'xfun_record_results' print( x, browse = interactive(), to = if (browse) "html" else "text", template = TRUE, ... )
code |
A character vector of R source code. |
dev |
A graphics device. It can be a function name, a function, or a character string that can be evaluated to a function to open a graphics device. |
dev.path |
A base file path for plots. Actual plot filenames will be
this base path plus incremental suffixes. For example, if |
dev.ext |
The file extension for plot files. By default, it will be inferred from the first argument of the device function if possible. |
dev.args |
Extra arguments to be passed to the device. The default
arguments are |
message , warning , error
|
If |
cache |
A list of options for caching. See the |
print |
A (typically S3) function that takes the value of an expression
in the code as input and returns output. The default is |
print.args |
A list of arguments for the |
verbose |
|
envir |
An environment in which the code is evaluated. |
x |
An object returned by |
to |
The output format (text, markdown, or html). |
encode |
For HTML output, whether to base64 encode plots. |
template |
For HTML output, whether to embed the formatted results in an
HTML template. Alternatively, this argument can take a file path, i.e.,
path to an HTML template that contains the variable |
... |
Currently ignored. |
browse |
Whether to browse the results on an HTML page. |
record()
returns a list of the class xfun_record_results
that
contains elements with these possible classes: record_source
(source
code), record_output
(text output), record_plot
(plot file paths),
record_message
(messages), record_warning
(warnings), and
record_error
(errors, only when the argument error = TRUE
).
The format()
method returns a character vector of plain-text output
or HTML code for displaying the results.
The print()
method prints the results as plain text or HTML to the
console or displays the HTML page.
code = c("# a warning test", "1:2 + 1:3", "par(mar = c(4, 4, 1, .2))", "barplot(5:1, col = 2:6, horiz = TRUE)", "head(iris)", "sunflowerplot(iris[, 3:4], seg.col = 'purple')", "if (TRUE) {\n message('Hello, xfun::record()!')\n}", "# throw an error", "1 + 'a'") res = xfun::record(code, dev.args = list(width = 9, height = 6.75), error = TRUE) xfun::tree(res) format(res) # find and clean up plot files plots = Filter(function(x) inherits(x, "record_plot"), res) file.remove(unlist(plots))
code = c("# a warning test", "1:2 + 1:3", "par(mar = c(4, 4, 1, .2))", "barplot(5:1, col = 2:6, horiz = TRUE)", "head(iris)", "sunflowerplot(iris[, 3:4], seg.col = 'purple')", "if (TRUE) {\n message('Hello, xfun::record()!')\n}", "# throw an error", "1 + 'a'") res = xfun::record(code, dev.args = list(width = 9, height = 6.75), error = TRUE) xfun::tree(res) format(res) # find and clean up plot files plots = Filter(function(x) inherits(x, "record_plot"), res) file.remove(unlist(plots))
record()
An S3 generic function to be called to print visible values in code when the
code is recorded by record()
. It is similar to knitr::knit_print()
. By
default, it captures the normal print()
output and returns the result as a
character vector. The knitr_kable
method is for printing knitr::kable()
output. Users and package authors can define other S3 methods to extend this
function.
record_print(x, ...) ## Default S3 method: record_print(x, ...) ## S3 method for class 'record_asis' record_print(x, ...) new_record(x, class)
record_print(x, ...) ## Default S3 method: record_print(x, ...) ## S3 method for class 'record_asis' record_print(x, ...) new_record(x, class)
x |
For |
... |
Other arguments to be passed to |
class |
A class name. Possible values are |
A record_print()
method should return a character vector or a list
of character vectors. The original classes of the vector will be discarded,
and the vector will be treated as console output by default (i.e.,
new_record(class = "output")
). If it should be another type of output,
wrap the vector in new_record()
and specify a class name.
Given a directory, return the relative path that is relative to this directory. For example, the path ‘foo/bar.txt’ relative to the directory ‘foo/’ is ‘bar.txt’, and the path ‘/a/b/c.txt’ relative to ‘/d/e/’ is ‘../../a/b/c.txt’.
relative_path(x, dir = ".", use.. = TRUE, error = TRUE)
relative_path(x, dir = ".", use.. = TRUE, error = TRUE)
x |
A vector of paths to be converted to relative paths. |
dir |
Path to a directory. |
use.. |
Whether to use double-dots (‘..’) in the relative path. A
double-dot indicates the parent directory (starting from the directory
provided by the |
error |
Whether to signal an error if a path cannot be converted to a relative path. |
A vector of relative paths if the conversion succeeded; otherwise the
original paths when error = FALSE
, and an error when error = TRUE
.
xfun::relative_path("foo/bar.txt", "foo/") xfun::relative_path("foo/bar/a.txt", "foo/haha/") xfun::relative_path(getwd())
xfun::relative_path("foo/bar.txt", "foo/") xfun::relative_path("foo/bar/a.txt", "foo/haha/") xfun::relative_path(getwd())
Rename a series of files and add an incremental numeric prefix to the filenames. For example, files ‘a.txt’, ‘b.txt’, and ‘c.txt’ can be renamed to ‘1-a.txt’, ‘2-b.txt’, and ‘3-c.txt’.
rename_seq( pattern = "^[0-9]+-.+[.]Rmd$", format = "auto", replace = TRUE, start = 1, dry_run = TRUE )
rename_seq( pattern = "^[0-9]+-.+[.]Rmd$", format = "auto", replace = TRUE, start = 1, dry_run = TRUE )
pattern |
A regular expression for |
format |
The format for the numeric prefix. This is passed to
|
replace |
Whether to remove existing numeric prefixes in filenames. |
start |
The starting number for the prefix (it can start from 0). |
dry_run |
Whether to not really rename files. To be safe, the default is
|
A named character vector. The names are original filenames, and the vector itself is the new filenames.
xfun::rename_seq() xfun::rename_seq("[.](jpeg|png)$", format = "%04d")
xfun::rename_seq() xfun::rename_seq("[.](jpeg|png)$", format = "%04d")
Read data from a REST API and optionally with an authorization token in the
request header. The function rest_api_raw()
returns the raw text of
the response, and rest_api()
will parse the response with
jsonlite::fromJSON()
(assuming that the response is in the JSON
format).
rest_api(...) rest_api_raw(root, endpoint, token = "", params = list(), headers = NULL) github_api( endpoint, token = "", params = list(), headers = NULL, raw = !loadable("jsonlite") )
rest_api(...) rest_api_raw(root, endpoint, token = "", params = list(), headers = NULL) github_api( endpoint, token = "", params = list(), headers = NULL, raw = !loadable("jsonlite") )
... |
Arguments to be passed to |
root |
The API root URL. |
endpoint |
The API endpoint. |
token |
A named character string (e.g., |
params |
A list of query parameters to be sent with the API call. |
headers |
A named character vector of HTTP headers, e.g., |
raw |
Whether to return the raw response or parse the response with jsonlite. |
These functions are simple wrappers based on url()
and
read_utf8()
. Specifically, the headers
argument is
passed to url()
, and read_utf8()
will send a ‘GET’ request
to the API server. This means these functions only support the ‘GET’
method. If you need to use other HTTP methods (such as ‘POST’), you have
to use other packages such as curl and httr.
github_api()
is a wrapper function based on
rest_api_raw()
to obtain data from the GitHub API:
https://docs.github.com/en/rest. You can provide a personal access
token (PAT) via the token
argument, or via one of the environment
variables GITHUB_PAT, GITHUB_TOKEN, GH_TOKEN. A PAT
allows for a much higher rate limit in API calls. Without a token, you can
only make 60 calls in an hour.
A character vector (the raw JSON response) or an R object parsed from the JSON text.
# a normal GET request xfun::rest_api("https://httpbin.org", "/get") xfun::rest_api_raw("https://httpbin.org", "/get") # send the request with an auth header xfun::rest_api("https://httpbin.org", "/headers", "OPEN SESAME!") # with query parameters xfun::rest_api("https://httpbin.org", "/response-headers", params = list(foo = "bar")) # get the rate limit info from GitHub xfun::github_api("/rate_limit")
# a normal GET request xfun::rest_api("https://httpbin.org", "/get") xfun::rest_api_raw("https://httpbin.org", "/get") # send the request with an auth header xfun::rest_api("https://httpbin.org", "/headers", "OPEN SESAME!") # with query parameters xfun::rest_api("https://httpbin.org", "/response-headers", params = list(foo = "bar")) # get the rate limit info from GitHub xfun::github_api("/rate_limit")
If the function returns an error, retry it for the specified number of times, with a pause between attempts.
retry(fun, ..., .times = 3, .pause = 5)
retry(fun, ..., .times = 3, .pause = 5)
fun |
A function. |
... |
Arguments to be passed to the function. |
.times |
The number of times. |
.pause |
The number of seconds to wait before the next attempt. |
One application of this function is to download a web resource. Since the download might fail sometimes, you may want to retry it for a few more times.
# read the GitHub releases info of the repo yihui/xfun xfun::retry(xfun::github_releases, "yihui/xfun")
# read the GitHub releases info of the repo yihui/xfun xfun::retry(xfun::github_releases, "yihui/xfun")
R CMD check
on the reverse dependencies of a packageInstall the source package, figure out the reverse dependencies on CRAN,
download all of their source packages, and run R CMD check
on them
in parallel.
rev_check( pkg, which = "all", recheck = NULL, ignore = NULL, update = TRUE, timeout = getOption("xfun.rev_check.timeout", 15 * 60), src = file.path(src_dir, pkg), src_dir = getOption("xfun.rev_check.src_dir") ) compare_Rcheck(status_only = TRUE, output = "00check_diffs.md")
rev_check( pkg, which = "all", recheck = NULL, ignore = NULL, update = TRUE, timeout = getOption("xfun.rev_check.timeout", 15 * 60), src = file.path(src_dir, pkg), src_dir = getOption("xfun.rev_check.src_dir") ) compare_Rcheck(status_only = TRUE, output = "00check_diffs.md")
pkg |
The package name. |
which |
Which types of reverse dependencies to check. See
|
recheck |
A vector of package names to be (re)checked. If not provided
and there are any ‘*.Rcheck’ directories left by certain packages
(this often means these packages failed the last time), |
ignore |
A vector of package names to be ignored in |
update |
Whether to update all packages before the check. |
timeout |
Timeout in seconds for |
src |
The path of the source package directory. |
src_dir |
The parent directory of the source package directory. This can be set in a global option if all your source packages are under a common parent directory. |
status_only |
If |
output |
The output Markdown file to which the diffs in check logs will be written. If the markdown package is available, the Markdown file will be converted to HTML, so you can see the diffs more clearly. |
Everything occurs under the current working directory, and you are recommended to call this function under a designated directory, especially when the number of reverse dependencies is large, because all source packages will be downloaded to this directory, and all ‘*.Rcheck’ directories will be generated under this directory, too.
If a source tarball of the expected version has been downloaded before (under the ‘tarball’ directory), it will not be downloaded again (to save time and bandwidth).
After a package has been checked, the associated ‘*.Rcheck’ directory will be deleted if the check was successful (no warnings or errors or notes), which means if you see a ‘*.Rcheck’ directory, it means the check failed, and you need to take a look at the log files under that directory.
The time to finish the check is recorded for each package. As the check goes
on, the total remaining time will be roughly estimated via n * mean(times)
, where n
is the number of packages remaining to be
checked, and times
is a vector of elapsed time of packages that have
been checked.
If a check on a reverse dependency failed, its ‘*.Rcheck’ directory will
be renamed to ‘*.Rcheck2’, and another check will be run against the
CRAN version of the package unless options(xfun.rev_check.compare = FALSE)
is set. If the logs of the two checks are the same, it means no new
problems were introduced in the package, and you can probably ignore this
particular reverse dependency. The function compare_Rcheck()
can be
used to create a summary of all the differences in the check logs under
‘*.Rcheck’ and ‘*.Rcheck2’. This will be done automatically if
options(xfun.rev_check.summary = TRUE)
has been set.
A recommended workflow is to use a special directory to run
rev_check()
, set the global options()
xfun.rev_check.src_dir
and repos
in the R startup (see
?
Startup
) profile file .Rprofile
under this directory,
and (optionally) set R_LIBS_USER
in ‘.Renviron’ to use a special
library path (so that your usual library will not be cluttered). Then run
xfun::rev_check(pkg)
once, investigate and fix the problems or (if you
believe it was not your fault) ignore broken packages in the file
‘00ignore’, and run xfun::rev_check(pkg)
again to recheck the
failed packages. Repeat this process until all ‘*.Rcheck’ directories
are gone.
As an example, I set options(repos = c(CRAN = 'https://cran.rstudio.com'), xfun.rev_check.src_dir = '~/Dropbox/repo')
in
‘.Rprofile’, and R_LIBS_USER=~/R-tmp
in ‘.Renviron’. Then I
can run, for example, xfun::rev_check('knitr')
repeatedly under a
special directory ‘~/Downloads/revcheck’. Reverse dependencies and their
dependencies will be installed to ‘~/R-tmp’, and knitr will be
installed from ‘~/Dropbox/repo/kintr’.
A named numeric vector with the names being package names of reverse
dependencies; 0
indicates check success, 1
indicates failure,
and 2
indicates that a package was not checked due to global
timeout.
devtools::revdep_check()
is more sophisticated, but currently
has a few major issues that affect me: (1) It always deletes the
‘*.Rcheck’ directories
(https://github.com/r-lib/devtools/issues/1395), which makes it
difficult to know more information about the failures; (2) It does not
fully install the source package before checking its reverse dependencies
(https://github.com/r-lib/devtools/pull/1397); (3) I feel it is
fairly difficult to iterate the check (ignore the successful packages and
only check the failed packages); by comparison, xfun::rev_check()
only requires you to run a short command repeatedly (failed packages are
indicated by the existing ‘*.Rcheck’ directories, and automatically
checked again the next time).
xfun::rev_check()
borrowed a very nice feature from
devtools::revdep_check()
: estimating and displaying the remaining
time. This is particularly useful for packages with huge numbers of reverse
dependencies.
Rscript
and R CMD
Wrapper functions to run the commands Rscript
and R CMD
.
Rscript(args, ...) Rcmd(args, ...)
Rscript(args, ...) Rcmd(args, ...)
args |
A character vector of command-line arguments. |
... |
Other arguments to be passed to |
A value returned by system2()
.
library(xfun) Rscript(c("-e", "1+1")) Rcmd(c("build", "--help"))
library(xfun) Rscript(c("-e", "1+1")) Rcmd(c("build", "--help"))
Rscript()
Save the argument values of a function in a temporary RDS file, open a new R
session via Rscript()
, read the argument values, call the function, and
read the returned value back to the current R session.
Rscript_call( fun, args = list(), options = NULL, ..., wait = TRUE, fail = sprintf("Failed to run '%s' in a new R session", deparse(substitute(fun))[1]) )
Rscript_call( fun, args = list(), options = NULL, ..., wait = TRUE, fail = sprintf("Failed to run '%s' in a new R session", deparse(substitute(fun))[1]) )
fun |
A function, or a character string that can be parsed and evaluated to a function. |
args |
A list of argument values. |
options |
A character vector of options to passed to |
... , wait
|
Arguments to be passed to |
fail |
The desired error message when an error occurred in calling the function. If the actual error message during running the function is available, it will be appended to this message. |
If wait = TRUE
, the returned value of the function in the new R
session. If wait = FALSE
, three file paths will be returned: the first
one stores fun
and args
(as a list), the second one is supposed to
store the returned value of the function, and the third one stores the
possible error message.
factorial(10) # should return the same value xfun::Rscript_call("factorial", list(10)) # the first argument can be either a character string or a function xfun::Rscript_call(factorial, list(10)) # Run Rscript starting a vanilla R session xfun::Rscript_call(factorial, list(10), options = c("--vanilla"))
factorial(10) # should return the same value xfun::Rscript_call("factorial", list(10)) # the first argument can be either a character string or a function xfun::Rscript_call(factorial, list(10)) # Run Rscript starting a vanilla R session xfun::Rscript_call(factorial, list(10), options = c("--vanilla"))
Use the rstudioapi package to insert characters one by one into the RStudio source editor, as if they were typed by a human.
rstudio_type(x, pause = function() 0.1, mistake = 0, save = 0)
rstudio_type(x, pause = function() 0.1, mistake = 0, save = 0)
x |
A character vector. |
pause |
A function to return a number in seconds to pause after typing each character. |
mistake |
The probability of making random mistakes when typing the next character. A random mistake is a random string typed into the editor and deleted immediately. |
save |
The probability of saving the document after typing each character. Note that If a document is not opened from a file, it will never be saved. |
library(xfun) if (loadable("rstudioapi") && rstudioapi::isAvailable()) { rstudio_type("Hello, RStudio! xfun::rstudio_type() looks pretty cool!", pause = function() runif(1, 0, 0.5), mistake = 0.1) }
library(xfun) if (loadable("rstudioapi") && rstudioapi::isAvailable()) { rstudio_type("Hello, RStudio! xfun::rstudio_type() looks pretty cool!", pause = function() runif(1, 0, 0.5), mistake = 0.1) }
Compare two paths after normalizing them with the same separator (/
).
same_path(p1, p2, ...)
same_path(p1, p2, ...)
p1 , p2
|
Two vectors of paths. |
... |
Arguments to be passed to |
library(xfun) same_path("~/foo", file.path(Sys.getenv("HOME"), "foo"))
library(xfun) same_path("~/foo", file.path(Sys.getenv("HOME"), "foo"))
This function tweaks the output of sessionInfo()
: (1) It adds
the RStudio version information if running in the RStudio IDE; (2) It removes
the information about matrix products, BLAS, and LAPACK; (3) It removes the
names of base R packages; (4) It prints out package versions in a single
group, and does not differentiate between loaded and attached packages.
session_info(packages = NULL, dependencies = TRUE)
session_info(packages = NULL, dependencies = TRUE)
packages |
A character vector of package names, of which the versions will be printed. If not specified, it means all loaded and attached packages in the current R session. |
dependencies |
Whether to print out the versions of the recursive dependencies of packages. |
It also allows you to only print out the versions of specified packages (via
the packages
argument) and optionally their recursive dependencies.
For these specified packages (if provided), if a function
xfun_session_info()
exists in a package, it will be called and
expected to return a character vector to be appended to the output of
session_info()
. This provides a mechanism for other packages to inject
more information into the session_info
output. For example,
rmarkdown (>= 1.20.2) has a function xfun_session_info()
that
returns the version of Pandoc, which can be very useful information for
diagnostics.
A character vector of the session information marked as
raw_string()
.
xfun::session_info() if (xfun::loadable("MASS")) xfun::session_info("MASS")
xfun::session_info() if (xfun::loadable("MASS")) xfun::session_info("MASS")
Set environment variables from a named character vector, and return the old values of the variables, so they could be restored later.
set_envvar(vars)
set_envvar(vars)
vars |
A named character vector of the form |
The motivation of this function is that Sys.setenv()
does not
return the old values of the environment variables, so it is not
straightforward to restore the variables later.
Old values of the variables (if not set, NA
).
vars = xfun::set_envvar(c(FOO = "1234")) Sys.getenv("FOO") xfun::set_envvar(vars) Sys.getenv("FOO")
vars = xfun::set_envvar(c(FOO = "1234")) Sys.getenv("FOO") xfun::set_envvar(vars) Sys.getenv("FOO")
Use magick::image_resize()
to shrink an
image if its width is larger than the value specified by the argument
width
, and optionally call tinify()
to compress it.
shrink_images( width = 800, dir = ".", files = all_files("[.](png|jpe?g|webp)$", dir), tinify = FALSE )
shrink_images( width = 800, dir = ".", files = all_files("[.](png|jpe?g|webp)$", dir), tinify = FALSE )
width |
The desired maximum width of images. |
dir |
The directory of images. |
files |
A vector of image file paths. By default, this is all
‘.png’, ‘.jpeg’, and ‘.webp’ images under |
tinify |
Whether to compress images using |
f = xfun:::all_files("[.](png|jpe?g)$", R.home("doc")) file.copy(f, tempdir()) f = file.path(tempdir(), basename(f)) magick::image_info(magick::image_read(f)) # some widths are larger than 300 xfun::shrink_images(300, files = f) magick::image_info(magick::image_read(f)) # all widths <= 300 now file.remove(f)
f = xfun:::all_files("[.](png|jpe?g)$", R.home("doc")) file.copy(f, tempdir()) f = file.path(tempdir(), basename(f)) magick::image_info(magick::image_read(f)) # some widths are larger than 300 xfun::shrink_images(300, files = f) magick::image_info(magick::image_read(f)) # all widths <= 300 now file.remove(f)
Call unlist(strsplit(x, '\n'))
on the character vector x
and
make sure it works in a few edge cases: split_lines('')
returns
''
instead of character(0)
(which is the returned value of
strsplit('', '\n')
); split_lines('a\n')
returns c('a', '')
instead of c('a')
(which is the returned value of
strsplit('a\n', '\n')
.
split_lines(x)
split_lines(x)
x |
A character vector. |
All elements of the character vector are split by '\n'
into
lines.
xfun::split_lines(c("a", "b\nc"))
xfun::split_lines(c("a", "b\nc"))
Parse the lines of code one by one to find complete expressions in the code, and put them in a list.
split_source(x, merge_comments = FALSE, line_number = FALSE)
split_source(x, merge_comments = FALSE, line_number = FALSE)
x |
A character vector of R source code. |
merge_comments |
Whether to merge consecutive lines of comments as a single expression to be combined with the next non-comment expression (if any). |
line_number |
Whether to store the line numbers of each expression in the returned value. |
A list of character vectors, and each vector contains a complete R
expression, with an attribute lines
indicating the starting and ending
line numbers of the expression if the argument line_number = TRUE
.
code = c("# comment 1", "# comment 2", "if (TRUE) {", "1 + 1", "}", "print(1:5)") xfun::split_source(code) xfun::split_source(code, merge_comments = TRUE)
code = c("# comment 1", "# comment 2", "if (TRUE) {", "1 + 1", "}", "print(1:5)") xfun::split_source(code) xfun::split_source(code, merge_comments = TRUE)
A wrapper function to make strwrap()
return a character vector of the same
length as the input vector; each element of the output vector is a string
formed by concatenating wrapped strings by \n
.
str_wrap(...)
str_wrap(...)
... |
Arguments passed to |
A character vector.
x = sample(c(letters, " "), 200, TRUE, c(rep(0.5/26, 26), 0.5)) x = rep(paste(x, collapse = ""), 2) strwrap(x, width = 30) xfun::str_wrap(x, width = 30) # same length as x
x = sample(c(letters, " "), 200, TRUE, c(rep(0.5/26, 26), 0.5)) x = rep(paste(x, collapse = ""), 2) strwrap(x, width = 30) xfun::str_wrap(x, width = 30) # same length as x
A strict list is essentially a normal list()
but it does not
allow partial matching with $
.
strict_list(...) as_strict_list(x) ## S3 method for class 'xfun_strict_list' x$name ## S3 method for class 'xfun_strict_list' print(x, ...)
strict_list(...) as_strict_list(x) ## S3 method for class 'xfun_strict_list' x$name ## S3 method for class 'xfun_strict_list' print(x, ...)
... |
Objects (list elements), possibly named. Ignored in the
|
x |
For For |
name |
The name (a character string) of the list element. |
To me, partial matching is often more annoying and surprising than
convenient. It can lead to bugs that are very hard to discover, and I have
been bitten by it many times. When I write x$name
, I always mean
precisely name
. You should use a modern code editor to autocomplete
the name
if it is too long to type, instead of using partial names.
Both strict_list()
and as_strict_list()
return a list
with the class xfun_strict_list
. Whereas as_strict_list()
attempts to coerce its argument x
to a list if necessary,
strict_list()
just wraps its argument ...
in a list, i.e., it
will add another list level regardless if ...
already is of type
list.
library(xfun) (z = strict_list(aaa = "I am aaa", b = 1:5)) z$a # NULL! z$aaa # I am aaa z$b z$c = "create a new element" z2 = unclass(z) # a normal list z2$a # partial matching z3 = as_strict_list(z2) # a strict list again z3$a # NULL again!
library(xfun) (z = strict_list(aaa = "I am aaa", b = 1:5)) z$a # NULL! z$aaa # I am aaa z$b z$c = "create a new element" z2 = unclass(z) # a normal list z2$a # partial matching z3 = as_strict_list(z2) # a strict list again z3$a # NULL again!
Remove HTML tags and comments from text.
strip_html(x)
strip_html(x)
x |
A character vector. |
A character vector with HTML tags and comments stripped off.
xfun::strip_html("<a href=\"#\">Hello <!-- comment -->world!</a>")
xfun::strip_html("<a href=\"#\">Hello <!-- comment -->world!</a>")
Build a source package and submit it to CRAN with the curl package.
submit_cran(file = pkg_build(), comment = "")
submit_cran(file = pkg_build(), comment = "")
file |
The path to the source package tarball. By default, the current
working directory is treated as the package root directory, and
automatically built into a tarball, which is deleted after submission. This
means you should run |
comment |
Submission comments for CRAN. By default, if a file ‘cran-comments.md’ exists, its content will be read and used as the comment. |
devtools::submit_cran()
does the same job, with a few more
dependencies in addition to curl (such as cli);
xfun::submit_cran()
only depends on curl.
system2()
and mark its character output as UTF-8 if appropriateThis is a wrapper function based on system2()
. If system2()
returns character output (e.g., with the argument stdout = TRUE
),
check if the output is encoded in UTF-8. If it is, mark it with UTF-8
explicitly.
system3(...)
system3(...)
... |
Passed to |
The value returned by system2()
.
a = shQuote(c("-e", "print(intToUtf8(c(20320, 22909)))")) x2 = system2("Rscript", a, stdout = TRUE) Encoding(x2) # unknown x3 = xfun::system3("Rscript", a, stdout = TRUE) # encoding of x3 should be UTF-8 if the current locale is UTF-8 !l10n_info()[["UTF-8"]] || Encoding(x3) == "UTF-8" # should be TRUE
a = shQuote(c("-e", "print(intToUtf8(c(20320, 22909)))")) x2 = system2("Rscript", a, stdout = TRUE) Encoding(x2) # unknown x3 = xfun::system3("Rscript", a, stdout = TRUE) # encoding of x3 should be UTF-8 if the current locale is UTF-8 !l10n_info()[["UTF-8"]] || Encoding(x3) == "UTF-8" # should be TRUE
The tab titles are names of list members, and the tab content contains the values of list members. If a list member is also a list, it will be represented recursively with a child tabset.
tabset(x, value = str)
tabset(x, value = str)
x |
A list. |
value |
A function to print the value of a list member. By default,
|
A character vector of Markdown that can be rendered to HTML with
litedown::mark()
.
xfun::tabset(iris) xfun::tabset(iris, dput) xfun::tabset(iris, print) # a deeply nested list plot(1:10) p = recordPlot() xfun::tabset(p)
xfun::tabset(iris) xfun::tabset(iris, dput) xfun::tabset(iris, print) # a deeply nested list plot(1:10) p = recordPlot() xfun::tabset(p)
TAML is a tiny subset of YAML. See https://yihui.org/litedown/#sec:yaml-syntax for its specifications.
taml_load(x, envir = parent.frame()) taml_file(path) taml_save(x, path = NULL, indent = " ")
taml_load(x, envir = parent.frame()) taml_file(path) taml_save(x, path = NULL, indent = " ")
x |
For |
envir |
The environment in which R expressions in YAML are evaluated. To
disable the evaluation, use |
path |
A file path to read from or write to. |
indent |
A character string to indent sub-lists by one level. |
taml_load()
and taml_file()
return a list; if path = NULL
,
taml_save()
returns a character vector, otherwise the vector is written
to the file specified by the path
.
(res = taml_load("a: 1")) taml_save(res) (res = taml_load("a: 1\nb: \"foo\"\nc: null")) taml_save(res) (res = taml_load("a:\n b: false\n c: true\n d: 1.234\ne: bar")) taml_save(res) taml_save(res, indent = "\t") taml_load("a: !expr paste(1:10, collapse = \", \")") taml_load("a: [1, 3, 4, 2]") taml_load("a: [1, \"abc\", 4, 2]") taml_load("a: [\"foo\", \"bar\"]") taml_load("a: [true, false, true]") # the other form of array is not supported taml_load("a:\n - b\n - c") # and you must use the yaml package if (loadable("yaml")) yaml_load("a:\n - b\n - c")
(res = taml_load("a: 1")) taml_save(res) (res = taml_load("a: 1\nb: \"foo\"\nc: null")) taml_save(res) (res = taml_load("a:\n b: false\n c: true\n d: 1.234\ne: bar")) taml_save(res) taml_save(res, indent = "\t") taml_load("a: !expr paste(1:10, collapse = \", \")") taml_load("a: [1, 3, 4, 2]") taml_load("a: [1, \"abc\", 4, 2]") taml_load("a: [\"foo\", \"bar\"]") taml_load("a: [true, false, true]") # the other form of array is not supported taml_load("a:\n - b\n - c") # and you must use the yaml package if (loadable("yaml")) yaml_load("a:\n - b\n - c")
Compress PNG/JPEG images with ‘api.tinify.com’, and download the
compressed images. These functions require R packages curl and
jsonlite. tinify_dir()
is a wrapper function of tinify()
to
compress images under a directory.
tinify( input, output, quiet = FALSE, force = FALSE, key = env_option("xfun.tinify.key"), history = env_option("xfun.tinify.history") ) tinify_dir(dir = ".", ...)
tinify( input, output, quiet = FALSE, force = FALSE, key = env_option("xfun.tinify.key"), history = env_option("xfun.tinify.history") ) tinify_dir(dir = ".", ...)
input |
A vector of input paths of images. |
output |
A vector of output paths or a function that takes |
quiet |
Whether to suppress detailed information about the compression,
which is of the form ‘input.png (10 Kb) ==> output.png (5 Kb, 50%);
compression count: 42’. The percentage after |
force |
Whether to compress an image again when it appears to have been
compressed before. This argument only makes sense when the |
key |
The Tinify API key. It can be set via either the global option
|
history |
Path to a history file to record the MD5 checksum of
compressed images. If the checksum of an expected output image exists in
this file and |
dir |
A directory under which all ‘.png’, ‘.jpeg’, and ‘.webp’ files are to be compressed. |
... |
Arguments passed to |
You are recommended to set the API key in ‘.Rprofile’ or
‘.Renviron’. After that, the only required argument of this function is
input
. If the original images can be overwritten by the compressed images,
you may either use output = identity
, or set the value of the history
argument in ‘.Rprofile’ or ‘.Renviron’.
The output file paths.
Tinify API: https://tinypng.com/developers.
The tinieR package (https://github.com/jmablog/tinieR/) is a
more comprehensive implementation of the Tinify API, whereas
xfun::tinify()
has only implemented the feature of shrinking images.
f = xfun:::R_logo("jpg$") xfun::tinify(f) # remember to set the API key before trying this
f = xfun:::R_logo("jpg$") xfun::tinify(f) # remember to set the API key before trying this
A JSON serializer that only works on a limited types of R data (NULL
,
lists, logical scalars, character/numeric vectors). A character string of the
class JS_EVAL
is treated as raw JavaScript, so will not be quoted. The
function json_vector()
converts an atomic R vector to JSON.
tojson(x) json_vector(x, to_array = FALSE, quote = TRUE)
tojson(x) json_vector(x, to_array = FALSE, quote = TRUE)
x |
An R object. |
to_array |
Whether to convert a vector to a JSON array (use |
quote |
Whether to double quote the elements. |
A character string.
The jsonlite package provides a full JSON serializer.
library(xfun) tojson(NULL) tojson(1:10) tojson(TRUE) tojson(FALSE) cat(tojson(list(a = 1, b = list(c = 1:3, d = "abc")))) cat(tojson(list(c("a", "b"), 1:5, TRUE))) # the class JS_EVAL is originally from htmlwidgets::JS() JS = function(x) structure(x, class = "JS_EVAL") cat(tojson(list(a = 1:5, b = JS("function() {return true;}"))))
library(xfun) tojson(NULL) tojson(1:10) tojson(TRUE) tojson(FALSE) cat(tojson(list(a = 1, b = list(c = 1:3, d = "abc")))) cat(tojson(list(c("a", "b"), 1:5, TRUE))) # the class JS_EVAL is originally from htmlwidgets::JS() JS = function(x) structure(x, class = "JS_EVAL") cat(tojson(list(a = 1:5, b = JS("function() {return true;}"))))
str()
into a tree diagramThe super useful function str()
uses ..
to indicate the level
of sub-elements of an object, which may be difficult to read. This function
uses vertical pipes to connect all sub-elements on the same level, so it is
clearer which elements belong to the same parent element in an object with a
nested structure (such as a nested list).
tree(...)
tree(...)
... |
Arguments to be passed to |
A character string as a raw_string()
.
fit = lsfit(1:9, 1:9) str(fit) xfun::tree(fit) fit = lm(dist ~ speed, data = cars) str(fit) xfun::tree(fit) # some trivial examples xfun::tree(1:10) xfun::tree(iris)
fit = lsfit(1:9, 1:9) str(fit) xfun::tree(fit) fit = lm(dist ~ speed, data = cars) str(fit) xfun::tree(fit) # some trivial examples xfun::tree(1:10) xfun::tree(iris)
Use tryCatch()
to check if an expression throws an error.
try_error(expr)
try_error(expr)
expr |
An R expression. |
TRUE
(error) or FALSE
(success).
xfun::try_error(stop("foo")) # TRUE xfun::try_error(1:10) # FALSE
xfun::try_error(stop("foo")) # TRUE xfun::try_error(1:10) # FALSE
An abbreviation of try(silent = TRUE)
.
try_silent(expr)
try_silent(expr)
expr |
An R expression. |
library(xfun) z = try_silent(stop("Wrong!")) inherits(z, "try-error")
library(xfun) z = try_silent(stop("Wrong!")) inherits(z, "try-error")
curl
The function upload_ftp()
runs the command curl -T file
server
to upload a file to an FTP server if the system command
curl
is available, otherwise it uses the R package curl. The
function upload_win_builder()
uses upload_ftp()
to upload
packages to the win-builder server.
upload_ftp(file, server, dir = "") upload_win_builder( file = pkg_build(), version = c("R-devel", "R-release", "R-oldrelease"), server = c("ftp", "https"), solaris = pkg_available("rhub") )
upload_ftp(file, server, dir = "") upload_win_builder( file = pkg_build(), version = c("R-devel", "R-release", "R-oldrelease"), server = c("ftp", "https"), solaris = pkg_available("rhub") )
file |
Path to a local file. |
server |
The address of the FTP server. For |
dir |
The remote directory to which the file should be uploaded. |
version |
The R version(s) on win-builder. |
solaris |
Whether to also upload the package to the Rhub server to check it on Solaris. |
These functions were written mainly to save package developers the trouble of going to the win-builder web page and uploading packages there manually.
Status code returned from system2()
or
curl::curl_fetch_memory()
.
This function uses the curl package or the system command curl
(whichever is available) to upload a image to https://imgur.com.
upload_imgur( file, key = env_option("xfun.upload_imgur.key", "9f3460e67f308f6"), use_curl = loadable("curl"), include_xml = FALSE )
upload_imgur( file, key = env_option("xfun.upload_imgur.key", "9f3460e67f308f6"), use_curl = loadable("curl"), include_xml = FALSE )
file |
Path to the image file to be uploaded. |
key |
Client ID for Imgur. It can be set via either the global option
|
use_curl |
Whether to use the R package curl to upload the image.
If |
include_xml |
Whether to include the XML response in the returned value. |
One application is to upload local image files to Imgur when knitting a
document with knitr: you can set the knitr::opts_knit$set(upload.fun = xfun::upload_imgur
, so the output document does not need local image files
any more, and it is ready to be published online.
A character string of the link to the image. If include_xml = TRUE
,
this string carries an attribute named XML
, which is the XML response
from Imgur (it will be parsed by xml2 if available). See Imgur API in
the references.
Please register your own Imgur application to get your client ID; you can certainly use mine, but this ID is in the public domain so everyone has access to all images associated to it.
Yihui Xie, adapted from the imguR package by Aaron Statham
A demo: https://yihui.org/knitr/demo/upload/
## Not run: f = tempfile(fileext = ".png") png(f) plot(rnorm(100), main = R.version.string) dev.off() res = imgur_upload(f, include_xml = TRUE) res # link to original URL of the image attr(res, "XML") # all information if (interactive()) browseURL(res) # to use your own key options(xfun.upload_imgur.key = "your imgur key") ## End(Not run)
## Not run: f = tempfile(fileext = ".png") png(f) plot(rnorm(100), main = R.version.string) dev.off() res = imgur_upload(f, include_xml = TRUE) res # link to original URL of the image attr(res, "XML") # all information if (interactive()) browseURL(res) # to use your own key options(xfun.upload_imgur.key = "your imgur key") ## End(Not run)
Try to send a HEAD
request to a URL using
curlGetHeaders()
or the curl package, and see if it
returns a successful status code.
url_accessible(x, use_curl = !capabilities("libcurl"), ...)
url_accessible(x, use_curl = !capabilities("libcurl"), ...)
x |
A URL as a character string. |
use_curl |
Whether to use the curl package or the
|
... |
Arguments to be passed to |
TRUE
or FALSE
.
xfun::url_accessible("https://yihui.org")
xfun::url_accessible("https://yihui.org")
Get the base names of URLs via basename()
, and remove the
possible query parameters or hash from the names.
url_filename(x, default = "index.html")
url_filename(x, default = "index.html")
x |
A character vector of URLs. |
default |
The default filename when it cannot be determined from the URL, e.g., when the URL ends with a slash. |
A character vector of filenames at the end of URLs.
xfun::url_filename("https://yihui.org/images/logo.png") xfun::url_filename("https://yihui.org/index.html") xfun::url_filename("https://yihui.org/index.html?foo=bar") xfun::url_filename("https://yihui.org/index.html#about") xfun::url_filename("https://yihui.org") xfun::url_filename("https://yihui.org/")
xfun::url_filename("https://yihui.org/images/logo.png") xfun::url_filename("https://yihui.org/index.html") xfun::url_filename("https://yihui.org/index.html?foo=bar") xfun::url_filename("https://yihui.org/index.html#about") xfun::url_filename("https://yihui.org") xfun::url_filename("https://yihui.org/")
Try to parse()
the code and see if an error occurs.
valid_syntax(code, silent = TRUE)
valid_syntax(code, silent = TRUE)
code |
A character vector of R source code. |
silent |
Whether to suppress the error message when the code is not valid. |
TRUE
if the code could be parsed, otherwise FALSE
.
xfun::valid_syntax("1+1") xfun::valid_syntax("1+") xfun::valid_syntax(c("if(T){1+1}", "else {2+2}"), silent = FALSE)
xfun::valid_syntax("1+1") xfun::valid_syntax("1+") xfun::valid_syntax(c("if(T){1+1}", "else {2+2}"), silent = FALSE)
Split a document into the YAML metadata (which starts with ---
in the
beginning of the document) and the body.
yaml_body(x, ..., parse = TRUE)
yaml_body(x, ..., parse = TRUE)
x |
A character vector of the document content. |
... |
Arguments to be passed to |
parse |
Whether to parse the YAML data. |
A list of components yaml
(the YAML data), lines
(starting and
ending line numbers of YAML), and body
(a character vector of the body
text). If YAML metadata does not exist in the document, the components
yaml
and lines
will be missing.
xfun::yaml_body(c("---", "title: Hello", "output: litedown::html_format", "---", "", "Content."))
xfun::yaml_body(c("---", "title: Hello", "output: litedown::html_format", "---", "", "Content."))
If the yaml package is installed, use yaml::yaml.load()
to read the
data. If not, use the simple parser taml_load()
instead.
yaml_load( x, ..., handlers = NULL, envir = parent.frame(), use_yaml = loadable("yaml") )
yaml_load( x, ..., handlers = NULL, envir = parent.frame(), use_yaml = loadable("yaml") )
x |
A character vector of YAML data. |
... , handlers
|
Arguments to be passed to |
envir |
The environment in which R expressions in YAML are evaluated. To
disable the evaluation, use |
use_yaml |
Whether to use the yaml package. |
An R object (typically a list).
R expressions in YAML will be returned as expressions when they are
not evaluated. This is different with yaml::yaml.load()
, which returns
character strings for expressions.
yaml_load("a: 1") yaml_load("a: 1", use_yaml = FALSE)
yaml_load("a: 1") yaml_load("a: 1", use_yaml = FALSE)