Reputation: 374
If I create a warning using the rlang package inside a function, the warning is output after the function output:
delayed_warning <- function() {
rlang::warn("A warning")
Sys.sleep(10)
"A return value"
}
delayed_warning()
#> Warning: A warning
#> [1] "A return value"
Created on 2023-09-18 with reprex v2.0.2
In some circumstances it is useful to output warnings immediately, for example when the warning is to tell the user that a function might take a long time. If I were to use the base-R warning()
, I could use immediate. = TRUE
to output the warning immediately.
My question: is there a way to output immediately a warning created using rlang::warn()
?
(This feels like the sort of thing that I should have been able to answer with a Google search, so I suspect I'm not searching for the right terms.)
Upvotes: 2
Views: 174
Reputation: 18581
I didn't realize that rlang::warn
calls base R's warning()
under the hood, as Ritchie Sacramento pointed out.
We can use this fact to alter the call of rlang:warn
on the fly. This is some dirty function hacking, but it works.
We define a function warn2
which:
rlang::warn
body()
immediate. = TRUE
argumentThis approach does work, but it has two downsides.
First, you'd need to check prior versions of rlang if rlang::warn
really ends with calling warning()
. In a package just set the required rlang version to the version that starts using warning()
in the last line.
Second, it is of course not guaranteed that future versions of rlang will still use base R's warning()
in the last line. If at one point this changes your custom warn2
function would not work anymore and we would probably need some error checks and workarounds for this.
All in all I'd say this kind of dirty function hacking is not a clean solution to your problem. But if you want to throw immediate warnings with rlang this is the way you could go. I have no idea if this kind of function hacking is allowed in a CRAN package, but I'd be curious to find out.
warn2 <- function(msg) {
f <- rlang::warn
f_bdy <- body(f)
f_bdy_ls <- as.list(f_bdy)
lst_line <- length(f_bdy_ls)
warn_cl <- f_bdy_ls[[lst_line]]
warn_ls <- as.list(warn_cl)
warn_ls <- append(warn_ls, list(immediate. = TRUE))
f_bdy_ls[[lst_line]] <- as.call(warn_ls)
body(f) <- as.call(f_bdy_ls)
f(msg)
}
delayed_warning <- function() {
warn2("A warning")
Sys.sleep(10)
"A return value"
}
delayed_warning()
#> Warning: A warning
#> [1] "A return value"
Created on 2023-09-23 with reprex v2.0.2
Upvotes: 1
Reputation: 374
After doing some further digging I found a GitHub issue requesting exactly this functionality in rlang. The issue has been closed with a note saying there is no plan to provide this.
Since outputting a warning immediately isn't possible using rlang::warn()
, it seems the best workaround (as posted by @TimTeaFan in a comment) is to instead use rlang::inform()
, since messages are produced immediately by default:
instant_message <- function() {
rlang::inform("A message")
Sys.sleep(10)
"A return value"
}
instant_message()
#> A message
#> [1] "A return value"
Created on 2023-09-20 with reprex v2.0.2
Upvotes: 0