From 3ddd5e0953f32f23c069d0b22b8be1dbd2b113c4 Mon Sep 17 00:00:00 2001 From: Sebastian Warnholz Date: Sun, 1 Oct 2023 21:58:41 +0200 Subject: [PATCH] Add guard to export when called outside of module --- R/export.R | 24 ++++++++++++++++++------ tests/testthat/test-export.R | 9 ++++++++- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/R/export.R b/R/export.R index 64268e4..9fa944a 100644 --- a/R/export.R +++ b/R/export.R @@ -16,6 +16,9 @@ #' declared. A regular expression is denoted, as a convention, as character #' vector of length one with a leading "^". #' +#' When \code{export} is called outside of a module, it has no effect and +#' returns early. A warning is raised in this case. +#' #' @examples #' module({ #' export("foo") @@ -48,6 +51,7 @@ #' }) #' @export export <- function(..., where = parent.frame()) { + if (exportCalledOutsideOfModule(where)) return(invisible(NULL)) exportWarnOnNonStandardCalls(match.call()) objectsToExport <- deparseEllipsis(match.call(), "where") currentExports <- exportGetCurrentValue(where) @@ -60,6 +64,14 @@ export <- function(..., where = parent.frame()) { invisible(NULL) } +exportCalledOutsideOfModule <- function(where) { + calledOutsideOfModule <- !exists(exportNameWithinModule(), where, inherits = FALSE) + if (calledOutsideOfModule) { + warning("Calling 'export' outside of a module has no effect.") + } + calledOutsideOfModule +} + exportWarnOnNonStandardCalls <- function(call) { # exporting with do.call is not working properly, so we throw a warning, in # case we can detect it. Consider the following examples: @@ -71,7 +83,7 @@ exportWarnOnNonStandardCalls <- function(call) { # do.call(export, list(fun = sm$fun)) # }) # It will not work, although `export(fun = sm$fun)` does work as expected. - # This is extremely difficult to dubug and it seems to be better to turn it + # This is extremely difficult to debug and it seems to be better to turn it # off until someone can fix it. if (length(deparse(call[[1]])) > 1) { warning( @@ -110,13 +122,13 @@ exportResolveFinalValue <- function(envir) { exportExtractElement <- function(where) { function(element, name) { name <- if (name == "") element else name - # we need to make sure that special names, - # - infix operators: %*%, - # - S3 methods for binary operators: ==.foo - # - names with whitespaces + # we need to make sure that special names, + # - infix operators: %*%, + # - S3 methods for binary operators: ==.foo + # - names with whitespace # - single character punctuation: ! # are parsed correctly - regexp <- "^%.*%$|^[[:alnum:][:space:]]+$|^[[:punct:]]{2,}.*$|^[[:punct:]]$" + regexp <- "^%.*%$|^[[:alnum:][:space:]]+$|^[[:punct:]]{2,}.*$|^[[:punct:]]$" element <- if (grepl(regexp, element)) paste0("`", element, "`") else element # Exclude Linting object <- tryCatch( eval(parse(text = element), where, baseenv()), diff --git a/tests/testthat/test-export.R b/tests/testthat/test-export.R index b062e17..30bf90d 100644 --- a/tests/testthat/test-export.R +++ b/tests/testthat/test-export.R @@ -1,7 +1,14 @@ +testthat::test_that("export can be called savely outside of module #47", { + testthat::expect_warning( + modules::export("something"), + "Calling 'export' outside of a module has no effect." + ) +}) + test_that("Exports of special names #43", { m <- module({ "==.foo" <- function(lhs, rhs) base::`==`(lhs, rhs) # Exclude Linting - "!.foo" <- function(lhs, rhs) base::`!=`(lhs, rhs) # Exclude Linting + "!.foo" <- function(lhs, rhs) base::`!=`(lhs, rhs) # Exclude Linting }) testthat::expect_true(m$`==.foo`(1, 1)) testthat::expect_true(m$`!.foo`(1, 2))