Захват трассировки из R при возникновении RRuntimeError
Класс Python выполняет функции R через rpy2, и я хотел бы иметь возможность получить обратную трассировку от R в случае, если функция R генерирует ошибку.
Код R является устаревшим, поэтому его изменение было бы очень рискованным; Я бы предпочел сделать что-то на стороне Python.
Вот как выглядит код Python в настоящее время:
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
...
Как я должен изменить call_raw
так что он захватывает трассировку от R в случае, если функция R вызывает ошибку?
2 ответа
traceback()
является функцией перехода для генерации трассировок ошибок в R. Использование rpy2.robjects.r
Вы можете оценить traceback()
Функция и сохранить результат непосредственно в переменную Python.
Примечание для rpy2 v2.8.x: результат traceback()
это pairlist, с которым rpy2 может работать просто отлично, но есть проблема, которая мешает repr
работать правильно. Чтобы сделать код проще для отладки, он использует unlist
чтобы преобразовать pairlist в список.
Быть в курсе, что traceback()
также отправляет трассировку в stdout, и я не знаю (как я знаю) способа избежать этого, кроме как [временно] переопределить sys.stdout
,
Вот как RAdapter.call_raw()
может захватить трассировку R:
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:
# Attempt to capture the traceback from R.
# noinspection SpellCheckingInspection
try:
# noinspection SpellCheckingInspection
e.context = {
# :kludge: Have to use `unlist` because `traceback`
# returns a pairlist, which rpy2 doesn't know how
# to handle.
'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
...
Протестировано с rpy2==2.8.3
,
rpy2
может справиться с R pairlists (в основном) нормально. Однако их представление (метод __repr__
), кажется, есть ошибка: общее __repr__
для векторов R используются срезы, а срезы недоступны для объектов паирлистов.
>>> from rpy2.robjects import baseenv
>>> opts = baseenv['.Options']
>>> opts.typeof # this is a pairlist
2
>>> print(opts) # working
...
>>> str(opts) # working
>>> opts.items() # working
>>> repr(opts) # ValueError: Cannot handle R type 2