Skip to content

Instantly share code, notes, and snippets.

@infotroph
Last active November 2, 2017 03:16
Show Gist options
  • Save infotroph/d8759135eabe5c14989954b18ee33718 to your computer and use it in GitHub Desktop.
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
#' 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