[Rd] [External] Re: message(<cond>) and warning(<cond>) circumvent calling handlers and signal the original class, e.g. an error

iuke-tier@ey m@iii@g oii uiow@@edu iuke-tier@ey m@iii@g oii uiow@@edu
Thu Mar 3 21:10:08 CET 2022


I'll look into what effect adding stopifnot(inherits(cond, "error"))
and similar to the others has on CRAN/BIOC packages. Probably won't
get there for a while though.

Best,

luke

On Wed, 2 Mar 2022, Henrik Bengtsson wrote:

> Thank you, Luke.  I discovered this problem last year, where a user
> reported that their use of message(<error>) in futures would not work
> the same way as without futures. The issue is that the future
> framework captures the error condition and relays it, rather than
> outputting the message string, which happens if you don't capture the
> error condition. Today there was another similar report from another
> package using futures. They both had in common that they use
>
> res <- tryCatch({
>  some_fcn(x)
> }, error = function(e) {
>  message(e)
>  NA
> })
>
> to return a missing value on errors, while outputting the error
> message string to inform the user on the error.  I've been informing
> them to instead use
>
>  message(conditionMessage(e))
>
> in this case. Your reply confirms this, and I can now confidently say
> that using message(e) is incorrect here.
>
> I think the help pages on message, warning, and stop could be more
> explicit on this behavior.
>
> My preference would be that it is an error if calling message(cond)
> with !inherits(cond, "message"), calling warning(cond) with
> !inherits(cond, "warning"), and stop(cond) with !inherits(cond,
> "error").  But, maybe there are valid arguments for allowing such use
> cases.
>
> Thanks,
>
> Henrik
>
> On Tue, Mar 1, 2022 at 3:12 PM <luke-tierney using uiowa.edu> wrote:
>>
>> This is behaving as documented and as intended. If you want to
>> call stop() with a condition argument and you want to have that
>> condition handled as an error then you need to make sure that your
>> condition inherits from "error". One way to do this would be to define
>> something like
>>
>> warningToError <- function(w)
>>     errorCondition(conditionMessage(w),
>>                    warning = w,
>>                   class = "warningToError")
>>
>> and use stop(warningToError(w)).
>>
>> If you call stop() with a condition argument then that is the
>> condition stop() will signal, regardless of its class. I can't at the
>> moment think of a good reason why I would want to call stop() with a
>> warning condition argument, and I suspect most cases where that
>> happens would be mistakes. So checking in stop() that a condition
>> argument inherits from "error" and signaling a warning, or maybe an
>> error, if it does not might be worth considering (with analogous
>> changes for warning() and message()).
>>
>> The condition system separates the signaling protocol from the process
>> of determining handlers. Signaling itself is done by
>> signalCondition().  message() and warning() signal a condition with a
>> muffle restart available, and return if the condition is not handled.
>> stop() is guaranteed not to return; if the condition is not handled,
>> then it invokes the default error handler, which will not return. None
>> of these currently look at the class of the condition.
>> signalCondition() looks at the condition's class to find out what
>> handlers are available. It will invoke error handlers for error
>> conditions and warning handlers for warning conditions.  It does not
>> know or care about whether it was called from stop(), warning(),
>> message(), or some other way.
>>
>> The most common high-level usage of stop(), warning(), or message() is
>> to call them with a string and possibly some additional arguments used
>> to create a message. In these cases a condition object of class
>> "error" for stop(), "warning" for warning(), and "message" for message
>> is created implicitly and signaled.
>>
>> Calling these functions with a condition argument is using lower level
>> functionality, which gives more power but also means users need to
>> understand what they are doing. In particular, users who want to call
>> stop() with a condition argument _and_ want handlers for error
>> conditions to be used need to make sure that the class of the
>> condition they signal inherits from "error".
>>
>> Best,
>>
>> luke
>>
>> On Tue, 1 Mar 2022, Andreas Kersting wrote:
>>
>>> Hi,
>>>
>>> There is the same issue with stop():
>>>
>>>> w <- simpleWarning("careful")
>>>> tryCatch(stop(w), condition = identity)
>>> <simpleWarning: careful>
>>>
>>> I very recently stumbled upon this, when a warning was re-raised as an error, which was then not caught by an outer try():
>>>
>>>> try(
>>> +   tryCatch(warning("careful"), warning = function(w) stop(w)),
>>> +   silent = TRUE
>>> + )
>>> Error in doTryCatch(return(expr), name, parentenv, handler) : careful
>>>
>>> I would also like to see this behavior changed. I think that stop() should always signal an error, warning() a warning and message() a message.
>>>
>>> Best,
>>> Andreas
>>>
>>> 2022-03-01 19:38 GMT+01:00 "Henrik Bengtsson" <henrik.bengtsson using gmail.com>:
>>>> Hi, in help("message", package = "base"), we can read:
>>>>
>>>> Description: 'message' is used for generating 'simple' diagnostic
>>>> messages which are neither warnings nor errors, but nevertheless
>>>> represented as conditions.
>>>>
>>>> From this, I conclude that message() should generate a condition that
>>>> are neither warning nor errors.
>>>>
>>>> However, the following signals a condition of class 'error':
>>>>
>>>>> e <- simpleError("boom!\n")
>>>>> message(e)
>>>> boom!
>>>>
>>>> This can be seen if we do:
>>>>
>>>>> res <- tryCatch(message(e), condition = identity)
>>>>> res
>>>> <simpleError: boom!
>>>>
>>>> This stems from message(e) using signalCondition(e) internally.
>>>>
>>>> Another problem with this behavior is that message(e) cannot be suppressed:
>>>>
>>>>> suppressMessages(message(e))
>>>> boom!
>>>>
>>>> or captured with calling handlers, e.g.
>>>>
>>>>> res <- withCallingHandlers(message(e), condition = identity)
>>>> boom!
>>>>> res
>>>> NULL
>>>>
>>>> If we replace e <- simpleError("boom") with e <-
>>>> simpleWarning("careful"), we see a similar behavior.  These problems
>>>> exist also with warning(e).  The current behaviors prevent functions
>>>> from capturing and relaying message(<error>), message(<warning>), and
>>>> warning(<error>).
>>>>
>>>> I'm happy to post a bug report to <https://bugs.r-project.org/>.
>>>>
>>>> /Henrik
>>>>
>>>> PS. BTW, it looks like some recent "..." tweaks to the warning() and
>>>> stop() code could be applied also to message().
>>>>
>>>> ______________________________________________
>>>> R-devel using r-project.org mailing list
>>>> https://stat.ethz.ch/mailman/listinfo/r-devel
>>>>
>>> ______________________________________________
>>> R-devel using r-project.org mailing list
>>> https://stat.ethz.ch/mailman/listinfo/r-devel
>>>
>>
>> --
>> Luke Tierney
>> Ralph E. Wareham Professor of Mathematical Sciences
>> University of Iowa                  Phone:             319-335-3386
>> Department of Statistics and        Fax:               319-335-3017
>>     Actuarial Science
>> 241 Schaeffer Hall                  email:   luke-tierney using uiowa.edu
>> Iowa City, IA 52242                 WWW:  http://www.stat.uiowa.edu
>

-- 
Luke Tierney
Ralph E. Wareham Professor of Mathematical Sciences
University of Iowa                  Phone:             319-335-3386
Department of Statistics and        Fax:               319-335-3017
    Actuarial Science
241 Schaeffer Hall                  email:   luke-tierney using uiowa.edu
Iowa City, IA 52242                 WWW:  http://www.stat.uiowa.edu



More information about the R-devel mailing list