I have a helper function for displaying warnings:
mywarn <- function() {
warning("I warn you")
}
If I call the function directly, I get the name of the function where the error occurred:
mywarn()
> Warning message:
In mywarn() : I warn you
However I want to use the warning function from within several other functions:
myfun <- function(x) {
x <- x^2
mywarn()
x
}
But then it still shows the name of my warning function when warning is displayed:
myfun(10)
> Warning message:
In mywarn() : I warn you
What is the best way to make the warning message display myfun
instead of mywarn
?
In comments you pointed out that other solutions to your question fail on cases like
sapply(10, myfun)
where they display something like
Warning message:
In FUN(X[[i]], ...) : I warn you
I think this is nearly unavoidable without debug information. In the sapply()
call, myfun
is an object; sapply
doesn't care about its name. So to put "myfun" into the message instead of the local variable in the sapply
implementation, your mywarn
would need to recognize that it was being called from sapply
, and look further up the call stack to find the name of the function being called. And there are lots of other functions besides sapply
that it would have to handle, so this isn't a feasible approach.
If you happen to know that this is going to be a problem, you can work around it by making myfun
do more work. Instead of calling mywarn
with simply the error message, it could pass its own name as part of the message. This could be as simple as
mywarn("myfun")
or more elaborate like
mywarn(call = substitute(myfun(x),list(x = substitute(x))))
(where some of the ugliness could probably be moved into mywarn
, but the presence of myfun(x)
is essential).
You can do a lot more if you have enabled debug information in your source. This is the default if you're using source()
,
but it's optional for package source code.
For example, if you put the code below in a file named test.R
and call source("test.R")
, you'll get the output as shown:
myfun <- function(x) {
x <- x^2
mywarn()
x
}
mywarn <- function (msg = "I warn you", call = sys.call(sys.parent()))
{
calls <- sys.calls()
for (i in rev(seq_along(calls))) {
location <- getSrcref(calls[[i]])
if (!is.null(location)) {
call <- NULL
filename <- getSrcFilename(location)
linenum <- getSrcLocation(location)
functionname <- findLineNum(attr(location, "srcfile"), linenum)
if (length(functionname) < 1) prefix <- "At "
else prefix <- paste0("In ", functionname[[1]]$name, "() at ")
msg <- sprintf("%s%s#%d: %s", prefix, basename(filename), linenum, msg)
}
}
warning(simpleWarning(msg, call))
}
sapply(10, myfun)
Warning message:
At test.R#25: In myfun() at test.R#3: I warn you
which indicates that your source file line 25 called something that eventually got to myfun()
in your source file line 3 and issued the warning. You can make the prefix more or less informative according to your taste; I'd probably only include the closest location (In myfun() at test.R#3:
) for example.
If you are working in a situation where the debug info isn't present, it will fall back to @KonradRudolph's solution.