Skip to content

Commit 7b9c1ea

Browse files
authored
Merge pull request #422 from ramnathv/fast-staticimports
2 parents a3aa2b1 + b29cfd6 commit 7b9c1ea

File tree

6 files changed

+221
-140
lines changed

6 files changed

+221
-140
lines changed

R/htmlwidgets.R

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -476,13 +476,9 @@ createWidget <- function(name,
476476
shinyWidgetOutput <- function(outputId, name, width, height, package = name,
477477
inline = FALSE, reportSize = FALSE, reportTheme = FALSE) {
478478

479-
checkShinyVersion()
480-
481479
# Theme reporting requires this shiny feature
482480
# https://github.com/rstudio/shiny/pull/2740/files
483-
if (reportTheme &&
484-
nzchar(system.file(package = "shiny")) &&
485-
packageVersion("shiny") < "1.4.0.9003") {
481+
if (reportTheme && !is_installed("shiny", "1.4.0.9003")) {
486482
message("`reportTheme = TRUE` requires shiny v.1.4.0.9003 or higher. Consider upgrading shiny.")
487483
}
488484

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

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

591-
checkShinyVersion <- function(error = TRUE) {
592-
x <- utils::packageDescription('htmlwidgets', fields = 'Enhances')
593-
r <- '^.*?shiny \\(>= ([0-9.]+)\\).*$'
594-
if (is.na(x) || length(grep(r, x)) == 0 || system.file(package = 'shiny') == '')
595-
return()
596-
v <- gsub(r, '\\1', x)
597-
f <- if (error) stop else packageStartupMessage
598-
if (utils::packageVersion('shiny') < v)
599-
f("Please upgrade the 'shiny' package to (at least) version ", v)
600-
}
601-
602586
# Helper function to create payload
603587
createPayload <- function(instance){
604588
if (!is.null(instance$preRenderHook)){

R/knitr-methods.R

Lines changed: 2 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,8 @@
1-
# Reusable function for registering a set of methods with S3 manually. The
2-
# methods argument is a list of character vectors, each of which has the form
3-
# c(package, genname, class).
4-
registerMethods <- function(methods) {
5-
lapply(methods, function(method) {
6-
pkg <- method[[1]]
7-
generic <- method[[2]]
8-
class <- method[[3]]
9-
func <- get(paste(generic, class, sep="."))
10-
if (pkg %in% loadedNamespaces()) {
11-
registerS3method(generic, class, func, envir = asNamespace(pkg))
12-
}
13-
setHook(
14-
packageEvent(pkg, "onLoad"),
15-
function(...) {
16-
registerS3method(generic, class, func, envir = asNamespace(pkg))
17-
}
18-
)
19-
})
20-
}
21-
221
.onLoad <- function(...) {
23-
# htmlwidgets provides methods for knitr::knit_print, but knitr isn't a Depends or
24-
# Imports of htmltools, only an Enhances. Therefore, the NAMESPACE file has to
25-
# declare it as an export, not an S3method. That means that R will only know to
26-
# use our methods if htmlwidgets is actually attached, i.e., you have to use
27-
# library(htmlwidgets) in a knitr document or else you'll get escaped HTML in your
28-
# document. This code snippet manually registers our method(s) with S3 once both
29-
# htmlwidgets and knitr are loaded.
30-
registerMethods(list(
31-
# c(package, genname, class)
32-
c("knitr", "knit_print", "htmlwidget")
33-
))
34-
}
35-
36-
.onAttach <- function(...) {
37-
# warn if the version of shiny is lower than what was specified in DESCRIPTION
38-
checkShinyVersion(error = FALSE)
2+
s3_register("knitr::knit_print", "htmlwidget")
3+
register_upgrade_message("shiny", "1.1", error = TRUE)
394
}
405

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

R/scaffold.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ scaffoldWidget <- function(name, bowerPkg = NULL, edit = interactive()){
3232

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

3838
capName = function(name){
@@ -80,7 +80,7 @@ addWidgetYAML <- function(name, bowerPkg, edit){
8080

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

8686
if (!file.exists(file_ <- sprintf('inst/htmlwidgets/%s.js', name))){

R/shim.R

Lines changed: 0 additions & 62 deletions
This file was deleted.

R/staticimports.R

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
# Generated by staticimports; do not edit by hand.
2+
# ======================================================================
3+
# Imported from pkg:staticimports
4+
# ======================================================================
5+
6+
`%||%` <- function(a, b) {
7+
if (is.null(a)) b else a
8+
}
9+
10+
# Borrowed from pkgload:::dev_meta, with some modifications.
11+
# Returns TRUE if `pkg` was loaded with `devtools::load_all()`.
12+
devtools_loaded <- function(pkg) {
13+
ns <- .getNamespace(pkg)
14+
if (is.null(ns) || is.null(ns$.__DEVTOOLS__)) {
15+
return(FALSE)
16+
}
17+
TRUE
18+
}
19+
20+
get_package_version <- function(pkg) {
21+
# `utils::packageVersion()` can be slow, so first try the fast path of
22+
# checking if the package is already loaded.
23+
ns <- .getNamespace(pkg)
24+
if (is.null(ns)) {
25+
utils::packageVersion(pkg)
26+
} else {
27+
as.package_version(ns$.__NAMESPACE__.$spec[["version"]])
28+
}
29+
}
30+
31+
is_installed <- function(pkg, version = NULL) {
32+
installed <- isNamespaceLoaded(pkg) || nzchar(system_file_cached(package = pkg))
33+
if (is.null(version)) {
34+
return(installed)
35+
}
36+
installed && isTRUE(get_package_version(pkg) >= version)
37+
}
38+
39+
register_upgrade_message <- function(pkg, version, error = FALSE) {
40+
41+
msg <- sprintf(
42+
"This version of '%s' is designed to work with '%s' >= %s.
43+
Please upgrade via install.packages('%s').",
44+
environmentName(environment(register_upgrade_message)),
45+
pkg, version, pkg
46+
)
47+
48+
cond <- if (error) stop else packageStartupMessage
49+
50+
if (pkg %in% loadedNamespaces() && !is_installed(pkg, version)) {
51+
cond(msg)
52+
}
53+
54+
# Always register hook in case pkg is loaded at some
55+
# point the future (or, potentially, but less commonly,
56+
# unloaded & reloaded)
57+
setHook(
58+
packageEvent(pkg, "onLoad"),
59+
function(...) {
60+
if (!is_installed(pkg, version)) cond(msg)
61+
}
62+
)
63+
}
64+
65+
# Simplified version rlang:::s3_register() that just uses
66+
# warning() instead of rlang::warn() when registration fails
67+
# https://github.com/r-lib/rlang/blob/main/R/compat-s3-register.R
68+
s3_register <- function(generic, class, method = NULL) {
69+
stopifnot(is.character(generic), length(generic) == 1)
70+
stopifnot(is.character(class), length(class) == 1)
71+
72+
pieces <- strsplit(generic, "::")[[1]]
73+
stopifnot(length(pieces) == 2)
74+
package <- pieces[[1]]
75+
generic <- pieces[[2]]
76+
77+
caller <- parent.frame()
78+
79+
get_method_env <- function() {
80+
top <- topenv(caller)
81+
if (isNamespace(top)) {
82+
asNamespace(environmentName(top))
83+
} else {
84+
caller
85+
}
86+
}
87+
get_method <- function(method, env) {
88+
if (is.null(method)) {
89+
get(paste0(generic, ".", class), envir = get_method_env())
90+
} else {
91+
method
92+
}
93+
}
94+
95+
register <- function(...) {
96+
envir <- asNamespace(package)
97+
98+
# Refresh the method each time, it might have been updated by
99+
# `devtools::load_all()`
100+
method_fn <- get_method(method)
101+
stopifnot(is.function(method_fn))
102+
103+
# Only register if generic can be accessed
104+
if (exists(generic, envir)) {
105+
registerS3method(generic, class, method_fn, envir = envir)
106+
} else {
107+
warning(
108+
"Can't find generic `", generic, "` in package ", package,
109+
" register S3 method. Do you need to update ", package,
110+
" to the latest version?", call. = FALSE
111+
)
112+
}
113+
}
114+
115+
# Always register hook in case package is later unloaded & reloaded
116+
setHook(packageEvent(package, "onLoad"), function(...) {
117+
register()
118+
})
119+
120+
# Avoid registration failures during loading (pkgload or regular).
121+
# Check that environment is locked because the registering package
122+
# might be a dependency of the package that exports the generic. In
123+
# that case, the exports (and the generic) might not be populated
124+
# yet (#1225).
125+
if (isNamespaceLoaded(package) && environmentIsLocked(asNamespace(package))) {
126+
register()
127+
}
128+
129+
invisible()
130+
}
131+
132+
# Borrowed from pkgload::shim_system.file, with some modifications. This behaves
133+
# like `system.file()`, except that (1) for packages loaded with
134+
# `devtools::load_all()`, it will return the path to files in the package's
135+
# inst/ directory, and (2) for other packages, the directory lookup is cached.
136+
# Also, to keep the implementation simple, it doesn't support specification of
137+
# lib.loc or mustWork.
138+
system_file <- function(..., package = "base") {
139+
if (!devtools_loaded(package)) {
140+
return(system_file_cached(..., package = package))
141+
}
142+
143+
if (!is.null(names(list(...)))) {
144+
stop("All arguments other than `package` must be unnamed.")
145+
}
146+
147+
# If package was loaded with devtools (the package loaded with load_all),
148+
# also search for files under inst/, and don't cache the results (it seems
149+
# more likely that the package path will change during the development
150+
# process)
151+
pkg_path <- find.package(package)
152+
153+
# First look in inst/
154+
files_inst <- file.path(pkg_path, "inst", ...)
155+
present_inst <- file.exists(files_inst)
156+
157+
# For any files that weren't present in inst/, look in the base path
158+
files_top <- file.path(pkg_path, ...)
159+
present_top <- file.exists(files_top)
160+
161+
# Merge them together. Here are the different possible conditions, and the
162+
# desired result. NULL means to drop that element from the result.
163+
#
164+
# files_inst: /inst/A /inst/B /inst/C /inst/D
165+
# present_inst: T T F F
166+
# files_top: /A /B /C /D
167+
# present_top: T F T F
168+
# result: /inst/A /inst/B /C NULL
169+
#
170+
files <- files_top
171+
files[present_inst] <- files_inst[present_inst]
172+
# Drop cases where not present in either location
173+
files <- files[present_inst | present_top]
174+
if (length(files) == 0) {
175+
return("")
176+
}
177+
# Make sure backslashes are replaced with slashes on Windows
178+
normalizePath(files, winslash = "/")
179+
}
180+
181+
# A wrapper for `system.file()`, which caches the results, because
182+
# `system.file()` can be slow. Note that because of caching, if
183+
# `system_file_cached()` is called on a package that isn't installed, then the
184+
# package is installed, and then `system_file_cached()` is called again, it will
185+
# still return "".
186+
system_file_cached <- local({
187+
pkg_dir_cache <- character()
188+
189+
function(..., package = "base") {
190+
if (!is.null(names(list(...)))) {
191+
stop("All arguments other than `package` must be unnamed.")
192+
}
193+
194+
not_cached <- is.na(match(package, names(pkg_dir_cache)))
195+
if (not_cached) {
196+
pkg_dir <- system.file(package = package)
197+
pkg_dir_cache[[package]] <<- pkg_dir
198+
} else {
199+
pkg_dir <- pkg_dir_cache[[package]]
200+
}
201+
202+
file.path(pkg_dir, ...)
203+
}
204+
})

0 commit comments

Comments
 (0)