How to get the name of the calling function inside the called routine?
Solution 1:
Thanks @GavinSimpson and @RicardoSporta, but I've figured it out. I'll post an answer in case somebody searches for this in SO.
The name of the function that generated the current call can be retrieved by
deparse(sys.calls()[[sys.nframe()-1]])
This returns a string that contains not only the name of the function, but the entire call object. The name alone can be retrieve by subsetting sys.calls()[[sys.nframe()-1]]
before deparsing.
I wanted this because I wrote a function that checks the arguments and halts execution in case of an error. But I wanted this function to (i) dump the environment and (ii) show name of the function one level above in the execution stack. (i) is easy, but I was stuck in (ii).
As for the second question in my post, this is what happens: the expression stop("invalid input")
is evaluated in the environment of the test
function, but this is not the same thing as if the expression was part of test
's body, because the execution stack is different in this 2 scenarios. In the latter case, stop
has only test
above it, but in the first, it has eval
, check
and then test
upwards. The execution stack, returned by sys.calls()
is not the same thing as the enclosing environments. This is what may cause confusion.
Solution 2:
See ?match.call
. For example:
foo <- function() {
match.call()[[1]]
}
foo()
as.character(foo())
which produces
> foo()
foo
>
> as.character(foo())
[1] "foo"
A simplified version of your code is
check <- function(x) {
match.call()[[1]]
}
test <- function(y) {
check(y)
}
giving
> test(2)
check
> as.character(test(2))
[1] "check"
Note match.call()
works via the use of sys.call()
(actually it calls sys.call(sys.parent())
) when called as I did above with no arguments. So you may wish to consult ?sys.call
too.
Solution 3:
For the record, as Hadley has suggested, you can use sys.call()
. For example:
funx = function(...) {
callingFun = as.list(sys.call(-1))[[1]]
calledFun = as.list(sys.call())[[1]]
message(paste(callingFun, " is calling ", calledFun, sep=""))
}
funy = function(...) {funx(...)}
> funy(a = 1, b = 2)
funy is calling funx
Solution 4:
Question #1 is answered by Gavin (use match.call
).
However, based on what you are describing, you should also look at traceback()
, the output of which you can pass to other functions.
As for Question #2:
It isn't failing, but working exactly as expected. The error you are seeing is not an error in the true sense, but rather the error from your stop(.)
function.
If you look at print(evalq)
, you will see that it in turn calls eval(substitute(expr), envir, enclos))
where expr
is your stop("invalid input.")
The correct workaround is to use one more level of quoting
evalq(quote(stop("invalid input.")))
# stop("invalid input.")
Solution 5:
Alternative way with the rlang
package:
f <- function() {
name <- rlang::call_frame(n = 2)$fn_name
rlang::abort(paste("The function", name, "was called and I throw an error."))
}
g <- function() f()
g()
#> Error: The function g was called and I throw an error.
Created on 2019-03-03 by the reprex package (v0.2.1)