iuke-tier@ey m@iii@g oii uiow@@edu
2022-Mar-01 23:12 UTC
[Rd] [External] Re: message(<cond>) and warning(<cond>) circumvent calling handlers and signal the original class, e.g. an error
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 at 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 at r-project.org mailing list >> https://stat.ethz.ch/mailman/listinfo/r-devel >> > ______________________________________________ > R-devel at 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 at uiowa.edu Iowa City, IA 52242 WWW: http://www.stat.uiowa.edu
Henrik Bengtsson
2022-Mar-02 02:54 UTC
[Rd] [External] Re: message(<cond>) and warning(<cond>) circumvent calling handlers and signal the original class, e.g. an error
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 at 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 at 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 at r-project.org mailing list > >> https://stat.ethz.ch/mailman/listinfo/r-devel > >> > > ______________________________________________ > > R-devel at 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 at uiowa.edu > Iowa City, IA 52242 WWW: http://www.stat.uiowa.edu