Skip to content

Commit fae2efe

Browse files
authored
Merge pull request #1886 from rstudio/fix-cran-load-hook-py-require-warning
Use startup messages for py_require checks during package load
2 parents 9667edc + f1a3c95 commit fae2efe

File tree

4 files changed

+73
-24
lines changed

4 files changed

+73
-24
lines changed

R/package.R

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -370,10 +370,10 @@ check_virtualenv_required_packages <- function(config) {
370370
# - numba==0.60.0
371371
# + numba==0.61.0
372372
if (any(grepl("downloading", pip_output, ignore.case = TRUE))) {
373-
warning(paste(
373+
warning_or_startup_message(paste(
374374
pip_output[grepl("downloading", pip_output, ignore.case = TRUE)],
375375
collapse = "\n"
376-
))
376+
), call. = FALSE)
377377
}
378378

379379
# uv pip doesn't have an option to produce structured output,
@@ -399,7 +399,7 @@ check_virtualenv_required_packages <- function(config) {
399399
vapply(packages, function(pkg) any(startsWith(pkg, would_install_pkgs)), TRUE,
400400
USE.NAMES = FALSE)
401401
]
402-
warning(
402+
warning_or_startup_message(
403403
"Some Python package requirements declared via `py_require()` are not",
404404
" installed in the selected Python environment: (", config$python, ")\n",
405405
" ", paste0(would_install_pkgs, collapse=" "),
@@ -433,14 +433,7 @@ check_forbidden_initialization <- function() {
433433

434434
call <- calls[[i]]
435435
frame <- frames[[i]]
436-
if (!identical(call[[1]], as.name("runHook")))
437-
next
438-
439-
bad <-
440-
identical(call[[2]], ".onLoad") ||
441-
identical(call[[2]], ".onAttach")
442-
443-
if (!bad)
436+
if (!is_package_loading(list(call)))
444437
next
445438

446439
pkgname <- tryCatch(

R/use_python.R

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -278,20 +278,10 @@ use_miniconda <- function(condaenv = NULL, required = NULL) {
278278

279279
use_python_required <- function() {
280280

281-
# for now, assume that calls from within a package's .onLoad() are
281+
# for now, assume that calls from within package load hooks are
282282
# advisory, but we may consider relaxing this in the future
283-
calls <- sys.calls()
284-
for (call in calls) {
285-
286-
match <-
287-
length(call) >= 2 &&
288-
identical(call[[1L]], as.name("runHook")) &&
289-
identical(call[[2L]], ".onLoad")
290-
291-
if (match)
292-
return(FALSE)
293-
294-
}
283+
if (is_package_loading())
284+
return(FALSE)
295285

296286
# default to TRUE
297287
TRUE

R/utils.R

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,36 @@ parent.pkg <- function(env = parent.frame(2)) {
742742
NULL # print visible
743743
}
744744

745+
is_package_loading <- function(calls = sys.calls()) {
746+
for (call in calls) {
747+
if (length(call) < 2L)
748+
next
749+
750+
hook <- call[[2L]]
751+
if (is.symbol(hook))
752+
hook <- as.character(hook)
753+
754+
match <-
755+
identical(call[[1L]], as.name("runHook")) &&
756+
is.character(hook) &&
757+
hook %in% c(".onLoad", ".onAttach")
758+
759+
if (match)
760+
return(TRUE)
761+
}
762+
763+
FALSE
764+
}
765+
766+
warning_or_startup_message <- function(..., call. = TRUE) {
767+
msg <- .makeMessage(...)
768+
if (is_package_loading()) {
769+
packageStartupMessage(msg)
770+
} else {
771+
warning(msg, call. = call.)
772+
}
773+
}
774+
745775
warn_and_return <- function(..., call. = TRUE) {
746776
cond <- if (inherits(..1, "condition")) {
747777
..1

tests/testthat/test-py_require.R

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,3 +244,39 @@ test_that("py_require() warns missing packages in a virtual env", {
244244
}
245245
)
246246
})
247+
248+
test_that("package load hooks get startup messages, not warnings", {
249+
expect_true(is_package_loading(list(quote(runHook(".onLoad", foo)))))
250+
expect_true(is_package_loading(list(quote(runHook(.onAttach, foo)))))
251+
expect_false(is_package_loading(list(quote(runHook(".onUnload", foo)))))
252+
253+
msg <- paste(
254+
"Some Python package requirements declared via `py_require()` are not",
255+
"installed in the selected Python environment: (/path/to/python)\n",
256+
" numpy"
257+
)
258+
259+
expect_warning(
260+
warning_or_startup_message(msg, call. = FALSE),
261+
"Some Python package requirements declared via `py_require()` are not",
262+
fixed = TRUE
263+
)
264+
265+
runHook <- function(name, expr) expr()
266+
expect_message(
267+
expect_warning(
268+
runHook(".onLoad", function() warning_or_startup_message(msg, call. = FALSE)),
269+
NA
270+
),
271+
"Some Python package requirements declared via `py_require()` are not",
272+
fixed = TRUE
273+
)
274+
expect_message(
275+
expect_warning(
276+
runHook(".onAttach", function() warning_or_startup_message(msg, call. = FALSE)),
277+
NA
278+
),
279+
"Some Python package requirements declared via `py_require()` are not",
280+
fixed = TRUE
281+
)
282+
})

0 commit comments

Comments
 (0)