Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

R: what's the proper way to overwrite a function from a package?

I am using a R package, in which there are 2 functions f1 and f2 (with f2 calling f1)
I wish to overwrite function f1.

Since R 2.15 and the mandatory usage of namespace in packages, if I just source the new function, it is indeed available in the global environement (ie. just calling f1(x) in the console returns the new result). However, calling f2 will still use the packaged function f1. (Because the namespace modifies the search path, and seals it as explained here in the Writing R Extensions tutorial)
What is the proper way to completely replace f1 with the new one? (apart from building again the package!) This can be useful in several situations. For instance if there is a bug in a package that you have not developed. Or if you don't want to re-build your packages everyday while they are still under development.

I know about function

assignInNamespace("f1",f1,ns="mypackage")

However, the help page ?assignInNamespace is a bit enignmatic and seems to discourage people from using it without giving more information, and I couldn't find any best practice recommendations on the official CRAN tutorial. and after calling this function:

# Any of these 2 calls return the new function
mypackage::f1 
getFromNamespace(x = "f1", envir = as.environment("package:mypackage"))

# while this one still returns the old packaged version
getFunction(name = "f1", where = as.environment("package:mypackage")) 

This is very disturbing. How is the search path affected?

For now I am doing some ugly things such as modifying the lockEnvironment function so that library doesn't lock the package namespace, and I can lock it at a later stage once I have replaced f1 (which seems really not a good practice)

So basically I have 2 questions:

  1. what does exactly do assignInNamespace in the case of a package namespace (which is supposed to be locked)
  2. What are the good practices?

many thanks for sharing your experience there.

EDIT: people interested in this question might find this blog post extremely interesting.

like image 632
RockScience Avatar asked Oct 30 '14 03:10

RockScience


2 Answers

There are lots of different cases here.

If it's a bug in someone else's package
Then the best practice is to contact the package maintainer and persuade them to fix it. That way everyone gets the fix, not just you.

If it's a bug while developing your own package
Then you need to find a workflow where rebuilding packages is easy. Like using the devtools package and typing build(mypackage), or clicking a button ("Build & Reload" in RStudio; "R CMD build" in Architect).

If you just want different behaviour to an existing package
If it isn't a bug as such, or the package maintainer won't make the fix that you want, then you'll have to maintain you own copy of f1. Using assignInNamespace to override it in the existing package is OK for exploring, but it's a bit hacky so it isn't really suitable for a permanent solution.

Your best bet is to create your own package containing copies of f1 and f2. This is less effort than it sounds, since you can just define f2 <- existingpackage::f2.


In response to the comment:

Second and third cases makes sense if you are alone but they require to build and install the packages which is tricky in the case of my organisation as the packages are deployed on dozens of computer and I need root access to update the packages.

So take a copy of the existing package source, apply your patch, and host it on your company network or github or Bitbucket. Then the updated package can be installed programmatically via

install.packages("//some/network/path/mypackage_0.0-1.tar.gz", repos = NULL)

or

library(devtools)
install_github("mypackage", "mygithubusername")

Since the installation is just a line of code, you can easily push it to as many machines as you like. You don't need root access either - just install the package to a library folder that doesn't require root access to write to. (Read the Startup and .libPaths help pages for how to define a new library.) You'll need network access to those machines, but I can't help you with that. Speak to your network administrator or your boss or whoever can get you permission.

like image 72
Richie Cotton Avatar answered Nov 18 '22 00:11

Richie Cotton


In case the function has no explicit binding within the package:

rlang::env_unlock(env = asNamespace('mypackage'))
rlang::env_binding_unlock(env = asNamespace('mypackage'))
assign('f1', f1, envir = asNamespace('mypackage'))
rlang::env_binding_lock(env = asNamespace('mypackage'))
rlang::env_lock(asNamespace('mypackage'))
like image 40
Davor Josipovic Avatar answered Nov 18 '22 00:11

Davor Josipovic