Numeri di riga dello script R in errore?


105

Se eseguo un lungo script R dalla riga di comando (R --slave script.R), come posso fare in modo che fornisca numeri di riga in caso di errori?

Non voglio aggiungere comandi di debug allo script, se possibile, voglio solo che R si comporti come la maggior parte degli altri linguaggi di scripting ...


31
Nessun aggiornamento? Quattro 4 anni dopo, sembra che il problema persista, nonostante tutta l'adozione mainstream di R.
Gui Ambros

Ho anche uno script R molto lungo con un sacco di piccoli output, voglio stampare (underscore) (underscore) LINE / FILE (underscore) (underscore) (numeri di riga e nome script) come quello in C, invece di codificare i numeri di riga nella fonte.
mosh

Non so se R internamente abbia davvero una nozione di "numeri di riga". Tuttavia, ha una nozione di attività complete, ovvero attività di livello superiore. Si potrebbe, ad esempio, definire facilmente un gestore di attività per indicare quale attività di primo livello non è riuscita. Ovviamente, questo non è un grande conforto per chi ha grandi catene o grandi affermazioni condizionali.
russellpierce

Risposte:


45

Questo non ti darà il numero di linea, ma ti dirà dove si verifica l'errore nello stack di chiamate che è molto utile:

traceback()

[Modifica:] Quando esegui uno script dalla riga di comando dovrai saltare una o due chiamate, vedi traceback () per sessioni R interattive e non interattive

Non sono a conoscenza di un altro modo per farlo senza i soliti sospetti di debug:

  1. debug ()
  2. del browser ()
  3. opzioni (errore = ripristino) [seguito da opzioni (errore = NULL) per ripristinarlo]

Potresti voler guardare questo post correlato.

[Modifica:] Scusa ... ho appena visto che lo stai eseguendo dalla riga di comando. In tal caso suggerirei di lavorare con la funzionalità delle opzioni (errore). Ecco un semplice esempio:

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

Puoi creare uno script elaborato quanto desideri in una condizione di errore, quindi dovresti solo decidere quali informazioni hai bisogno per il debug.

Altrimenti, se ci sono aree specifiche di cui sei preoccupato (ad esempio la connessione a un database), inseriscile in una funzione tryCatch ().


La soluzione collegata nel primo blocco [Modifica:] funziona per me. L'approccio migliore sembra essere il commento di @dshepherd, cioè add options(error=function() { traceback(2); if(!interactive()) quit("no", status = 1, runLast = FALSE) })(vedi commento della risposta accettata). Penso che avrebbe senso aggiungerlo alla risposta qui piuttosto che fornire solo un collegamento a un altro thread.
cryo111

1
una nuova opzione che ti permette di ottenere i numeri di riga nel traceback github.com/aryoda/tryCatchLog
lunguini

13

Fare options(error=traceback)fornisce qualche informazione in più sul contenuto delle righe che portano all'errore. Fa apparire un traceback se c'è un errore e per alcuni errori ha il numero di riga, preceduto da #. Ma è incostante, molti errori non ottengono i numeri di riga.


2
Non funziona abbastanza per me. Ho solo un file e non mostra il numero di riga, dice solo No traceback availabledopo l'errore.
Mark Lakata

11

Il supporto per questo sarà disponibile in R 2.10 e versioni successive. Duncan Murdoch ha appena postato su r-devel il 10 settembre 2009 riguardo a findLineNum e setBreapoint :

Ho appena aggiunto un paio di funzioni a R-devel per aiutare con il debug. findLineNum()trova quale riga di quale funzione corrisponde a una particolare riga di codice sorgente; setBreakpoint()prende l'output di findLineNume chiama trace()per impostare un punto di interruzione lì.

Questi si basano sulla presenza di informazioni di debug di riferimento di origine nel codice. Questa è l'impostazione predefinita per il codice letto da source(), ma non per i pacchetti. Per ottenere i riferimenti sorgente nel codice del pacchetto, impostare la variabile di ambiente R_KEEP_PKG_SOURCE=yeso all'interno di R, impostare options(keep.source.pkgs=TRUE), quindi installare il pacchetto dal codice sorgente. Leggi ?findLineNumper i dettagli su come dirgli di cercare all'interno dei pacchetti, piuttosto che limitare la ricerca all'ambiente globale.

Per esempio,

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

Questo stamperà

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

e puoi usare

setBreakpoint("<text>#3")

per impostare un punto di interruzione lì.

Ci sono ancora alcune limitazioni (e probabilmente bug) nel codice; Li aggiusterò


Grazie. Mi sono appena iscritto anche alla mailing list di r-devel. Ho evitato r-help partendo dal presupposto che avrebbe intasato la mia casella di posta (r-sig-finance lo fa già).
Shane

1
non capisco davvero come funziona dalla riga di comando senza curiosare nello script R
Herman Toothrot

1
@ Hirse: questa è quasi dieci la tua vecchia risposta. Perché diavolo l'hai riformattato per far finta che stessi citando? Non lo ero, e il tuo cambiamento non riflette il mio intento.
Dirk Eddelbuettel

"Duncan Murdoch ha appena pubblicato:" suona molto come una citazione, ma se non è corretta, per favore annulla la modifica. Volevo renderlo più leggibile per me stesso e non ho controllato la data finché non ho finito. Se l'intera risposta è troppo obsoleta, puoi anche eliminarla per rimuovere confusione dai futuri lettori.
assunzione

Puoi ripristinarlo per favore? Grazie.
Dirk Eddelbuettel

6

Lo fai impostando

options(show.error.locations = TRUE)

Mi chiedo solo perché questa impostazione non è predefinita in R? Dovrebbe esserlo, come in ogni altra lingua.


1
Per informazioni di base su questa opzione, vedere stat.ethz.ch/R-manual/R-devel/library/base/html/options.html
R Yoda

1
Questo funzionava, ma è stato disabilitato perché non è affidabile. Penso che sia un tentativo di costringerti a usare RStudio che alla fine non sarà libero.
Eric Leschinski

6
Ne dubito. R core e RStudio sono organizzazioni molto diverse, e R core in particolare sono sostenitori open-source.
Ben Bolker

Ha lavorato su CentOS 6.9, R-3.4.2
irritable_phd_syndrom

Forse vale la pena menzionare, dovresti impostare le opzioni in anticipo, prima di procurarti qualsiasi codice.
JAponte

3

La specifica dell'opzione R globale per la gestione degli errori non catastrofici ha funzionato per me, insieme a un flusso di lavoro personalizzato per conservare le informazioni sull'errore ed esaminare queste informazioni dopo l'errore. Attualmente sto eseguendo la versione R 3.4.1. Di seguito, ho incluso una descrizione del flusso di lavoro che ha funzionato per me, nonché un codice che ho usato per impostare l'opzione di gestione degli errori globali in R.

Così come l'ho configurato, la gestione degli errori crea anche un file RData contenente tutti gli oggetti nella memoria di lavoro al momento dell'errore. Questo dump può essere letto di nuovo in R usandoload() e quindi i vari ambienti esistenti al momento dell'errore possono essere ispezionati in modo interattivo utilizzando debugger(errorDump).

Noterò che sono stato in grado di ottenere i numeri di riga traceback()nell'output da qualsiasi funzione personalizzata all'interno dello stack, ma solo se ho utilizzato l' keep.source=TRUEopzione durante la chiamata source()di qualsiasi funzione personalizzata utilizzata nel mio script. Senza questa opzione, l'impostazione dell'opzione di gestione degli errori globale come di seguito ha inviato l'output completo ditraceback() a un registro degli errori denominato error.log, ma i numeri di riga non erano disponibili.

Ecco i passaggi generali che ho seguito nel mio flusso di lavoro e come sono stato in grado di accedere al dump della memoria e al registro degli errori dopo un errore R non interattivo.

  1. Ho inserito quanto segue all'inizio dello script principale che stavo chiamando dalla riga di comando. Questo imposta l'opzione di gestione degli errori globali per la sessione R. Il mio script principale è stato chiamato myMainScript.R. Le varie righe del codice hanno commenti dopo di loro che descrivono ciò che fanno. Fondamentalmente, con questa opzione, quando R incontra un errore che si innesca stop(), creerà un file di dump RData (* .rda) della memoria di lavoro in tutti gli ambienti attivi nella directory ~/myUsername/directoryForDumpe scriverà anche un registro degli errori denominato error.logcon alcune informazioni utili per il stessa directory. È possibile modificare questo frammento per aggiungere altra gestione in caso di errore (ad esempio, aggiungere un timestamp al file di dump e ai nomi dei file del registro degli errori, ecc.).

    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. Assicurati che dallo script principale e da eventuali chiamate di funzione successive, ogni volta che viene generata una funzione, keep.source=TRUEvenga utilizzata l'opzione . Cioè, per generare una funzione, useresti source('~/path/to/myFunction.R', keep.source=TRUE). Ciò è necessario affinché l' traceback()output contenga i numeri di riga. Sembra che tu possa anche essere in grado di impostare questa opzione globalmente usando options( keep.source=TRUE ), ma non l'ho testato per vedere se funziona. Se non hai bisogno di numeri di riga, puoi omettere questa opzione.

  3. Dal terminale (all'esterno di R), chiama lo script principale in modalità batch utilizzando Rscript myMainScript.R. Ciò avvia una nuova sessione R non interattiva ed esegue lo script myMainScript.R. Lo snippet di codice fornito nel passaggio 1 che è stato posizionato all'inizio di myMainScript.Rimposta l'opzione di gestione degli errori per la sessione R non interattiva.
  4. Incontra un errore da qualche parte durante l'esecuzione di myMainScript.R. Questo può essere nello stesso script principale o annidato in profondità diverse funzioni. Quando si verifica l'errore, la gestione verrà eseguita come specificato nel passaggio 1 e la sessione R verrà terminata.
  5. Un file di dump RData denominato errorDump.rdae e un log degli errori denominato error.logvengono creati nella directory specificata da '~/myUsername/directoryForDump'nell'impostazione dell'opzione di gestione degli errori globali.
  6. A tuo piacimento, controlla error.logper esaminare le informazioni sull'errore, incluso il messaggio di errore stesso e la traccia completa dello stack che ha portato all'errore. Ecco un esempio del registro generato in caso di errore; notare che i numeri dopo il #carattere sono i numeri di riga dell'errore in vari punti nello stack di chiamate:

    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. A tuo piacimento, puoi caricare errorDump.rdain una sessione R interattiva usando load('~/path/to/errorDump.rda'). Una volta caricato, chiama debugger(errorDump)per esplorare tutti gli oggetti R in memoria in uno qualsiasi degli ambienti attivi. Vedere la guida R debugger()per maggiori informazioni.

Questo flusso di lavoro è estremamente utile quando si esegue R in un tipo di ambiente di produzione in cui si hanno sessioni R non interattive che vengono avviate dalla riga di comando e si desidera che vengano conservate le informazioni sugli errori imprevisti. La capacità di eseguire il dump della memoria in un file che è possibile utilizzare per ispezionare la memoria di lavoro al momento dell'errore, insieme ad avere i numeri di riga dell'errore nello stack di chiamate, facilita il debug post-mortem veloce di ciò che ha causato l'errore.


0

Prima options(show.error.locations = TRUE)e poi traceback(). Il numero della riga di errore verrà visualizzato dopo #

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.