Issue
A Python class executes R functions via rpy2, and I would like to be able to capture the traceback from R in the event that an R function generates an error.
The R code is legacy, so modifying it would be very risky; I would prefer to do something on the Python side.
Here's what the Python code looks like currently:
from rpy2.rinterface import RRuntimeError
from rpy2.robjects import DataFrame
from rpy2.robjects.packages import InstalledPackage
class RAdapter(BaseRAdapter):
_module = None # type: InstalledPackage
def call_raw(self, function_name, *args, **kwargs):
# type: (str, tuple, dict) -> DataFrame
"""
Invokes an R function and returns the result as a DataFrame.
"""
try:
return getattr(self._module, function_name)(*args, **kwargs)
except RRuntimeError as e:
# :todo: Capture traceback from R and attach to `e`.
e.context = {'r_traceback': '???'}
raise
...
How should I modify call_raw
so that it captures the traceback from R in the event that the R function causes an error?
Solution
traceback()
is the go-to function for generating error tracebacks in R. Using rpy2.robjects.r
, you can eval the traceback()
function and store the result directly to a Python variable.
Note for rpy2 v2.8.x: The result of traceback()
is a pairlist, which rpy2 can work with just fine, but there's an issue that prevents repr
from working correctly. To make the code easier to debug, it uses unlist
to convert the pairlist into a list.
Be aware that traceback()
also sends the traceback to stdout, and there is no way (that I know of) to avoid this, except to [temporarily] override sys.stdout
.
Here's how RAdapter.call_raw()
can capture the R traceback:
# If you are using rpy2 < 3.4.5 change the next line to:
# from rpy2.rinterface import RRuntimeError
from rpy2.rinterface_lib.embedded import RRuntimeError
from rpy2.robjects import DataFrame, r
from rpy2.robjects.packages import InstalledPackage
class RAdapter(BaseRAdapter):
_module = None # type: InstalledPackage
def call_raw(self, function_name, *args, **kwargs):
# type: (str, *typing.Any, **typing.Any) -> DataFrame
"""
Invokes an R function and returns the result as a DataFrame.
"""
try:
return getattr(self._module, function_name)(*args, **kwargs)
except RRuntimeError as e:
# Attempt to capture the traceback from R.
try:
e.context = {
# :kludge: Have to use `unlist` because `traceback`
# returns a pairlist, which rpy2 doesn't know how
# to repr.
'r_traceback': '\n'.join(r('unlist(traceback())'))
}
except Exception as traceback_exc:
e.context = {
'r_traceback':
'(an error occurred while getting traceback from R)',
'r_traceback_err': traceback_exc,
}
raise
...
Tested with rpy2==2.8.3
.
Answered By - todofixthis
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.