Reputation: 56955
I'm trying to mask a function that a function from a package calls.
As a reproducible (I think) example, look at the function isTRUE
:
function (x)
identical(TRUE, x)
Suppose for some reason I wanted identical
to always return "foobar"
, and hence isTRUE
would always return "foobar":
# try override identical
identical <- function(...) { return('foobar') }
identical(TRUE, 'a') # 'foobar', as expected
Now I call isTRUE
, hoping that the call to identical
in that function will access my masked version, but it doesn't:
isTRUE('a') # hope that this will return 'foobar'
# [1] FALSE
So in general, how do I temporarily cause a function called from within a packaged function to return something different?
I have a function in my package:
myFunc <- function(...) {
if (!require(rgdal)) {
stop("You do not have rgdal installed")
}
# ...
}
I want to test that if rgdal is not installed, the function throws an error.
However, I do have rgdal installed. I want myFunc
to think it isn't (temporarily), so I am trying to do:
require <- function(...) { return(FALSE) }
before calling myFunc
hoping that it will fail. However, it appears that myFunc
isn't tricked by this and still calls base::require
instead of my require
.
(Yes, this seems like a trivial thing, because most certainly myFunc
would throw an error if I didn't have rgdal installed, but suppose now the condition were more complex and I wanted to test in the same way - my question still stands)
Upvotes: 4
Views: 1303
Reputation: 1606
I was experimenting with the same overrideIn
trick that mnel and mathematical.coffee explained above and I ended up finding another alternative from here:
overRideIn3 <- function(fun,...){
e <- environment()
overrides <- list(...)
ns <- nchar(names(overrides)) > 0L
list2env(overrides[ns], envir = e)
environment(fun) <- e
fun
}
Note that all of these dynamic scoping tricks only work on the scope of fun
. That means that any helper functions that fun
calls will not have access to the definitions provided in overrides
unless fun
also wraps those helper functions with overRideIn
.
Upvotes: 0
Reputation: 115505
You can programatically create a function
foo <- function(...) if(!require('MASS')) stop('foo')
testfun <- function(fun){
require <- function(...) FALSE
fff <- function(){}
formals(fff) <- formals(fun)
body(fff) <- body(fun)
fff
}
testfoo <- testfun('foo')
require
is defined as is testfun
when the function is now created.
foo()
## Loading required package: MASS
detach(package:MASS)
testfoo()
# Error in testfoo() : foo
You could do something similar with local
, but I think it would be messier
eg
testfoo2 <- local({
require <- function(...) FALSE
foo <- function(...) if(!require('MASS')) stop('foo')
})
testfoo2()
## Error in testfoo2() : foo
(From mathematicalcoffee - a followup based on this answer).
I was able to define a function:
overrideIn <- function(f, ...) {
overrides <- list(...)
nms <- names(overrides)[names(overrides) != '']
# stub out the functions
for (nm in nms) {
assign(nm, overrides[[nm]])
}
# copy over f
fff <- function () {}
formals(fff) <- formals(f)
body(fff) <- body(f)
return(fff)
}
so that I could do
f <- overrideIn(myFunc, require=function (...) FALSE)
Now when I call f
it has the overridden version of require
in it, so I can do (using the fantastic testthat package):
expect_that(f(), throws_error('You do not have rgdal installed'))
A slightly more succinct version of the overide function (mnel again)
overRideIn2 <- function(fun,...){
e <- environment()
.list <- list(...)
ns <- nchar(names(.list))>0L
list2env(.list[ns], envir = e)
fff <- as.function(as.list(fun))
}
Upvotes: 2