Sklyar, Oleg (London)
2010-Jul-21 09:36 UTC
[Rd] Bug: broken exception handling in S4 methods
Hi all: we have noticed for quite a while that certain errors cannot be handled by R try, tryCatch etc blocks, but it was fairly difficult to understand what were the conditions for this incorrect behaviour. Finally I stabbed across a very understandable case, which is outlined in the (runnable) example below. The main message is: wrapping an S4 method call in a try block will not help if an error occurs in evaluating an argument to such a call; this works just fine for function calls (as opposed to S4 methods) The particular example is a result of trying to write a unit test for a constructor of a class object which should fail under certain conditions. This failure obviously need to be caught for the unit test to proceed. However, it is a general problem with handling some exceptions in R. # Consider a simple class MyClassA, which is derived from numeric and for which # we define a constructor (in form of a method). On its own this class works nicely # and so does exception handling of it: setClass("MyClassA", contains = "numeric", validity = function(object) { stopifnot(object[1] == object[2]) TRUE } ) setGeneric("MyClassA", function(x) standardGeneric("MyClassA")) setMethod("MyClassA", signature(x = "numeric"), function(x) { new("MyClassA", x) } ) ## OK er = try({ MyClassA(c(1,2)) }) ## OK (error in constructor) er = try({ MyClassA(c(1,2)) }) ## OK (error evaluating argument to a function) er = try({ sin(MyClassA(c(1,2))) }) # Now consider we define MyClassB that has MyClassA in a slot # and we define a constructor that takes such objects: setClassUnion("MyClassAOrNULL", c("MyClassA", "NULL")) setClass("MyClassB", representation( ca = "MyClassAOrNULL" ), prototype(ca = NULL) ) setGeneric("MyClassB", function(x) standardGeneric("MyClassB")) setMethod("MyClassB", signature(x = "MyClassA"), function(x) { new("MyClassB", ca = x) } ) ## OK b = MyClassB(MyClassA(c(1,1))) ## FAILS (error evaluating argument to a method) er = try({ MyClassB(MyClassA(c(1,2))) }) # As you see the last error cannot be handled # Moreover. If we define a function and a method as function(x) x then # the function can be handled by try, yet the method cannot: f = function(x) x setGeneric("g", function(x) standardGeneric("g")) setMethod("g", "MyClassA", function(x) x) ## OK (error evaluating argument to a function) er = try({ f(MyClassA(c(1,2))) }) ## FAILS (error evaluating argument to a method) er = try({ g(MyClassA(c(1,2))) })> sessionInfo()R version 2.11.0 Patched (2010-05-05 r51914) x86_64-unknown-linux-gnu locale: [1] LC_CTYPE=en_GB LC_NUMERIC=C LC_TIME=en_GB LC_COLLATE=en_GB [5] LC_MONETARY=C LC_MESSAGES=en_GB LC_PAPER=en_GB LC_NAME=C [9] LC_ADDRESS=C LC_TELEPHONE=C LC_MEASUREMENT=en_GB LC_IDENTIFICATION=C attached base packages: [1] splines stats graphics utils datasets grDevices methods base Dr Oleg Sklyar Research Technologist AHL / Man Investments Ltd +44 (0)20 7144 3803 osklyar at ahl.com ********************************************************************** Please consider the environment before printing this email or its attachments. The contents of this email are for the named addressees ...{{dropped:19}}
The problem in this example is (plausibly) that the argument evaluation code in method selection itself uses an internal C-level version of try(), overriding the user's setting. If this is the bug, I'll have to defer to more expert advice on whether, and if so how, the code can adjust for the current exception handling. (There don't seem to be many uses of this R_try() mechanism.) On 7/21/10 2:36 AM, Sklyar, Oleg (London) wrote:> Hi all: > > we have noticed for quite a while that certain errors cannot be handled > by R try, tryCatch etc blocks, but it was fairly difficult to understand > what were the conditions for this incorrect behaviour. Finally I stabbed > across a very understandable case, which is outlined in the (runnable) > example below. > > The main message is: wrapping an S4 method call in a try block will not > help if an error occurs in evaluating an argument to such a call; this > works just fine for function calls (as opposed to S4 methods) > > The particular example is a result of trying to write a unit test for a > constructor of a class object which should fail under certain > conditions. This failure obviously need to be caught for the unit test > to proceed. However, it is a general problem with handling some > exceptions in R. > > # Consider a simple class MyClassA, which is derived from numeric and > for which > # we define a constructor (in form of a method). On its own this class > works nicely > # and so does exception handling of it: > > setClass("MyClassA", > contains = "numeric", > validity = function(object) > { > stopifnot(object[1] == object[2]) > TRUE > } > ) > > > setGeneric("MyClassA", function(x) standardGeneric("MyClassA")) > > setMethod("MyClassA", > signature(x = "numeric"), > function(x) > { > new("MyClassA", x) > } > ) > > ## OK > er = try({ MyClassA(c(1,2)) }) > > ## OK (error in constructor) > er = try({ MyClassA(c(1,2)) }) > > ## OK (error evaluating argument to a function) > er = try({ sin(MyClassA(c(1,2))) }) > > > # Now consider we define MyClassB that has MyClassA in a slot > # and we define a constructor that takes such objects: > > > setClassUnion("MyClassAOrNULL", c("MyClassA", "NULL")) > > setClass("MyClassB", > representation( > ca = "MyClassAOrNULL" > ), > prototype(ca = NULL) > ) > > setGeneric("MyClassB", function(x) standardGeneric("MyClassB")) > > setMethod("MyClassB", > signature(x = "MyClassA"), > function(x) > { > new("MyClassB", ca = x) > } > ) > > ## OK > b = MyClassB(MyClassA(c(1,1))) > > ## FAILS (error evaluating argument to a method) > er = try({ MyClassB(MyClassA(c(1,2))) }) > > # As you see the last error cannot be handled > > > # Moreover. If we define a function and a method as function(x) x then > # the function can be handled by try, yet the method cannot: > > f = function(x) x > > setGeneric("g", function(x) standardGeneric("g")) > setMethod("g", "MyClassA", function(x) x) > > ## OK (error evaluating argument to a function) > er = try({ f(MyClassA(c(1,2))) }) > > ## FAILS (error evaluating argument to a method) > er = try({ g(MyClassA(c(1,2))) }) > > > >> sessionInfo() > R version 2.11.0 Patched (2010-05-05 r51914) > x86_64-unknown-linux-gnu > > locale: > [1] LC_CTYPE=en_GB LC_NUMERIC=C LC_TIME=en_GB > LC_COLLATE=en_GB > [5] LC_MONETARY=C LC_MESSAGES=en_GB LC_PAPER=en_GB > LC_NAME=C > [9] LC_ADDRESS=C LC_TELEPHONE=C LC_MEASUREMENT=en_GB > LC_IDENTIFICATION=C > > attached base packages: > [1] splines stats graphics utils datasets grDevices methods > base > > > Dr Oleg Sklyar > Research Technologist > AHL / Man Investments Ltd > +44 (0)20 7144 3803 > osklyar at ahl.com > > ********************************************************************** > Please consider the environment before printing this email or its attachments. > The contents of this email are for the named addressees ...{{dropped:19}} > > ______________________________________________ > R-devel at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel >