Skip to content

Commit

Permalink
Merge pull request #422 from ramnathv/fast-staticimports
Browse files Browse the repository at this point in the history
  • Loading branch information
cpsievert authored Nov 17, 2021
2 parents a3aa2b1 + b29cfd6 commit 7b9c1ea
Show file tree
Hide file tree
Showing 6 changed files with 221 additions and 140 deletions.
18 changes: 1 addition & 17 deletions R/htmlwidgets.R
Original file line number Diff line number Diff line change
Expand Up @@ -476,13 +476,9 @@ createWidget <- function(name,
shinyWidgetOutput <- function(outputId, name, width, height, package = name,
inline = FALSE, reportSize = FALSE, reportTheme = FALSE) {

checkShinyVersion()

# Theme reporting requires this shiny feature
# https://github.com/rstudio/shiny/pull/2740/files
if (reportTheme &&
nzchar(system.file(package = "shiny")) &&
packageVersion("shiny") < "1.4.0.9003") {
if (reportTheme && !is_installed("shiny", "1.4.0.9003")) {
message("`reportTheme = TRUE` requires shiny v.1.4.0.9003 or higher. Consider upgrading shiny.")
}

Expand Down Expand Up @@ -512,7 +508,6 @@ shinyWidgetOutput <- function(outputId, name, width, height, package = name,
#' @rdname htmlwidgets-shiny
#' @export
shinyRenderWidget <- function(expr, outputFunction, env, quoted, cacheHint = "auto") {
checkShinyVersion()
# generate a function for the expression
shiny::installExprFunction(expr, "func", env, quoted)

Expand Down Expand Up @@ -588,17 +583,6 @@ shinyRenderWidget <- function(expr, outputFunction, env, quoted, cacheHint = "au
# For the magic behind shiny::installExprFunction()
utils::globalVariables("func")

checkShinyVersion <- function(error = TRUE) {
x <- utils::packageDescription('htmlwidgets', fields = 'Enhances')
r <- '^.*?shiny \\(>= ([0-9.]+)\\).*$'
if (is.na(x) || length(grep(r, x)) == 0 || system.file(package = 'shiny') == '')
return()
v <- gsub(r, '\\1', x)
f <- if (error) stop else packageStartupMessage
if (utils::packageVersion('shiny') < v)
f("Please upgrade the 'shiny' package to (at least) version ", v)
}

# Helper function to create payload
createPayload <- function(instance){
if (!is.null(instance$preRenderHook)){
Expand Down
40 changes: 2 additions & 38 deletions R/knitr-methods.R
Original file line number Diff line number Diff line change
@@ -1,44 +1,8 @@
# Reusable function for registering a set of methods with S3 manually. The
# methods argument is a list of character vectors, each of which has the form
# c(package, genname, class).
registerMethods <- function(methods) {
lapply(methods, function(method) {
pkg <- method[[1]]
generic <- method[[2]]
class <- method[[3]]
func <- get(paste(generic, class, sep="."))
if (pkg %in% loadedNamespaces()) {
registerS3method(generic, class, func, envir = asNamespace(pkg))
}
setHook(
packageEvent(pkg, "onLoad"),
function(...) {
registerS3method(generic, class, func, envir = asNamespace(pkg))
}
)
})
}

.onLoad <- function(...) {
# htmlwidgets provides methods for knitr::knit_print, but knitr isn't a Depends or
# Imports of htmltools, only an Enhances. Therefore, the NAMESPACE file has to
# declare it as an export, not an S3method. That means that R will only know to
# use our methods if htmlwidgets is actually attached, i.e., you have to use
# library(htmlwidgets) in a knitr document or else you'll get escaped HTML in your
# document. This code snippet manually registers our method(s) with S3 once both
# htmlwidgets and knitr are loaded.
registerMethods(list(
# c(package, genname, class)
c("knitr", "knit_print", "htmlwidget")
))
}

.onAttach <- function(...) {
# warn if the version of shiny is lower than what was specified in DESCRIPTION
checkShinyVersion(error = FALSE)
s3_register("knitr::knit_print", "htmlwidget")
register_upgrade_message("shiny", "1.1", error = TRUE)
}

knit_print.htmlwidget <- function(x, ..., options = NULL) {
knitr::knit_print(toHTML(x, standalone = FALSE, knitrOptions = options), options = options, ...)
}

4 changes: 2 additions & 2 deletions R/scaffold.R
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ scaffoldWidget <- function(name, bowerPkg = NULL, edit = interactive()){

addWidgetConstructor <- function(name, package, edit){
tpl <- paste(readLines(
system.file('templates/widget_r.txt', package = 'htmlwidgets')
system_file('templates/widget_r.txt', package = 'htmlwidgets')
), collapse = "\n")

capName = function(name){
Expand Down Expand Up @@ -80,7 +80,7 @@ addWidgetYAML <- function(name, bowerPkg, edit){

addWidgetJS <- function(name, edit){
tpl <- paste(readLines(
system.file('templates/widget_js.txt', package = 'htmlwidgets')
system_file('templates/widget_js.txt', package = 'htmlwidgets')
), collapse = "\n")

if (!file.exists(file_ <- sprintf('inst/htmlwidgets/%s.js', name))){
Expand Down
62 changes: 0 additions & 62 deletions R/shim.R

This file was deleted.

204 changes: 204 additions & 0 deletions R/staticimports.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
# Generated by staticimports; do not edit by hand.
# ======================================================================
# Imported from pkg:staticimports
# ======================================================================

`%||%` <- function(a, b) {
if (is.null(a)) b else a
}

# Borrowed from pkgload:::dev_meta, with some modifications.
# Returns TRUE if `pkg` was loaded with `devtools::load_all()`.
devtools_loaded <- function(pkg) {
ns <- .getNamespace(pkg)
if (is.null(ns) || is.null(ns$.__DEVTOOLS__)) {
return(FALSE)
}
TRUE
}

get_package_version <- function(pkg) {
# `utils::packageVersion()` can be slow, so first try the fast path of
# checking if the package is already loaded.
ns <- .getNamespace(pkg)
if (is.null(ns)) {
utils::packageVersion(pkg)
} else {
as.package_version(ns$.__NAMESPACE__.$spec[["version"]])
}
}

is_installed <- function(pkg, version = NULL) {
installed <- isNamespaceLoaded(pkg) || nzchar(system_file_cached(package = pkg))
if (is.null(version)) {
return(installed)
}
installed && isTRUE(get_package_version(pkg) >= version)
}

register_upgrade_message <- function(pkg, version, error = FALSE) {

msg <- sprintf(
"This version of '%s' is designed to work with '%s' >= %s.
Please upgrade via install.packages('%s').",
environmentName(environment(register_upgrade_message)),
pkg, version, pkg
)

cond <- if (error) stop else packageStartupMessage

if (pkg %in% loadedNamespaces() && !is_installed(pkg, version)) {
cond(msg)
}

# Always register hook in case pkg is loaded at some
# point the future (or, potentially, but less commonly,
# unloaded & reloaded)
setHook(
packageEvent(pkg, "onLoad"),
function(...) {
if (!is_installed(pkg, version)) cond(msg)
}
)
}

# Simplified version rlang:::s3_register() that just uses
# warning() instead of rlang::warn() when registration fails
# https://github.com/r-lib/rlang/blob/main/R/compat-s3-register.R
s3_register <- function(generic, class, method = NULL) {
stopifnot(is.character(generic), length(generic) == 1)
stopifnot(is.character(class), length(class) == 1)

pieces <- strsplit(generic, "::")[[1]]
stopifnot(length(pieces) == 2)
package <- pieces[[1]]
generic <- pieces[[2]]

caller <- parent.frame()

get_method_env <- function() {
top <- topenv(caller)
if (isNamespace(top)) {
asNamespace(environmentName(top))
} else {
caller
}
}
get_method <- function(method, env) {
if (is.null(method)) {
get(paste0(generic, ".", class), envir = get_method_env())
} else {
method
}
}

register <- function(...) {
envir <- asNamespace(package)

# Refresh the method each time, it might have been updated by
# `devtools::load_all()`
method_fn <- get_method(method)
stopifnot(is.function(method_fn))

# Only register if generic can be accessed
if (exists(generic, envir)) {
registerS3method(generic, class, method_fn, envir = envir)
} else {
warning(
"Can't find generic `", generic, "` in package ", package,
" register S3 method. Do you need to update ", package,
" to the latest version?", call. = FALSE
)
}
}

# Always register hook in case package is later unloaded & reloaded
setHook(packageEvent(package, "onLoad"), function(...) {
register()
})

# Avoid registration failures during loading (pkgload or regular).
# Check that environment is locked because the registering package
# might be a dependency of the package that exports the generic. In
# that case, the exports (and the generic) might not be populated
# yet (#1225).
if (isNamespaceLoaded(package) && environmentIsLocked(asNamespace(package))) {
register()
}

invisible()
}

# Borrowed from pkgload::shim_system.file, with some modifications. This behaves
# like `system.file()`, except that (1) for packages loaded with
# `devtools::load_all()`, it will return the path to files in the package's
# inst/ directory, and (2) for other packages, the directory lookup is cached.
# Also, to keep the implementation simple, it doesn't support specification of
# lib.loc or mustWork.
system_file <- function(..., package = "base") {
if (!devtools_loaded(package)) {
return(system_file_cached(..., package = package))
}

if (!is.null(names(list(...)))) {
stop("All arguments other than `package` must be unnamed.")
}

# If package was loaded with devtools (the package loaded with load_all),
# also search for files under inst/, and don't cache the results (it seems
# more likely that the package path will change during the development
# process)
pkg_path <- find.package(package)

# First look in inst/
files_inst <- file.path(pkg_path, "inst", ...)
present_inst <- file.exists(files_inst)

# For any files that weren't present in inst/, look in the base path
files_top <- file.path(pkg_path, ...)
present_top <- file.exists(files_top)

# Merge them together. Here are the different possible conditions, and the
# desired result. NULL means to drop that element from the result.
#
# files_inst: /inst/A /inst/B /inst/C /inst/D
# present_inst: T T F F
# files_top: /A /B /C /D
# present_top: T F T F
# result: /inst/A /inst/B /C NULL
#
files <- files_top
files[present_inst] <- files_inst[present_inst]
# Drop cases where not present in either location
files <- files[present_inst | present_top]
if (length(files) == 0) {
return("")
}
# Make sure backslashes are replaced with slashes on Windows
normalizePath(files, winslash = "/")
}

# A wrapper for `system.file()`, which caches the results, because
# `system.file()` can be slow. Note that because of caching, if
# `system_file_cached()` is called on a package that isn't installed, then the
# package is installed, and then `system_file_cached()` is called again, it will
# still return "".
system_file_cached <- local({
pkg_dir_cache <- character()

function(..., package = "base") {
if (!is.null(names(list(...)))) {
stop("All arguments other than `package` must be unnamed.")
}

not_cached <- is.na(match(package, names(pkg_dir_cache)))
if (not_cached) {
pkg_dir <- system.file(package = package)
pkg_dir_cache[[package]] <<- pkg_dir
} else {
pkg_dir <- pkg_dir_cache[[package]]
}

file.path(pkg_dir, ...)
}
})
Loading

0 comments on commit 7b9c1ea

Please sign in to comment.