Build package namespace dynamically during `.onLoad()`

From what I've read, you're doing the right thing. From the help page for .onLoad:

Anything needed for the functioning of the namespace should be handled at load/unload times by the .onLoad and .onUnload hooks.

From the "Using reticulate in an R Package" vignette:

If you write an R package that wraps one or more Python packages, it’s likely that you’ll be importing Python modules within the .onLoad method of your package so that you can have convenient access to them within the rest of the package source code.

You can automate the process a bit, if you'd like. This is working for me so far in a testing package:

inst/python/the_py_module.py

def f1():
  return "f one"


def f2():
  return "f two"

R/load-python.R

# Load the module and create dummy objects from it, all of which are NULL
the_py_module <- reticulate::import_from_path(
  "the_py_module",
  file.path("inst", "python")
)
for (obj in names(the_py_module)) {
  assign(obj, NULL)
}
# Clean up
rm(the_py_module)

# Now all those names are in the namespace, and ready to be replaced on load
.onLoad <- function(libname, pkgname) {
  the_py_module <- reticulate::import_from_path(
    "the_py_module",
    system.file("python", package = packageName()),
    delay_load = TRUE
  )
  # assignInMyNamespace(...) is meant for namespace manipulation
  for (obj in names(the_py_module)) {
    assignInMyNamespace(obj, the_py_module[[obj]])
  }
}

Of course, you'll probably want to document each of the exported functions. In which case, writing fn <- NULL for each isn't much additional work.

2 Likes