Номери рядків сценарію R при помилці?


105

Якщо я використовую довгий R-скрипт з командного рядка (R --slave script.R), як я можу змусити його вводити номери рядків при помилках?

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


31
Будь які оновлення? Через чотири роки, здається, проблема все ще зберігається, незважаючи на все основне прийняття Р.
Гай Амброс

У мене також дуже довгий R-скрипт з великою кількістю невеликого виводу, я хочу надрукувати (підкреслення) (підкреслення) ЛІНІЯ / ФАЙЛ (підкреслення) (підкреслення) (номери рядків та ім'я сценарію), як це в C, замість жорсткого кодування рядкових чисел в джерело.
мош

Я не знаю, чи R всередині насправді має поняття "числа рядків". Однак у нього є поняття про завершені завдання, тобто завдання вищого рівня. Наприклад, можна було б легко визначити обробник завдань, щоб визначити, яке завдання вищого рівня не вдалося. Звичайно, це не є великим комфортом для тих, хто має великі ланцюги або великі умовні висловлювання.
russellpierce

Відповіді:


45

Це не дасть вам номер лінії, але підкаже, де відбувається збій у стеку викликів, що дуже корисно:

traceback()

[Редагувати:] Під час запуску скрипту з командного рядка вам доведеться пропустити один-два виклики, див. Прослідкування () для інтерактивних та неінтерактивних сеансів R

Я не знаю іншого способу зробити це без звичайних підозрюваних у відладці:

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

Можливо, ви захочете переглянути цю пов’язану публікацію.

[Редагувати:] Вибачте ... щойно побачив, що ви запускаєте це з командного рядка. У цьому випадку я б запропонував працювати з параметрами (помилка) функціоналом. Ось простий приклад:

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
новий варіант, який дозволить вам отримати номери рядків у traceback github.com/aryoda/tryCatchLog
lunguini

13

Додавання options(error=traceback)надає трохи більше інформації про вміст рядків, що призводить до помилки. Це призводить до появи зворотного відстеження, якщо є помилка, а для деяких помилок - номер рядка з префіксом #. Але це удар або промах, багато помилок не отримають номери рядків.


2
Це не дуже працює для мене. У мене є лише один файл, і він не показує номер рядка, просто каже No traceback availableпісля помилки.
Марк Лаката

11

Підтримка цього буде надана в R 2.10 та пізніше. Дункан Мердок щойно опублікував 10 вересня 2009 року на r-devel про 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-довідки за припущенням, що він буде засмічувати папку "Вхідні" (r-sig-finance вже це робить).
Шейн

1
насправді не розумію, як це працює з командного рядка, не
торкаючись

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
irritable_phd_syndrom

Можливо, варто згадати, ви повинні встановити параметри на передній панелі, перш ніж отримати будь-який код.
JAponte

3

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

Як я налаштував, обробка помилок також створює файл 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, і ви хочете, щоб інформація зберігалася про несподівані помилки. Можливість скидання пам’яті у файл, який ви можете використовувати для перевірки робочої пам’яті під час помилки, а також номери рядків помилки в стеці виклику, сприяють швидкому післясмертовому налагодженню того, що спричинило помилку.


0

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.