web-dev-qa-db-fra.com

Numéros de ligne de script erronés?

Si j'exécute un long script R à partir de la ligne de commande (R --slave script.R), comment puis-je l'obtenir pour indiquer des numéros de ligne par erreur? 

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

87
forkandwait

Cela ne vous donnera pas le numéro de ligne, mais vous dira où la défaillance se produit dans la pile d'appels, ce qui est très utile:

traceback()

[Modifier:] Lorsque vous exécutez un script à partir de la ligne de commande, vous devez ignorer un ou deux appels, voir traceback () pour les sessions R interactives et non interactives

Je ne connais pas d'autre moyen de faire cela sans les suspects habituels de débogage:

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

Vous voudrez peut-être regarder ce post connexe.

[Modifier:] Désolé ... viens de voir que vous exécutez ceci à partir de la ligne de commande. Dans ce cas, je suggérerais de travailler avec la fonctionnalité options (erreur). 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 en cas d'erreur. Vous devez donc décider des informations dont vous avez besoin pour le débogage.

Sinon, si des zones spécifiques vous préoccupent (par exemple, la connexion à une base de données), enveloppez-les dans une fonction tryCatch ().

36
Shane

Faire options(error=traceback) fournit un peu plus d’informations sur le contenu des lignes menant à l’erreur. Cela provoque l'apparition d'une trace en cas d'erreur. Pour certaines erreurs, le numéro de ligne, préfixé par # Mais c’est un hasard, beaucoup d’erreurs n’auront pas de numéro de ligne. 

11
Hugh Perkins

Le support pour ceci sera disponible dans R 2.10 et plus tard. Duncan Murdoch a posté sur r-devel le 10 septembre 2009 à propos de findLineNum and 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("<text>#3")   # <text> is a dummy filename used by parse(text=)

This will print

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

and you can use

setBreakpoint("<text>#3")

to set a breakpoint there.

There are still some limitations (and probably bugs) in the code; I'll
be fixing thos
9
Dirk Eddelbuettel

Vous le faites en mettant

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.

4
TMS

La spécification de l'option globale R pour la gestion des erreurs non catastrophiques a fonctionné pour moi, de même qu'un flux de travail personnalisé permettant de conserver les informations sur l'erreur et de les examiner après la défaillance. J'utilise actuellement la version 3.4.1 de R. Ci-dessous, j'ai inclus une description du flux de travail qui a fonctionné pour moi, ainsi que du code que j'avais utilisé pour définir l'option de gestion des erreurs globales dans R.

Comme je l'ai configuré, le traitement des erreurs crée également un fichier RData contenant tous les objets de la mémoire de travail au moment de l'erreur. Ce vidage peut être relu dans R à l'aide de load(), puis les divers environnements tels qu'ils existaient au moment de l'erreur peuvent être inspectés de manière interactive à l'aide de debugger(errorDump).

Je noterai que j'ai pu obtenir des numéros de ligne dans la sortie traceback() à partir de fonctions personnalisées dans la pile, mais uniquement si j'ai utilisé l'option keep.source=TRUE lors de l'appel de source() pour toutes les fonctions personnalisées utilisées dans mon script. Sans cette option, définir l'option de traitement des erreurs globales comme indiqué ci-dessous envoyait la sortie complète de traceback() à un journal des erreurs nommé error.log, mais les numéros de ligne n'étaient pas disponibles.

Voici la procédure générale suivie dans mon flux de travail et la manière dont j'ai pu accéder au vidage de la mémoire et au journal des erreurs après une défaillance non interactive de R.

  1. J'ai mis le texte suivant en haut du script principal que j'appelais à partir de la ligne de commande. Ceci définit l'option de traitement d'erreur global pour la session R. Mon script principal s'appelait myMainScript.R. Les différentes lignes du code sont suivies de commentaires décrivant leurs tâches. 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 un journal des erreurs nommé error.log avec des informations utiles. informations dans le même répertoire. Vous pouvez modifier cet extrait pour ajouter une autre gestion en cas d'erreur (par exemple, ajouter un horodatage au nom du fichier de vidage et des 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 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. Assurez-vous que, à partir du script principal et de tout appel de fonction ultérieur, l'option keep.source=TRUE est utilisée chaque fois qu'une fonction est obtenue. En d’autres termes, pour sélectionner une fonction, vous utiliseriez source('~/path/to/myFunction.R', keep.source=TRUE). Cela 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 à l'aide de options( keep.source=TRUE ), mais je n'ai pas testé cela pour voir si cela fonctionnait. Si vous n'avez pas besoin de numéros de ligne, vous pouvez omettre cette option. 

  3. Depuis le terminal (en dehors de R), appelez le script principal en mode de traitement par lots à 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 donné à l'étape 1 qui a été placé en haut de myMainScript.R définit l'option de traitement 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 même ou dans plusieurs fonctions imbriquées. Lorsque l'erreur est rencontrée, le traitement est effectué comme spécifié à l'étape 1 et la session R se termine.
  5. Un fichier de vidage RData nommé errorDump.rda et un journal des erreurs nommé error.log sont créés dans le répertoire spécifié par '~/myUsername/directoryForDump' dans le paramètre de l'option de traitement d'erreur global.
  6. A 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 pile menant à l’erreur. Voici un exemple du journal généré en cas d'erreur. notez que les chiffres après le caractère # sont les numéros de ligne de l'erreur en différents points de la pile 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 R interactive à l'aide de load('~/path/to/errorDump.rda'). Une fois chargé, appelez debugger(errorDump) pour parcourir tous les objets R en mémoire dans l’un des environnements actifs. Voir l'aide R sur debugger() pour plus d'informations.

Ce flux de travail est extrêmement utile lorsque vous exécutez R dans un type d'environnement de production où des sessions R non interactives sont lancées sur la ligne de commande et que vous souhaitez conserver les 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 pile d'appels, facilitent le débogage post-mortem rapide de la cause de l'erreur. 

2
bmosov01

D'abord, options(show.error.locations = TRUE) et ensuite traceback(). Le numéro de ligne d'erreur sera affiché après #

0
den2042