Nomor baris skrip R salah?

105

Jika saya menjalankan skrip R panjang dari baris perintah (R --slave script.R), bagaimana saya bisa membuatnya memberikan nomor baris pada saat kesalahan?

Saya tidak ingin menambahkan perintah debug ke skrip jika memungkinkan - saya hanya ingin R berperilaku seperti kebanyakan bahasa skrip lainnya ...

forkandwait
sumber
31
Ada pembaruan? Empat 4 tahun kemudian, tampaknya masalah masih berlanjut, meskipun semua adopsi umum R.
Gui Ambros
Saya juga memiliki skrip R yang sangat panjang dengan banyak output kecil, saya ingin mencetak (garis bawah) (garis bawah) LINE / FILE (garis bawah) (garis bawah) (nomor baris dan nama skrip) seperti itu di C, daripada nomor baris hardcode menjadi sumber.
mosh
Saya tidak tahu apakah R secara internal benar-benar memiliki gagasan tentang 'nomor baris'. Namun, ia memiliki gagasan tentang tugas yang lengkap, yaitu tugas tingkat atas. Misalnya, seseorang dapat dengan mudah menentukan penangan tugas untuk memberi tahu tugas tingkat atas mana yang gagal. Tentu saja, itu bukan kenyamanan besar bagi mereka yang memiliki rantai besar atau pernyataan bersyarat yang besar.
russellpierce

Jawaban:

45

Ini tidak akan memberi Anda nomor baris, tetapi ini akan memberi tahu Anda di mana kegagalan terjadi dalam tumpukan panggilan yang sangat membantu:

traceback()

[Edit:] Saat menjalankan skrip dari baris perintah, Anda harus melewati satu atau dua panggilan, lihat traceback () untuk sesi R interaktif dan non-interaktif

Saya tidak mengetahui cara lain untuk melakukan ini tanpa tersangka debugging yang biasa:

  1. debug ()
  2. browser ()
  3. opsi (error = pulih) [diikuti oleh opsi (error = NULL) untuk mengembalikannya]

Anda mungkin ingin melihat posting terkait ini.

[Edit:] Maaf ... baru saja melihat Anda menjalankan ini dari baris perintah. Dalam hal ini saya akan menyarankan bekerja dengan fungsionalitas opsi (kesalahan). Berikut contoh sederhananya:

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

Anda dapat membuat skrip serumit mungkin pada kondisi kesalahan, jadi Anda harus memutuskan informasi apa yang Anda perlukan untuk debugging.

Sebaliknya, jika ada area tertentu yang Anda khawatirkan (mis. Menghubungkan ke database), maka gabungkan mereka dalam fungsi tryCatch ().

Shane
sumber
Solusi yang ditautkan dalam blok [Sunting:] pertama bekerja untuk saya. Pendekatan terbaik tampaknya adalah komentar @dshepherd, yaitu tambahkan options(error=function() { traceback(2); if(!interactive()) quit("no", status = 1, runLast = FALSE) })(lihat komentar jawaban yang diterima). Saya pikir masuk akal untuk menambahkannya ke jawaban di sini daripada hanya memberikan tautan ke utas lain.
cryo111
1
opsi baru yang memungkinkan Anda mendapatkan nomor baris di traceback github.com/aryoda/tryCatchLog
lunguini
13

Melakukan options(error=traceback)memberikan lebih banyak informasi tentang konten baris yang mengarah ke kesalahan. Ini menyebabkan pelacakan balik muncul jika ada kesalahan, dan untuk beberapa kesalahan memiliki nomor baris, diawali dengan #. Tapi itu hit atau miss, banyak kesalahan tidak akan mendapatkan nomor baris.

Hugh Perkins
sumber
2
Tidak cukup berhasil untuk saya. Saya hanya punya satu file, dan itu tidak menunjukkan nomor baris, hanya mengatakan No traceback availablesetelah kesalahan.
Mark Lakata
11

Dukungan untuk ini akan datang di R 2.10 dan yang lebih baru. Duncan Murdoch baru saja memposting ke r-devel pada 10 Sep 2009 tentang findLineNum dan setBreapoint :

Saya baru saja menambahkan beberapa fungsi ke R-devel untuk membantu debugging. findLineNum()menemukan baris mana yang fungsinya sesuai dengan baris kode sumber tertentu; setBreakpoint()mengambil output dari findLineNum, dan memanggil trace()untuk menyetel breakpoint di sana.

Ini bergantung pada memiliki informasi debug referensi sumber dalam kode. Ini adalah default untuk kode yang dibaca source(), tetapi tidak untuk paket. Untuk mendapatkan referensi sumber dalam kode paket, setel variabel lingkungan R_KEEP_PKG_SOURCE=yes, atau di dalam R, setel options(keep.source.pkgs=TRUE), lalu instal paket dari kode sumber. Baca ?findLineNumdetailnya tentang bagaimana memberitahukannya untuk mencari di dalam paket, daripada membatasi pencarian ke lingkungan global.

Sebagai contoh,

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=)

Ini akan mencetak

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

dan Anda bisa menggunakan

setBreakpoint("<text>#3")

untuk mengatur breakpoint di sana.

Masih ada beberapa batasan (dan mungkin bug) dalam kode; Aku akan memperbaikinya

Dirk Eddelbuettel
sumber
Terima kasih. Baru saja mendaftar ke milis r-devel juga. Saya telah menghindari r-help dengan asumsi bahwa itu akan menyumbat kotak masuk saya (r-sig-finance sudah melakukan itu).
Shane
1
tidak begitu mengerti bagaimana ini bekerja dari baris perintah tanpa melihat-lihat di skrip R
Herman Toothrot
1
@hirse: Ini hampir sepuluh jawaban lama Anda. Mengapa Anda memformat ulang untuk berpura-pura saya mengutip? Saya tidak, dan perubahan Anda tidak mencerminkan niat saya.
Dirk Eddelbuettel
"Duncan Murdoch baru saja memposting:" terdengar sangat mirip dengan kutipan, tetapi jika itu tidak benar, harap kembalikan hasil editnya. Saya ingin membuatnya lebih mudah dibaca untuk diri saya sendiri dan tidak memeriksa tanggalnya sampai saya selesai. Jika seluruh jawaban terlalu usang, Anda juga dapat menghapusnya untuk menghilangkan kebingungan bagi pembaca selanjutnya.
hirse
Bisakah Anda mengembalikannya? Terima kasih.
Dirk Eddelbuettel
6

Anda melakukannya dengan mengatur

options(show.error.locations = TRUE)

Saya hanya bertanya-tanya mengapa pengaturan ini bukan default di R? Seharusnya demikian, seperti dalam setiap bahasa lainnya.

TMS
sumber
1
Untuk informasi latar belakang tentang opsi ini lihat stat.ethz.ch/R-manual/R-devel/library/base/html/options.html
R Yoda
1
Ini dulu berfungsi, tetapi dinonaktifkan karena tidak dapat diandalkan. Saya pikir ini adalah upaya untuk memaksa Anda menggunakan RStudio yang pada akhirnya tidak akan gratis.
Eric Leschinski
6
Aku meragukan itu. R core dan RStudio adalah organisasi yang sangat berbeda, dan R core khususnya merupakan sumber terbuka yang setia.
Ben Bolker
Bekerja pada CentOS 6.9, R-3.4.2
iritable_phd_syndrom
Mungkin perlu disebutkan, Anda harus mengatur opsi terlebih dahulu, sebelum mencari kode apa pun.
JAponte
3

Menentukan opsi R global untuk menangani kesalahan non-katastropik berhasil untuk saya, bersama dengan alur kerja yang disesuaikan untuk menyimpan info tentang kesalahan dan memeriksa info ini setelah kegagalan. Saya saat ini menjalankan R versi 3.4.1. Di bawah ini, saya telah menyertakan deskripsi alur kerja yang berhasil untuk saya, serta beberapa kode yang saya gunakan untuk mengatur opsi penanganan kesalahan global di R.

Seperti yang saya konfigurasikan, penanganan kesalahan juga membuat file RData yang berisi semua objek dalam memori kerja pada saat terjadi kesalahan. Dump ini dapat dibaca kembali ke R menggunakan load()dan kemudian berbagai lingkungan seperti yang ada pada saat kesalahan dapat diperiksa secara interaktif menggunakan debugger(errorDump).

Saya akan mencatat bahwa saya bisa mendapatkan nomor baris dalam traceback()output dari fungsi khusus apa pun dalam tumpukan, tetapi hanya jika saya menggunakan keep.source=TRUEopsi saat memanggil source()fungsi khusus apa pun yang digunakan dalam skrip saya. Tanpa opsi ini, pengaturan opsi penanganan kesalahan global seperti di bawah ini mengirimkan output penuh traceback()ke log kesalahan bernama error.log, tetapi nomor baris tidak tersedia.

Berikut adalah langkah-langkah umum yang saya ambil dalam alur kerja saya dan bagaimana saya dapat mengakses dump memori dan log kesalahan setelah kegagalan R non-interaktif.

  1. Saya meletakkan yang berikut ini di bagian atas skrip utama yang saya panggil dari baris perintah. Ini menetapkan opsi penanganan kesalahan global untuk sesi R. Skrip utama saya dipanggil myMainScript.R. Berbagai baris dalam kode memiliki komentar setelah mereka menjelaskan apa yang mereka lakukan. Pada dasarnya, dengan opsi ini, ketika R menemukan kesalahan yang memicu stop(), itu akan membuat file dump RData (* .rda) dari memori kerja di semua lingkungan aktif dalam direktori ~/myUsername/directoryForDumpdan juga akan menulis log kesalahan yang diberi nama error.logdengan beberapa informasi berguna ke direktori yang sama. Anda dapat memodifikasi potongan ini untuk menambahkan penanganan lain pada kesalahan (misalnya, menambahkan stempel waktu ke file dump dan nama file log kesalahan, dll.).

    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. Pastikan bahwa dari skrip utama dan pemanggilan fungsi berikutnya, kapan pun suatu fungsi bersumber, opsi keep.source=TRUEtersebut digunakan. Artinya, untuk mencari fungsi, Anda akan menggunakan source('~/path/to/myFunction.R', keep.source=TRUE). Ini diperlukan agar traceback()keluaran berisi nomor baris. Sepertinya Anda juga dapat menyetel opsi ini secara global options( keep.source=TRUE ), tetapi saya belum menguji ini untuk melihat apakah berhasil. Jika Anda tidak memerlukan nomor baris, Anda dapat menghilangkan opsi ini.

  3. Dari terminal (di luar R), panggil skrip utama dalam mode batch menggunakan Rscript myMainScript.R. Ini memulai sesi R non-interaktif baru dan menjalankan skrip myMainScript.R. Potongan kode yang diberikan di langkah 1 yang telah ditempatkan di bagian atas myMainScript.Rmenetapkan opsi penanganan kesalahan untuk sesi R non-interaktif.
  4. Menjumpai kesalahan di suatu tempat dalam eksekusi myMainScript.R. Ini mungkin dalam skrip utama itu sendiri, atau beberapa fungsi bertingkat dalam. Ketika kesalahan ditemukan, penanganan akan dilakukan seperti yang ditentukan pada langkah 1, dan sesi R akan berakhir.
  5. Berkas dump RData bernama errorDump.rdadan dan log galat bernama error.logdibuat di direktori yang ditentukan oleh '~/myUsername/directoryForDump'dalam pengaturan opsi penanganan galat global.
  6. Di waktu luang Anda, periksa error.loguntuk meninjau informasi tentang kesalahan tersebut, termasuk pesan kesalahan itu sendiri dan jejak tumpukan lengkap yang mengarah ke kesalahan tersebut. Berikut adalah contoh log yang dihasilkan karena kesalahan; perhatikan angka setelah #karakter adalah nomor baris kesalahan di berbagai titik dalam tumpukan panggilan:

    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. Di waktu luang Anda, Anda dapat memuat errorDump.rdasesi R interaktif menggunakan load('~/path/to/errorDump.rda'). Setelah dimuat, panggil debugger(errorDump)untuk menelusuri semua objek R dalam memori di salah satu lingkungan aktif. Lihat bantuan R debugger()untuk info lebih lanjut.

Alur kerja ini sangat membantu saat menjalankan R di beberapa jenis lingkungan produksi di mana Anda memiliki sesi R non-interaktif yang dimulai pada baris perintah dan Anda ingin informasi disimpan tentang kesalahan yang tidak terduga. Kemampuan untuk membuang memori ke file yang dapat Anda gunakan untuk memeriksa memori kerja pada saat terjadi kesalahan, bersama dengan memiliki nomor baris kesalahan dalam tumpukan panggilan, memfasilitasi debugging post-mortem yang cepat dari apa yang menyebabkan kesalahan.

bmosov01
sumber
0

Pertama, options(show.error.locations = TRUE)lalu traceback(). Nomor baris yang salah akan ditampilkan setelah #

den2042
sumber