Let's assume I have the following script:
msg.R
:
library(withr)
local_options(list(whatever = 1L))
suppressMessages(deferred_run())
I invoke it from the command line via
Rscript msg.R
I would have assumed to see no output (thanks to suppressMessage
), but in fact I see the message from deferred_run
:
No deferred expressions to run
I have 3 questions:
suppressMessages
would, well, suppress the message? If I run the code interactively the message is rightfully suppressed.No deferred expressions to run
I would have expected to see Ran 1/1 deferred expressions
as I am in fact changing the global state (thanks to local_options
) and I would have expected that this is reset? (Again when running this code interactively, I would see the run message [after disabling the suppression of course]).withr
for a script meant to be run from the command line at all? The script is anyways run in its own session, so changing the options should not have a bad effect anyways. So am I using an anti-pattern here?The message is initially perplexing but all is working as intended. The purpose of withr::local_options()
is to set options within some scope. When you run it in the global environment, this means you are setting the options for the entire R session. It prints:
Setting global deferred event(s).
i These will be run:
* Automatically, when the R session ends.
* On demand, if you call `withr::deferred_run()`.
The difference between your interactive example and the Rscript
version is that in the latter, your R session ends. Because you've called local_options()
at global scope, it checks whether there is anything left to run. This would also occur in interactive mode if you manually quit()
.
One way to demonstrate this is happening is with a script like this, where you don't suppress messages:
withr::local_options(list(whatever = 1L))
withr::deferred_run()
cat("***user code ended - script exiting***\n")
If you Rscript
this, you'll see the output:
Ran 1/1 deferred expressions
***user code ended - script exiting***
No deferred expressions to run
The message Ran 1/1 deferred expressions
is triggered by withr::deferred_run()
. The No deferred expressions to run
is triggered by exiting the R session. If you replace the second line with suppressMessages(withr::deferred_run())
, you'll get:
***user code ended - script exiting***
No deferred expressions to run
It looks like the suppressMessages()
is failing and it's not finding the deferred expressions. But in fact you have manually triggered the deferred expressions, so there are none left to find, and this is the cleanup call which runs when the R session ends, rather than the one that was wrapped in suppressMessages()
.
If you want to avoid triggering a cleanup when the R session ends, rather than setting options in the global environment, you can attach the handlers to a different scope. For example:
local({
env <- environment()
withr::local_options(list(whatever = 1L), .local_envir = env)
suppressMessages(withr::deferred_run(env))
# ^^ (Without suppression: Ran 1/1 deferred expressions)
})
This will run silently.
I think so - or at least it's probably not required. When you run withr::local_options(list(whatever = 1L))
at the top level, this means: set options(whatever=1L)
and revert the options to their previous state on exit. However, when you run Rscript
the options will revert anyway when the session ends, so if you're doing this at the global level, I don't see why it's necessary.