Last active
November 2, 2017 03:16
-
-
Save infotroph/d8759135eabe5c14989954b18ee33718 to your computer and use it in GitHub Desktop.
How to create a package namespace without saving any files! ...Wait, why would you though
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#' Generate a minimal fake package namespace | |
#' | |
#' Mocks up a tiny package namespace and monkey-patches it into the current R | |
#' sessions's namespace registry. This abuses some R internals and has high | |
#' potential to break things for the remainder of your session. Use it with | |
#' great caution, or maybe not at all. | |
#' | |
#' The intended use case was to provide nonfunctional skeletons of selected | |
#' functions from packages that are not installed, solely so that they could | |
#' then be replaced by test stubs. Embarrassingly soon after writing this | |
#' (within minutes!), I learned that mockery::stub works for uninstalled | |
#' functions without any need for a fake package, so this function is now of | |
#' entirely academic interest. | |
#' | |
#' The core logic is mostly taken from base::loadNamespace, but don't blame | |
#' the R-core team for any use or abuse of this function. | |
#' | |
#' @param name The name to assign to the namespace | |
#' @param functions named vector of function definitions to export. | |
#' @param version a version string, for those who track their monkey-patches | |
#' very carefully | |
#' @param load,attach logical: load namespace and attach to the search path | |
#' after registering it? | |
#' | |
#' @author Chris Black | |
#' | |
#' @examples | |
#' \dontrun{ | |
#' f = function()notReal::g() | |
#' f() # Error in loadNamespace(name) : there is no package called 'notReal' | |
#' | |
#' if(length(find.package("notReal")) == 0){ | |
#' makeNamespaceStub("notReal", c(g=function()1)) | |
#' on.exit(.Internal(unregisterNamespace("notReal"))) | |
#' } | |
#' | |
#' f() # 1 | |
#' mockery::stub(f, "notReal::g", function()2) | |
#' f() # 2 | |
#' } | |
#' | |
make_package_stub <- function(name, functions, version="0.0.1", load = FALSE, attach = FALSE){ | |
import_env <- new.env(parent = .BaseNamespaceEnv, hash = TRUE) | |
attr(import_env, "name") <- paste0("imports:", name) | |
pkg_env <- new.env(parent = import_env, hash = TRUE) | |
info <- new.env(hash = TRUE, parent = baseenv()) | |
pkg_env$.__NAMESPACE__. <- info | |
info$spec <- c(name = name, version = version) | |
setNamespaceInfo(pkg_env, "imports", list(base = TRUE)) | |
export_env <- new.env(parent = baseenv()) | |
for (i in seq_along(functions)){ | |
assign(names(functions)[[i]], functions[[i]], envir = pkg_env) | |
} | |
setNamespaceInfo(pkg_env, "exports", export_env) | |
data_env <- new.env(parent = baseenv(), hash = TRUE) | |
attr(data_env, "name") <- paste0("lazydata:", name) | |
setNamespaceInfo(pkg_env, "lazydata", data_env) | |
setNamespaceInfo(pkg_env, "dynlibs", NULL) | |
setNamespaceInfo(pkg_env, "S3methods", matrix(NA_character_, 0L, 3L)) | |
pkg_env$.__S3MethodsTable__. <- new.env(hash = TRUE, parent = baseenv()) | |
.Internal(registerNamespace(name, pkg_env)) | |
namespaceExport(pkg_env, names(functions)) | |
lockEnvironment(pkg_env, TRUE) | |
lockEnvironment(parent.env(pkg_env), TRUE) | |
if(load){ | |
loadNamespace(name) | |
} | |
if(attach){ | |
attachNamespace(name) | |
} | |
invisible(pkg_env) | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment