125 votes

Comment obtenir les numéros de ligne de R script en cas d'erreur ?

Si j'exécute un long script R à partir de la ligne de commande ( R --slave script.R ), alors comment puis-je faire en sorte qu'il donne les numéros de ligne en cas d'erreur ?

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

42 votes

Des mises à jour ? Quatre ans plus tard, il semble que le problème persiste, malgré l'adoption de R par le grand public.

0 votes

J'ai aussi un très long R script avec beaucoup de petites sorties, je veux imprimer (underscore)(underscore)LINE/FILE(underscore)(underscore) (numéros de ligne et nom du script) comme cela en C, au lieu de coder en dur les numéros de ligne dans la source.

0 votes

Je ne sais pas si R interne a vraiment une notion de "numéros de ligne". Cependant, il a une notion de tâches complètes, c'est-à-dire de tâches de haut niveau. On pourrait, par exemple, facilement définir un gestionnaire de tâches pour indiquer quelle tâche de niveau supérieur a échoué. Bien sûr, ce n'est pas un grand réconfort pour ceux qui ont de grandes chaînes ou de grandes déclarations conditionnelles.

54voto

Shane Points 40885

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

traceback()

[Edit :] Lorsque vous exécutez un script à partir de la ligne de commande, vous devrez sauter un ou deux appels, cf. 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 pouvez consulter ce billet connexe.

[Edit :] Désolé... je viens de voir que vous l'exécutez en ligne de commande. Dans ce cas, je vous suggère de travailler avec la fonctionnalité options(error). 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, donc vous devriez juste décider des informations dont vous avez besoin pour le débogage.

Sinon, si certains domaines spécifiques vous préoccupent (par exemple, la connexion à une base de données), intégrez-les dans une fonction tryCatch().

1 votes

La solution proposée dans le premier bloc [Edit :] fonctionne pour moi. La meilleure approche semble être le commentaire de @dshepherd, c'est à dire ajouter options(error=function() { traceback(2); if(!interactive()) quit("no", status = 1, runLast = FALSE) }) (voir commentaire de la réponse acceptée). Je pense qu'il serait judicieux de l'ajouter à la réponse ici plutôt que de fournir uniquement un lien vers un autre fil.

1 votes

Une nouvelle option qui vous permet d'obtenir les numéros de ligne dans le traceback github.com/aryoda/tryCatchLog

15voto

Hugh Perkins Points 1050

Faire options(error=traceback) fournit un peu plus d'informations sur le contenu des lignes menant à l'erreur. Il provoque l'apparition d'un retour de trace s'il y a une erreur, et pour certaines erreurs, il affiche le numéro de ligne, préfixé de # . Mais c'est aléatoire, de nombreuses erreurs n'obtiennent pas de numéros de ligne.

3 votes

Ça ne marche pas vraiment pour moi. Je n'ai qu'un seul fichier, et il n'affiche pas le numéro de ligne, il indique seulement No traceback available après l'erreur.

12voto

Dirk Eddelbuettel Points 134700

La prise en charge de cette fonction sera bientôt disponible dans R 2.10 et les versions ultérieures. Duncan Murdoch vient de poster sur r-devel le 10 septembre 2009 à propos de findLineNum et setBreapoint :

Je viens juste d'ajouter quelques fonctions à R-devel pour aider avec débogage. findLineNum() trouve quelle ligne de quelle fonction correspond à une ligne particulière du code source ; setBreakpoint() prend la sortie de findLineNum et appelle trace() pour définir un point d'arrêt là.

Ceux-ci reposent sur la présence d'informations de débogage de la référence source dans le code. C'est la valeur par défaut pour le code lu par source() mais pas pour les paquets. Pour obtenir les références sources dans le code du paquet, définissez la variable d'environnement variable R_KEEP_PKG_SOURCE=yes ou à l'intérieur de R, définir options(keep.source.pkgs=TRUE) puis installez le paquet à partir des sources source. Lire ?findLineNum pour plus de détails sur la façon de lui dire de chercher dans les paquets, plutôt que de limiter la recherche à l'environnement global. global.

Par exemple,

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

Ceci imprimera

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

et vous pouvez utiliser

setBreakpoint("<text>#3")

pour y placer un point d'arrêt.

Il y a encore quelques limitations (et probablement des bugs) dans le code. vais les corriger

0 votes

Merci. Je viens également de m'inscrire à la liste de diffusion r-devel. J'ai évité r-help en pensant qu'il encombrerait ma boîte de réception (r-sig-finance le fait déjà).

1 votes

Je ne comprends pas vraiment comment cela fonctionne à partir de la ligne de commande sans avoir à fouiller dans le script de R.

1 votes

@hirse : C'est une réponse qui date de presque 10 ans. Pourquoi diable l'avez-vous reformatée pour prétendre que je la citais ? Ce n'était pas le cas, et votre changement fait no reflètent mon intention.

8voto

TMS Points 17522

Pour ce faire, vous devez définir

options(show.error.locations = TRUE)

Je me demande simplement pourquoi ce paramètre n'est pas un paramètre par défaut dans R ? Il devrait l'être, comme c'est le cas dans tous les autres langages.

1 votes

Pour des informations générales sur cette option, voir stat.ethz.ch/R-manuel/R-devel/library/base/html/options.html

4 votes

Cette fonction fonctionnait auparavant, mais elle a été désactivée car elle n'est pas fiable. Je pense que c'est une tentative pour vous forcer à utiliser le RStudio qui, à terme, sera non-libre.

8 votes

J'en doute. Le noyau de R et RStudio sont des organisations très différentes, et le noyau de R, en particulier, est un fervent défenseur de l'open-source.

3voto

bmosov01 Points 164

La spécification de l'option globale de R pour la gestion des erreurs non catastrophiques a fonctionné pour moi, ainsi qu'un flux de travail personnalisé pour conserver les informations sur l'erreur et examiner ces informations après l'échec. 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 qu'une partie du code que j'ai utilisé pour définir l'option globale de gestion des erreurs dans R.

Tel que 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 fichier peut être relu dans R en utilisant load() et ensuite les différents environnements tels qu'ils existaient au moment de l'erreur peuvent être inspectés de manière interactive en utilisant debugger(errorDump) .

Je noterai que j'ai pu obtenir les numéros de ligne dans le fichier traceback() de toutes les fonctions personnalisées de la pile, mais seulement si j'utilise la fonction keep.source=TRUE lors de l'appel source() pour toutes les fonctions personnalisées utilisées dans mon script. Sans cette option, le paramétrage de l'option de gestion des erreurs globales comme ci-dessous a envoyé la sortie complète de la commande traceback() dans un journal d'erreurs nommé error.log mais les numéros de ligne n'étaient pas disponibles.

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

  1. J'ai mis ce qui suit en haut du script principal que j'appelais depuis la ligne de commande. Cela définit l'option de gestion globale des erreurs pour la session R. Mon script principal était appelé myMainScript.R . Les différentes lignes du code sont suivies de commentaires décrivant ce qu'elles font. Fondamentalement, avec cette option, lorsque R rencontre une erreur qui déclenche la commande stop() il créera un fichier de vidage RData (*.rda) de la mémoire de travail pour tous les environnements actifs du répertoire. ~/myUsername/directoryForDump et écrira également un journal d'erreurs nommé error.log avec quelques informations utiles dans le même répertoire. Vous pouvez modifier cet extrait pour ajouter d'autres traitements en cas d'erreur (par exemple, ajouter un horodatage au fichier de vidage et aux noms de fichiers 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 qu'à partir du script principal et de tout appel de fonction ultérieur, chaque fois qu'une fonction est sourcée, l'option keep.source=TRUE est utilisé. Ainsi, pour créer une source pour une fonction, on utilise source('~/path/to/myFunction.R', keep.source=TRUE) . Ceci est nécessaire pour le traceback() pour contenir les numéros de ligne. Il semble que vous puissiez également définir cette option de manière globale en utilisant options( keep.source=TRUE ) mais je ne l'ai pas testé pour voir si cela fonctionne. 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 batch en utilisant 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 l'écran myMainScript.R définit l'option de gestion des erreurs pour la session R non interactive.

  4. Rencontrer une erreur quelque part dans l'exécution de myMainScript.R . Cela peut être dans le script principal lui-même, ou imbriqué plusieurs fonctions en profondeur. Lorsque l'erreur est rencontrée, le traitement sera effectué comme spécifié à l'étape 1, et la session R se terminera.

  5. Un fichier de vidage RData nommé errorDump.rda et un journal d'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 des erreurs globales.

  6. À votre guise, vous pourrez inspecter error.log pour consulter les informations relatives à l'erreur, y compris le message d'erreur proprement dit et la trace complète de la pile ayant conduit à l'erreur. Voici un exemple de journal généré en cas d'erreur ; notez les chiffres après le symbole # sont les numéros de ligne de l'erreur à différents endroits 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. A votre guise, vous pouvez charger errorDump.rda dans une session R interactive en utilisant 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 de 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 à la ligne de commande et où 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 le fait de disposer des numéros de ligne de l'erreur dans la pile d'appels, facilitent le débogage rapide post-mortem de ce qui a causé l'erreur.

0 votes

D'une manière ou d'une autre, cela ne fonctionne plus. error.log n'inclut plus les informations de numéro de ligne ou de nom de fichier dans R 4.0 (je n'ai pas essayé les autres versions).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X