Title: | Explore the Historical API of R Packages |
---|---|
Description: | Check when functions were introduced and/or APIs changed in packages, using the 'MRAN' service. |
Authors: | David Hugh-Jones [aut, cre] |
Maintainer: | David Hugh-Jones <[email protected]> |
License: | MIT + file LICENSE |
Version: | 0.1.0.9000 |
Built: | 2024-08-09 02:46:38 UTC |
Source: | https://github.com/hughjonesd/apicheck |
This is a small package to check when functions were introduced and/or APIs changed in packages. It automatically installs different versions of a package in a separate directory and loads them without attaching them.
Packages are cached within a session. To cache packages across sessions, use
set_lib_dir()
to point to a persistent directory.
By default, apicheck`` uses the
remotespackage to install source versions from CRAN. Alternatively, it can use the
versions“ package to install different versions of a package
from https://mran.microsoft.com/. To do this set options(apicheck.use_mran = TRUE)
.
Be aware that functions can take a long time to return, as different versions of a package are installed and/or loaded.
Also, be aware that namespace loading and unloading can be unreliable. If this happens to you, try restarting your session.
Do not try to use apicheck
on itself. This will lead to fiery elephants in the sky.
api_same_at
reports whether a function had the same API at a previous version or date.
api_same_at(fun, version = version_at_date(package, date), package, date = NULL, quiet = TRUE, current_fun = NULL, ...)
api_same_at(fun, version = version_at_date(package, date), package, date = NULL, quiet = TRUE, current_fun = NULL, ...)
fun |
Function name as a character string. |
version |
Version as a character string. If omitted, use the version available at |
package |
Package. Alternatively, specify the function name as e.g. |
date |
Date, as a character string that can be read by |
quiet |
Logical. Try to minimize output from package installation. (Some output comes
from |
current_fun |
Current function for comparison. By default, |
... |
Arguments passed to |
If fun
is not exported at version
, api_same_at
returns FALSE
with a warning.
TRUE
or FALSE
.
## Not run: api_same_at("clipr::write_clip", version = "0.1.1") # equivalently api_same_at("write_clip", "clipr", "0.1.1") ## End(Not run)
## Not run: api_same_at("clipr::write_clip", version = "0.1.1") # equivalently api_same_at("write_clip", "clipr", "0.1.1") ## End(Not run)
This returns packages ordered by date, using either MRAN or
metacran (or rcheology
for R core packages).
Results are cached so as to
relieve pressure on the server. If options("apicheck.use_mran")
is TRUE
(and package
is non-core),
then only versions available on MRAN (i.e. after 2014-09-17) will be returned;
otherwise older versions will be returned too.
available_versions(package)
available_versions(package)
package |
Package name. |
A data frame with columns "version" and "date".
In my limited experience, metacran is much faster. YMMV.
## Not run: available_versions("clipr") ## End(Not run)
## Not run: available_versions("clipr") ## End(Not run)
cached_install
checks the package cache, installs the specified version if it is not
already installed, and loads the versioned package namespace.
cached_install(package, version, return = c("namespace", "path"), cache = TRUE, quiet = TRUE, partial = TRUE, ...)
cached_install(package, version, return = c("namespace", "path"), cache = TRUE, quiet = TRUE, partial = TRUE, ...)
package |
Package name. |
version |
Version as a character string. |
return |
Return the file "path" to the installed package, or the "namespace" object? |
cache |
If |
quiet |
Logical. Try to minimize output from package installation. (Some output comes
from |
partial |
Default |
... |
Arguments passed to |
If the package is already loaded, cached_install
will first attempt
to unload it with a warning. This may not always work!
Note that the namespace is not attached. Partial loading is faster and safer when you are (un)loading multiple versions, but does not export functions etc.
The namespace object or directory where the package is installed.
## Not run: cached_install("clipr", "0.4.0") ## End(Not run)
## Not run: cached_install("clipr", "0.4.0") ## End(Not run)
The package is downloaded and installed if necessary, and its namespace is loaded. Then
test(ns)
is called with the namespace object, and its value is returned. On exit, the
namespace is unloaded.
call_with_namespace(package, version, test, quiet = TRUE, ...)
call_with_namespace(package, version, test, quiet = TRUE, ...)
package |
Package name. |
version |
Version as a character string. |
test |
A one-argument function. |
quiet |
Logical. Try to minimize output from package installation. (Some output comes
from |
... |
Arguments passed to |
The value returned by test
.
## Not run: can_it_expand_urls <- function (namespace) "expand_urls" %in% names(namespace) call_with_namespace("longurl", "0.3.0", test = can_it_expand_urls) ## End(Not run)
## Not run: can_it_expand_urls <- function (namespace) "expand_urls" %in% names(namespace) call_with_namespace("longurl", "0.3.0", test = can_it_expand_urls) ## End(Not run)
compare_versions
reports how functions and APIs changed between versions of a package.
compare_versions(package, version = previous_version(package, version2), version2, methods = FALSE, quiet = TRUE, ...) ## S3 method for class 'versions_report' print(x, ...) ## S3 method for class 'versions_report' summary(object, ...)
compare_versions(package, version = previous_version(package, version2), version2, methods = FALSE, quiet = TRUE, ...) ## S3 method for class 'versions_report' print(x, ...) ## S3 method for class 'versions_report' summary(object, ...)
package |
Package name. |
version |
First version to compare. By default, the previous version to that currently installed. |
version2 |
Second version to compare. By default, the current installed version. |
methods |
Logical: include non-exported S3 methods? |
quiet |
Logical. Try to minimize output from package installation. (Some output comes
from |
... |
Arguments passed to |
x , object
|
An object of class |
compare_versions
returns a data frame of class versions_report
,
reporting functions that have been "Added", "Removed" or had "API changed",
and details of function arguments. Extra information is in the "package"
and "versions"
attributes.
summary
returns a string representation of changed function arguments.
## Not run: compare_versions("clipr", "0.2.1", "0.3.0") ## End(Not run)
## Not run: compare_versions("clipr", "0.2.1", "0.3.0") ## End(Not run)
Retrieve a function from a package version
fun_at(fun, version = version_at_date(package, date), package, date = NULL, quiet = TRUE, allow_core = FALSE, ...)
fun_at(fun, version = version_at_date(package, date), package, date = NULL, quiet = TRUE, allow_core = FALSE, ...)
fun |
Function name as a character string. |
version |
Version as a character string. If omitted, use the version available at |
package |
Package. Alternatively, specify the function name as e.g. |
date |
Date, as a character string that can be read by |
quiet |
Logical. Try to minimize output from package installation. (Some output comes
from |
allow_core |
See below. |
... |
Arguments passed to |
By default, you cannot get functions from previous versions of R core packages, which are not available
on CRAN/MRAN. If allow_core
is TRUE
, then a function will be returned from the
rcheology dataset, with a null body but the same formal
arguments as the historical function.
The function itself.
## Not run: fun_at("write_clip", "clipr", "0.1.1") ## End(Not run)
## Not run: fun_at("write_clip", "clipr", "0.1.1") ## End(Not run)
fun_exists_at
reports whether a function exists (i.e. is exported) from a package at a specific previous version
or date.
fun_exists_at(fun, version = version_at_date(package, date), package, date = NULL, quiet = TRUE, ...)
fun_exists_at(fun, version = version_at_date(package, date), package, date = NULL, quiet = TRUE, ...)
fun |
Function name as a character string. |
version |
Version as a character string. If omitted, use the version available at |
package |
Package. Alternatively, specify the function name as e.g. |
date |
Date, as a character string that can be read by |
quiet |
Logical. Try to minimize output from package installation. (Some output comes
from |
... |
Arguments passed to |
TRUE
or FALSE
.
## Not run: fun_exists_at("clipr::dr_clipr", version = "0.3.1") # or fun_exists_at("dr_clipr", "clipr", "0.3.1") ## End(Not run)
## Not run: fun_exists_at("clipr::dr_clipr", version = "0.3.1") # or fun_exists_at("dr_clipr", "clipr", "0.3.1") ## End(Not run)
Get help for a function at a package version
help_at(fun, version = version_at_date(package, date), package, date = NULL, quiet = TRUE, ...)
help_at(fun, version = version_at_date(package, date), package, date = NULL, quiet = TRUE, ...)
fun |
Function name as a character string. |
version |
Version as a character string. If omitted, use the version available at |
package |
Package. Alternatively, specify the function name as e.g. |
date |
Date, as a character string that can be read by |
quiet |
Logical. Try to minimize output from package installation. (Some output comes
from |
... |
Arguments passed to |
The help object (text format only).
## Not run: help_at("clipr::write_clip", "0.1.1") help_at("clipr::write_clip", "0.2.0") ## End(Not run)
## Not run: help_at("clipr::write_clip", "0.1.1") help_at("clipr::write_clip", "0.2.0") ## End(Not run)
package_report
lists all external function calls from a source package
using pkgapi::map_package()
.
It then checks backward-compatibility of each call with previous versions.
package_report(path = ".", include, exclude = core_packages(), cutoff_date = Sys.Date() - 2 * 365, max_n_versions = NULL, parallel = FALSE, progress = !parallel, ...)
package_report(path = ".", include, exclude = core_packages(), cutoff_date = Sys.Date() - 2 * 365, max_n_versions = NULL, parallel = FALSE, progress = !parallel, ...)
path |
Path to the root of an R source package. |
include |
Packages to include. By default, all are included except core packages and the package at
|
exclude |
Packages to exclude from checking. Overrides |
cutoff_date |
Don't check versions before this date (default: 2 years ago). Set to |
max_n_versions |
Check at most this number of previous versions. Set to |
parallel |
Run in parallel. |
progress |
Show a progress bar. |
... |
Arguments passed to |
A data frame with three rows:
package
for the external package
version
for the latest version which caused problems or NA
if there were no problems
funs
a list-column of function names where the API changed
For parallel search, you can set up your own parallel
cluster by using parallel::setDefaultCluster()
; otherwise one will be created, using
getOption("cl.cores")
cores if that is set. If you
set up your own cluster, it will not be stopped automatically (see
parallel::stopCluster()
).
## Not run: package_report(".") # to include base packages also: package_report(".", exclude = character(0)) ## End(Not run)
## Not run: package_report(".") # to include base packages also: package_report(".", exclude = character(0)) ## End(Not run)
set_lib_dir()
specifies where packages will be downloaded to. get_lib_dir()
returns this
directory. clear_lib_dir()
deletes all downloaded packages.
set_lib_dir(lib_dir, create = FALSE) get_lib_dir() clear_lib_dir()
set_lib_dir(lib_dir, create = FALSE) get_lib_dir() clear_lib_dir()
lib_dir |
Path to a directory, or |
create |
Logical. Try to create the directory if it doesn't exist. |
If lib_dir
is set to NULL
, a subdirectory of tempdir()
will be used.
lib_dir
will be normalized via normalizePath()
.
The package cache is under the directory specified by getOption("apicheck.lib_dir")
,
or, if that is unset, in a per-session temporary directory. You should use set_lib_dir()
to change this rather than setting the option directly. Within this directory, subdirectories
are named like package-version
, e.g. longurl-0.3.0
. Within these subdirectories are
the actual installed libraries. So, lib_dir
is not appropriate for passing to functions like
library
. To load a library from the cache yourself,
do e.g. library("blah", lib.loc = file.path(get_lib_dir(), "blah-0.1.0"))
.
set_lib_dir
invisibly returns the old library location, or NULL
if none was set in options.
get_lib_dir
returns the actual library location, whether or not an option has been set.
clear_lib_dir
invisibly returns TRUE if all files and directories could be removed, FALSE otherwise.
## Not run: set_lib_dir("~/.apicheck") ## End(Not run) get_lib_dir() ## Not run: clear_lib_dir() ## End(Not run)
## Not run: set_lib_dir("~/.apicheck") ## End(Not run) get_lib_dir() ## Not run: clear_lib_dir() ## End(Not run)
Return the current version of a package at a given date
version_at_date(package, date)
version_at_date(package, date)
package |
Package. Alternatively, specify the function name as e.g. |
date |
Date, as a character string that can be read by |
A version string.
## Not run: version_at_date("huxtable", "2017-01-01") ## End(Not run)
## Not run: version_at_date("huxtable", "2017-01-01") ## End(Not run)
when_api_same
reports package versions where the API of a function was the same as now (or
the same as current_fun
).
when_fun_exists
reports package versions where a function exists.
when_api_same(fun, package, current_fun = NULL, search = c("binary", "forward", "backward", "all", "parallel"), report = c("full", "brief"), quiet = TRUE, progress = interactive() && search != "parallel", min_version = NULL, max_version = NULL, ...) when_fun_exists(fun, package, search = c("binary", "forward", "backward", "all", "parallel"), report = c("full", "brief"), quiet = TRUE, progress = interactive() && search != "parallel", min_version = NULL, max_version = NULL, ...)
when_api_same(fun, package, current_fun = NULL, search = c("binary", "forward", "backward", "all", "parallel"), report = c("full", "brief"), quiet = TRUE, progress = interactive() && search != "parallel", min_version = NULL, max_version = NULL, ...) when_fun_exists(fun, package, search = c("binary", "forward", "backward", "all", "parallel"), report = c("full", "brief"), quiet = TRUE, progress = interactive() && search != "parallel", min_version = NULL, max_version = NULL, ...)
fun |
Function name as a character string. |
package |
Package. Alternatively, specify the function name as e.g. |
current_fun |
Current function for comparison. By default, |
search |
"binary", "forward", "backward", "all" or "parallel". See Search strategies. |
report |
"brief" or "full". See Value. |
quiet |
Logical. Try to minimize output from package installation. (Some output comes
from |
progress |
Print a progress bar. |
min_version |
Lowest version to check. |
max_version |
Highest version to check. |
... |
Arguments passed to |
"Same API" means having the same function arguments, as reported by formals()
.
If report
is "brief", the earliest "known good" version.
Otherwise, a data frame of versions with a full results column.
This function may download and install multiple versions from MRAN, so it is likely to be slow
when first used (and even afterwards if library loading is slow). Using search = "parallel"
may help, but not if the network is the bottleneck: see
https://hughjonesd.github.io/apicheck/performance2.html for details.
"forward"
("backward"
) searches incrementally from the earliest (latest) version.
"binary"
does a binary search from the midpoint.
These strategies assume that API changes happen just once - i.e. once a function exists or API is the same as now, it will stay so in future versions. This allows them to stop before searching every version.
"all"
searches every version.
"parallel"
searches every version in parallel using parallel::parLapply()
.
For parallel search, you can set up your own parallel
cluster by using parallel::setDefaultCluster()
; otherwise one will be created, using
getOption("cl.cores")
cores if that is set. If you
set up your own cluster, it will not be stopped automatically (see
parallel::stopCluster()
).
## Not run: when_api_same("read.dta", "foreign") ## End(Not run) ## Not run: when_fun_exists('read.dta', 'foreign') ## End(Not run)
## Not run: when_api_same("read.dta", "foreign") ## End(Not run) ## Not run: when_fun_exists('read.dta', 'foreign') ## End(Not run)