In reorganising my code base I’d like to clean up my code sharing mechanism. So far I’m using source
for lots of small, largely self-contained modules of functionality.
However, this approach suffers from a number of problems, among them
source
chains),chdir=TRUE
argument, hard-coded paths),Ideally I’d like to get something alike to the Python module mechanism. The R package mechanism would be overkill here: I do not want to generate nested path hierarchies, multiple files with tons of metadata and manually build the package just to get a small, self-contained, reusable code module.
For now I’m using a code snippet which allows me to solve the first two problems mentioned above. The syntax for inclusion is like this:
import(functional) import(io) import(strings)
… and a module is defined as a simple source file which resides in the local path. The definition of import
is straightforward but I cannot solve the third point: I want to import the module into a separate namespace but from what I see the namespace lookup mechanism is pretty hard-wired to packages. True, I could override `::`
or getExportedValue
and maybe asNamespace
and isNamespace
but that feels very dirty and has the potential of breaking other packages.
Each sub-package can now be separately installed, used, and versioned. Namespace packages can be useful for a large collection of loosely-related packages (such as a large corpus of client libraries for multiple products from a single company).
So if you want to create a namespace, you just need to call a function, instantiate an object, import a module or import a package. For example, we can create a class called Namespace and when you create an object of that class, you're basically creating a namespace.
On Python 3.3 you don't have to do anything, just don't put any __init__.py in your namespace package directories and it will just work. On pre-3.3, choose the pkgutil. extend_path() solution over the pkg_resources.
A namespace is a 1–15 character alphanumeric identifier that distinguishes your package and its contents from other packages in your customer's org. A namespace is assigned to a package at the time that it's created, and can't be changed.
Here's a function that completely automates package creation, compilation, and reloading. As others have noted, the utility functions package.skeleton()
and devtools::load_all()
already get you almost all the way there. This just combines their functionality, using package.skeleton()
to create the source directory in a temp directory that gets cleaned up when load_all()
is done processing it.
All you need to do is point to the source files from which you want to read in functions, and give the package a name: import()
does the rest for you.
import <- function(srcFiles, pkgName) { require(devtools) dd <- tempdir() on.exit(unlink(file.path(dd, pkgName), recursive=TRUE)) package.skeleton(name=pkgName, path = dd, code_files=srcFiles) load_all(file.path(dd, pkgName)) } ## Create a couple of example source files cat("bar <- function() {print('Hello World')}", file="bar.R") cat("baz <- function() {print('Goodbye, cruel world.')}", file="baz.R") ## Try it out import(srcFiles=c("bar.R", "baz.R"), pkgName="foo") ## Check that it worked head(search()) # [1] ".GlobalEnv" "package:foo" "package:devtools" # [4] "package:stats" "package:graphics" "package:grDevices" bar() # [1] "Hello World" foo::baz() # [1] "Goodbye, cruel world."
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With