R numéros de ligne de script à l’erreur?

Si je lance un long script R à partir de la ligne de commande (R –slave script.R), comment puis-je obtenir des numéros de ligne avec des erreurs?

Je ne veux pas append de commandes de débogage au script si possible – je veux juste que R se comporte comme la plupart des autres langages de script …

Cela ne vous donnera pas le numéro de ligne, mais cela vous indiquera où l’échec se produit dans la stack d’appels, ce qui est très utile:

traceback() 

[Edit:] Lors de l’exécution d’un script à partir de la ligne de commande, vous devrez ignorer un ou deux appels, consultez traceback () pour les sessions R interactives et non interactives.

Je ne suis pas au courant d’une autre façon de le faire sans les suspects habituels de débogage:

  1. déboguer()
  2. navigateur()
  3. options (error = recover) [suivi des options (error = NULL) pour le restaurer]

Vous pourriez vouloir regarder cette publication connexe.

[Edit:] Désolé … je viens de voir que vous lancez ceci depuis la ligne de commande. Dans ce cas, je suggère de travailler avec les fonctionnalités (erreur) des options. Voici un exemple simple:

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

Vous pouvez créer un script aussi élaboré que vous le souhaitez sur une condition d’erreur, vous devez donc décider des informations dont vous avez besoin pour le débogage.

Sinon, s’il existe des zones spécifiques qui vous préoccupent (par exemple, la connexion à une firebase database), enveloppez-les dans une fonction tryCatch ().

Faire des options(error=traceback) fournit un peu plus d’informations sur le contenu des lignes menant à l’erreur. Si une erreur survient, une traceback apparaît et, pour certaines erreurs, le numéro de ligne est préfixé par # . Mais c’est un échec ou un échec, beaucoup d’erreurs n’obtiendront pas de numéros de ligne.

Le support pour cela sera disponible dans R 2.10 et plus tard. Duncan Murdoch vient de poster sur r-devel le 10 septembre 2009 à propos de findLineNum et setBreapoint :

 I've just added a couple of functions to R-devel to help with debugging. findLineNum() finds which line of which function corresponds to a particular line of source code; setBreakpoint() takes the output of findLineNum, and calls trace() to set a breakpoint there. These rely on having source reference debug information in the code. This is the default for code read by source(), but not for packages. To get the source references in package code, set the environment variable R_KEEP_PKG_SOURCE=yes, or within R, set options(keep.source.pkgs=TRUE), then install the package from source code. Read ?findLineNum for details on how to tell it to search within packages, rather than limiting the search to the global environment. For example, 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("#3") #  is a dummy filename used by parse(text=) This will print f step 2,3,2 in  and you can use setBreakpoint("#3") to set a breakpoint there. There are still some limitations (and probably bugs) in the code; I'll be fixing thos 

La spécification de l’option globale R pour gérer les erreurs non catastrophiques a fonctionné pour moi, ainsi qu’un stream de travail personnalisé permettant de conserver les informations sur l’erreur et d’examiner ces informations après l’échec. J’utilise actuellement la version R 3.4.1. Ci-dessous, j’ai inclus une description du workflow qui a fonctionné pour moi, ainsi que du code que j’ai utilisé pour définir l’option globale de gestion des erreurs dans R.

Comme je l’ai configuré, la gestion des erreurs crée également un fichier RData contenant tous les objects de la mémoire de travail au moment de l’erreur. Ce vidage peut être lu dans R à l’aide de load() et les différents environnements tels qu’ils existaient au moment de l’erreur peuvent être inspectés de manière interactive à l’aide du debugger(errorDump) .

Je noterai que j’ai pu obtenir des numéros de ligne dans la sortie traceback() de toutes les fonctions personnalisées de la stack, mais uniquement si j’ai utilisé l’option keep.source=TRUE lors de l’appel de source() pour toute fonction personnalisée utilisée dans mon script. . Sans cette option, la définition de l’option globale de gestion des erreurs comme ci-dessous envoie le résultat complet de traceback() à un journal d’erreur nommé error.log , mais les numéros de ligne n’étaient pas disponibles.

Voici les étapes générales que j’ai sockets dans mon stream de travail et comment j’ai pu accéder au journal de vidage de mémoire et au journal des erreurs après un échec de R non interactif.

  1. Je mets le texte suivant en haut du script principal que j’appelais depuis la ligne de commande. Cela définit l’option de gestion des erreurs globales pour la session R. Mon script principal s’appelait myMainScript.R . Les différentes lignes du code contiennent des commentaires décrivant ce qu’elles font. Fondamentalement, avec cette option, lorsque R rencontre une erreur qui déclenche stop() , il crée un fichier de vidage RData (* .rda) de la mémoire de travail dans tous les environnements actifs du répertoire ~/myUsername/directoryForDump et écrit également une erreur error.log nom error.log avec des informations utiles dans le même répertoire. Vous pouvez modifier cet extrait pour append une autre manipulation en cas d’erreur (par exemple, append un horodatage au fichier de vidage et aux noms de fichier du journal des erreurs, etc.).

     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 resortingeve 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. Assurez-vous qu’à partir du script principal et des appels de fonction ultérieurs, chaque fois qu’une fonction est keep.source=TRUE , l’option keep.source=TRUE est utilisée. Autrement dit, pour générer une fonction, vous devez utiliser source('~/path/to/myFunction.R', keep.source=TRUE) . Ceci est nécessaire pour que la sortie traceback() contienne des numéros de ligne. Il semble que vous puissiez également définir cette option globalement en utilisant les options( keep.source=TRUE ) , mais je n’ai pas testé cela pour voir si cela fonctionne. Si vous n’avez pas besoin de numéros de lignes, vous pouvez omettre cette option.

  3. Depuis le terminal (hors R), appelez le script principal en mode batch à l’aide de Rscript myMainScript.R . Cela démarre une nouvelle session R non interactive et exécute le script myMainScript.R . L’extrait de code fourni à l’étape 1 qui a été placé en haut de myMainScript.R définit l’option de gestion des erreurs pour la session R non interactive.
  4. Rencontrez une erreur quelque part dans l’exécution de myMainScript.R . Cela peut être dans le script principal lui-même ou dans plusieurs fonctions nestedes. Lorsque l’erreur est rencontrée, la gestion sera effectuée comme spécifié à l’étape 1 et la session R se terminera.
  5. Un fichier de vidage RData nommé errorDump.rda et le journal des erreurs nommé error.log sont créés dans le répertoire spécifié par '~/myUsername/directoryForDump' dans le paramètre d’option de gestion des erreurs globales.
  6. À votre guise, inspectez error.log pour consulter les informations sur l’erreur, y compris le message d’erreur lui-même et la trace complète de la stack qui a généré l’erreur. Voici un exemple du journal généré en cas d’erreur. Notez les nombres après le caractère # sont les numéros de ligne de l’erreur à différents points de la stack d’appels:

     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. À votre guise, vous pouvez charger errorDump.rda dans une session interactive à l’aide de load('~/path/to/errorDump.rda') . Une fois chargé, appelez le debugger(errorDump) pour parcourir tous les objects R en mémoire dans l’un des environnements actifs. Voir l’aide R sur le debugger() pour plus d’informations.

Ce stream de travail est extrêmement utile lors de l’exécution de R dans un type d’environnement de production dans lequel des sessions R non interactives sont lancées sur la ligne de commande et vous souhaitez conserver des informations sur les erreurs inattendues. La possibilité de vider la mémoire dans un fichier que vous pouvez utiliser pour inspecter la mémoire de travail au moment de l’erreur, ainsi que les numéros de ligne de l’erreur dans la stack d’appels, facilitent le débogage post-mortem de la cause de l’erreur.

Vous le faites en définissant

 options(show.error.locations = TRUE) 

Je me demande juste pourquoi ce paramètre n’est pas un défaut dans R? Cela devrait être, comme dans toutes les autres langues.