2016-10-12 10 views
2

Класс Python выполняет R-функции через rpy2, и я хотел бы иметь возможность захватить трассировку из R в том случае, если функция R генерирует ошибку.Захват трассировки из R при поднятии RRuntimeError

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() является парным списком, который rpy2 can work with just fine, но есть an issue that prevents repr from working correctly. Чтобы сделать код более легким для отладки, он использует unlist для преобразования парного списка в список.

Следует помнить, что traceback() также отправляет трассировку на стандартный вывод, и нет способа (что я знаю), чтобы избежать этого, кроме [временно] переопределить 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.

1

rpy2 может обрабатывать парные списки (в основном). Однако их представление (метод __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 
+0

Спасибо за разъяснения! Я зарегистрировал [проблему на rpy2 repo] (https://bitbucket.org/rpy2/rpy2/issues/380/valueerror-when-getting-repr-of-a-pairlist) для последующего наблюдения. – user5568265