Номера строк сценария R при ошибке?

105

Если я запускаю длинный сценарий R из командной строки (R --slave script.R), как я могу заставить его выдавать номера строк при ошибках?

Я не хочу добавлять в сценарий команды отладки, если это вообще возможно - я просто хочу, чтобы R вел себя как большинство других языков сценариев ...

вилка
источник
31
Любые обновления? Четыре, 4 года спустя, похоже, проблема все еще сохраняется, несмотря на широкое распространение Р.
Ги Амброс
У меня также есть очень длинный сценарий R с большим количеством небольших выходных данных, я хочу напечатать (подчеркивание) (подчеркивание) СТРОКА / ФАЙЛ (подчеркивание) (подчеркивание) (номера строк и имя сценария), как это в C, вместо жесткого кодирования номеров строк в источник.
mosh
Я не знаю, действительно ли R внутренне имеет понятие «номера строк». Однако в нем есть понятие завершенных задач, то есть задач верхнего уровня. Например, можно легко определить обработчик задачи, который сообщал бы, какая задача верхнего уровня завершилась неудачно. Конечно, это не очень удобно для тех, у кого большие цепочки или большие условные выражения.
russellpierce

Ответы:

45

Это не даст вам номер строки, но сообщит вам, где происходит сбой в стеке вызовов, что очень полезно:

traceback()

[Edit:] При запуске сценария из командной строки вам придется пропустить один или два вызова, см. Traceback () для интерактивных и неинтерактивных сеансов R

Я не знаю другого способа сделать это без обычных подозреваемых в отладке:

  1. отлаживать()
  2. браузер ()
  3. параметры (ошибка = восстановление) [затем параметры (ошибка = NULL) для восстановления]

Возможно, вы захотите посмотреть этот связанный пост.

[Edit:] Извините ... только что увидел, что вы запускаете это из командной строки. В этом случае я бы посоветовал поработать с функциональностью опций (ошибок). Вот простой пример:

options(error = quote({dump.frames(to.file=TRUE); q()}))

Вы можете создать сколь угодно подробный сценарий для ошибки, поэтому вам нужно просто решить, какая информация вам нужна для отладки.

В противном случае, если есть определенные области, которые вас беспокоят (например, подключение к базе данных), заключите их в функцию tryCatch ().

Шейн
источник
Решение, указанное в первом блоке [Edit:], мне подходит. Лучшим подходом, по-видимому, является комментарий @dshepherd, то есть добавить options(error=function() { traceback(2); if(!interactive()) quit("no", status = 1, runLast = FALSE) })(см. Комментарий принятого ответа). Я думаю, было бы разумнее добавить его к ответу здесь, а не просто указывать ссылку на другой поток.
cryo111
1
новая опция, которая позволяет вам получить номера строк в
трассировке
13

Выполнение options(error=traceback)дает немного больше информации о содержании строк, ведущих к ошибке. Он вызывает появление трассировки, если есть ошибка, и для некоторых ошибок он имеет номер строки с префиксом #. Но это удачно или нет, многие ошибки не получают номеров строк.

Хью Перкинс
источник
2
У меня не совсем работает. У меня только один файл, и он не показывает номер строки, просто говорит No traceback availableпосле ошибки.
Марк Лаката,
11

Поддержка этого появится в R 2.10 и позже. Дункан Мердок только что написал в r-devel 10 сентября 2009 г. о findLineNum и setBreapoint :

Я только что добавил в R-devel пару функций, чтобы помочь с отладкой. findLineNum()находит, какая строка какой функции соответствует конкретной строке исходного кода; setBreakpoint()принимает выходные данные findLineNumи вызывает trace()там точку останова.

Они полагаются на наличие отладочной информации ссылки на источник в коде. Это значение по умолчанию для кода, читаемого source(), но не для пакетов. Чтобы получить ссылки на исходный код в коде пакета, установите переменную среды R_KEEP_PKG_SOURCE=yesили в R, установите options(keep.source.pkgs=TRUE), а затем установите пакет из исходного кода. Прочтите, ?findLineNumчтобы узнать, как указать ему искать внутри пакетов, а не ограничивать поиск глобальной средой.

Например,

x <- " f <- function(a, b) {
             if (a > b)  {
                 a
             } else {
                 b
             }
         }"


eval(parse(text=x))  # Normally you'd use source() to read a file...

findLineNum("<text>#3")   # <text> is a dummy filename used by
parse(text=)

Это напечатает

 f step 2,3,2 in <environment: R_GlobalEnv>

и вы можете использовать

setBreakpoint("<text>#3")

чтобы установить там точку останова.

В коде все еще есть некоторые ограничения (и, возможно, ошибки); Я исправлю это

Дирк Эддельбюттель
источник
Спасибо. Просто подписался на список рассылки r-devel. Я избегал r-help, полагая, что он забьет мой почтовый ящик (r-sig-finance уже делает это).
Шейн
1
не очень понимаю, как это работает из командной строки, не ковыряясь в сценарии R
Герман Зубец
1
@hirse: Это почти десять твоих старых ответов. С какой стати вы переформатировали его, чтобы сделать вид, будто я цитирую? Я не был, и ваша сдача не отражает моих намерений.
Дирк Эддельбюттель,
"Дункан Мердок только что написал:" звучит очень похоже на цитату, но если это неверно, отмените редактирование. Я хотел сделать его более читаемым для себя и не проверял дату, пока не закончил. Если весь ответ слишком устарел, вы также можете удалить его, чтобы не запутать будущих читателей.
hirse
Не могли бы вы отменить его? Спасибо.
Дирк Эддельбюттель,
6

Вы делаете это, устанавливая

options(show.error.locations = TRUE)

Мне просто интересно, почему этот параметр не установлен по умолчанию в R? Так должно быть, как и на любом другом языке.

ТМС
источник
1
Для получения дополнительной информации об этой опции см. Stat.ethz.ch/R-manual/R-devel/library/base/html/options.html
R Yoda
1
Раньше это работало, но было отключено, потому что это ненадежно. Я думаю, что это попытка заставить вас использовать RStudio, который в конечном итоге станет платным.
Эрик Лещински
6
Я сомневаюсь в этом. R core и RStudio - очень разные организации, и, в частности, R core - стойкие разработчики с открытым исходным кодом.
Бен Болкер
Работал над CentOS 6.9, R-3.4.2
iryable_phd_synd с
Возможно, стоит упомянуть, что вам следует установить параметры заранее, прежде чем искать какой-либо код.
JAponte
3

У меня сработало указание глобальной опции R для обработки некатастрофических ошибок, а также настроенный рабочий процесс для сохранения информации об ошибке и изучения этой информации после сбоя. В настоящее время я использую R версии 3.4.1. Ниже я включил описание рабочего процесса, который работал у меня, а также код, который я использовал для установки параметра глобальной обработки ошибок в R.

Как я настроил, обработка ошибок также создает файл RData, содержащий все объекты в рабочей памяти на момент ошибки. Этот дамп может быть считан обратно в R с помощью, load()а затем различные среды, которые существовали на момент ошибки, могут быть проверены в интерактивном режиме с помощью debugger(errorDump).

Замечу, что мне удалось получить номера строк на traceback()выходе из любых пользовательских функций в стеке, но только если я использовал эту keep.source=TRUEопцию при вызове source()любых пользовательских функций, используемых в моем скрипте. Без этой опции установка опции глобальной обработки ошибок, как показано ниже, отправляла полный вывод traceback()в журнал ошибок с именем error.log, но номера строк были недоступны.

Вот общие шаги, которые я предпринял в своем рабочем процессе, и то, как я смог получить доступ к дампу памяти и журналу ошибок после неинтерактивного сбоя R.

  1. Я поместил следующее вверху основного сценария, который я вызвал из командной строки. Это устанавливает параметр глобальной обработки ошибок для сеанса R. Назывался мой основной сценарий myMainScript.R. После различных строк кода есть комментарии, описывающие, что они делают. По сути, с этой опцией, когда R обнаруживает срабатывающую ошибку stop(), он создает файл дампа рабочей памяти RData (* .rda) во всех активных средах в каталоге, ~/myUsername/directoryForDumpа также записывает журнал ошибок error.logс некоторой полезной информацией в тот же каталог. Вы можете изменить этот фрагмент, чтобы добавить другую обработку ошибок (например, добавить отметку времени в файл дампа, имена файлов журнала ошибок и т. Д.).

    options(error = quote({
      setwd('~/myUsername/directoryForDump'); # Set working directory where you want the dump to go, since dump.frames() doesn't seem to accept absolute file paths.
      dump.frames("errorDump", to.file=TRUE, include.GlobalEnv=TRUE); # First dump to file; this dump is not accessible by the R session.
      sink(file="error.log"); # Specify sink file to redirect all output.
      dump.frames(); # Dump again to be able to retrieve error message and write to error log; this dump is accessible by the R session since not dumped to file.
      cat(attr(last.dump,"error.message")); # Print error message to file, along with simplified stack trace.
      cat('\nTraceback:');
      cat('\n');
      traceback(2); # Print full traceback of function calls with all parameters. The 2 passed to traceback omits the outermost two function calls.
      sink();
      q()}))
  2. Убедитесь, что из основного скрипта и любых последующих вызовов функций, каждый раз при получении функции используется опция keep.source=TRUE. То есть, чтобы создать функцию, вы должны использовать source('~/path/to/myFunction.R', keep.source=TRUE). Это необходимо, чтобы traceback()вывод содержал номера строк. Похоже, вы также сможете установить эту опцию глобально options( keep.source=TRUE ), но я не проверял, работает ли она. Если вам не нужны номера строк, вы можете опустить эту опцию.

  3. Из терминала (вне R) вызовите основной скрипт в пакетном режиме, используя Rscript myMainScript.R. Это запускает новый неинтерактивный сеанс R и запускает сценарий myMainScript.R. Фрагмент кода, приведенный на шаге 1, который был помещен в начало, myMainScript.Rустанавливает параметр обработки ошибок для неинтерактивного сеанса R.
  4. Обнаружить ошибку где-то при выполнении myMainScript.R. Это может быть в самом основном скрипте или вложено в несколько функций глубоко. При обнаружении ошибки обработка будет выполняться, как указано в шаге 1, и сеанс R завершится.
  5. Файл дампа RData с именем errorDump.rdaи и журнал ошибок с именем error.logсоздаются в каталоге, указанном '~/myUsername/directoryForDump'параметром глобальной обработки ошибок.
  6. На досуге error.logпросмотрите информацию об ошибке, включая само сообщение об ошибке и полную трассировку стека, ведущую к ошибке. Вот пример журнала, созданного при ошибке; обратите внимание, что числа после #символа - это номера строк ошибки в различных точках стека вызовов:

    Error in callNonExistFunc() : could not find function "callNonExistFunc"
    Calls: test_multi_commodity_flow_cmd -> getExtendedConfigDF -> extendConfigDF
    
    Traceback:
    3: extendConfigDF(info_df, data_dir = user_dir, dlevel = dlevel) at test_multi_commodity_flow.R#304
    2: getExtendedConfigDF(config_file_path, out_dir, dlevel) at test_multi_commodity_flow.R#352
    1: test_multi_commodity_flow_cmd(config_file_path = config_file_path, 
    spot_file_path = spot_file_path, forward_file_path = forward_file_path, 
    data_dir = "../", user_dir = "Output", sim_type = "spot", 
    sim_scheme = "shape", sim_gran = "hourly", sim_adjust = "raw", 
    nsim = 5, start_date = "2017-07-01", end_date = "2017-12-31", 
    compute_averages = opt$compute_averages, compute_shapes = opt$compute_shapes, 
    overwrite = opt$overwrite, nmonths = opt$nmonths, forward_regime = opt$fregime, 
    ltfv_ratio = opt$ltfv_ratio, method = opt$method, dlevel = 0)
  7. На досуге вы можете загрузить errorDump.rdaв интерактивный сеанс R, используя load('~/path/to/errorDump.rda'). После загрузки вызовите debugger(errorDump)для просмотра всех объектов R в памяти в любой из активных сред. Для debugger()получения дополнительной информации см. Справку по R.

Этот рабочий процесс чрезвычайно полезен при запуске R в некоторой производственной среде, где у вас есть неинтерактивные сеансы R, инициируемые из командной строки, и вы хотите, чтобы информация о неожиданных ошибках сохранялась. Возможность делать дамп памяти в файл, который вы можете использовать для проверки рабочей памяти во время ошибки, наряду с номерами строк ошибки в стеке вызовов, облегчает быструю посмертную отладку причины ошибки.

bmosov01
источник
0

Сначала, options(show.error.locations = TRUE)а потом traceback(). Номер строки ошибки будет отображаться после #

den2042
источник