¿Números de línea de secuencia de comandos R en error?

105

Si estoy ejecutando un script R largo desde la línea de comando (R --slave script.R), ¿cómo puedo hacer que dé números de línea en los errores?

No quiero agregar comandos de depuración al script si es posible, solo quiero que R se comporte como la mayoría de los otros lenguajes de script ...

esperar
fuente
31
¿Alguna actualización? Cuatro 4 años después, parece que el problema aún persiste, a pesar de toda la adopción generalizada de R.
Gui Ambros
También tengo un script R muy largo con muchos resultados pequeños, quiero imprimir (guión bajo) (guión bajo) LINE / FILE (guion bajo) (guion bajo) (números de línea y nombre del guion) así en C, en lugar de codificar números de línea en fuente.
mosh
No sé si R internamente realmente tiene una noción de 'números de línea'. Sin embargo, tiene una noción de tareas completas, es decir, tareas de nivel superior. Se podría, por ejemplo, definir fácilmente un administrador de tareas para indicarle qué tarea de nivel superior falló. Por supuesto, eso no es un gran consuelo para aquellos con grandes cadenas o grandes declaraciones condicionales.
russellpierce

Respuestas:

45

Esto no le dará el número de línea, pero le dirá dónde ocurre la falla en la pila de llamadas, lo cual es muy útil:

traceback()

[Editar:] Al ejecutar un script desde la línea de comandos, tendrá que omitir una o dos llamadas, consulte traceback () para sesiones de R interactivas y no interactivas

No conozco otra forma de hacer esto sin los sospechosos de depuración habituales:

  1. depurar()
  2. navegador()
  3. opciones (error = recuperar) [seguido de opciones (error = NULL) para revertirlo]

Es posible que desee ver esta publicación relacionada.

[Editar:] Lo siento ... acabo de ver que estás ejecutando esto desde la línea de comandos. En ese caso, sugeriría trabajar con la funcionalidad de opciones (error). He aquí un ejemplo sencillo:

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

Puede crear un script tan elaborado como desee en una condición de error, por lo que debe decidir qué información necesita para la depuración.

De lo contrario, si hay áreas específicas que le preocupan (por ejemplo, conectarse a una base de datos), envuélvalas en una función tryCatch ().

Shane
fuente
La solución vinculada en el primer bloque [Editar:] funciona para mí. El mejor enfoque parece ser el comentario de @dshepherd, es decir, agregar options(error=function() { traceback(2); if(!interactive()) quit("no", status = 1, runLast = FALSE) })(ver comentario de respuesta aceptada). Creo que tendría sentido agregarlo a la respuesta aquí en lugar de solo proporcionar un enlace a otro hilo.
cryo111
1
una nueva opción que le permite obtener los números de línea en el rastreo github.com/aryoda/tryCatchLog
Lunguini
13

Doing options(error=traceback)proporciona un poco más de información sobre el contenido de las líneas que conducen al error. Hace que aparezca un rastreo si hay un error, y para algunos errores tiene el número de línea precedido por #. Pero es impredecible, muchos errores no obtienen números de línea.

Hugh Perkins
fuente
2
No me funciona del todo. Solo tengo un archivo y no muestra el número de línea, solo dice No traceback availabledespués del error.
Mark Lakata
11

El soporte para esto estará disponible en R 2.10 y posteriores. Duncan Murdoch acaba de publicar en r-devel el 10 de septiembre de 2009 sobre findLineNum y setBreapoint :

Acabo de agregar un par de funciones a R-devel para ayudar con la depuración. findLineNum()encuentra qué línea de qué función corresponde a una línea particular de código fuente; setBreakpoint()toma la salida de findLineNumy llama trace()para establecer un punto de interrupción allí.

Éstos se basan en tener información de depuración de referencia de origen en el código. Este es el valor predeterminado para el código leído por source(), pero no para los paquetes. Para obtener las referencias fuente en el código del paquete, configure la variable de entorno R_KEEP_PKG_SOURCE=yes, o dentro de R, configure options(keep.source.pkgs=TRUE), luego instale el paquete desde el código fuente. Lea ?findLineNumpara obtener detalles sobre cómo decirle que busque dentro de los paquetes, en lugar de limitar la búsqueda al entorno global.

Por ejemplo,

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

Esto imprimirá

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

y puedes usar

setBreakpoint("<text>#3")

para establecer un punto de ruptura allí.

Todavía existen algunas limitaciones (y probablemente errores) en el código; Estaré arreglando esos

Dirk Eddelbuettel
fuente
Gracias. También me inscribí en la lista de correo de r-devel. He estado evitando r-help suponiendo que obstruiría mi bandeja de entrada (r-sig-finance ya lo hace).
Shane
1
realmente no entiendo cómo funciona esto desde la línea de comando sin hurgar en el script R
Herman Toothrot
1
@hirse: Esta es casi diez tu respuesta anterior. ¿Por qué demonios lo reformateó para fingir que estaba citando? No lo estaba, y tu cambio no refleja mi intención.
Dirk Eddelbuettel
"Duncan Murdoch acaba de publicar:" suena muy parecido a una cita, pero si eso no es correcto, revierta la edición. Quería hacerlo más legible para mí y no verifiqué la fecha hasta que terminé. Si toda la respuesta está demasiado desactualizada, también puede eliminarla para eliminar la confusión de los futuros lectores.
hirse
¿Puedes revertirlo? Gracias.
Dirk Eddelbuettel
6

Lo haces estableciendo

options(show.error.locations = TRUE)

Me pregunto por qué esta configuración no es predeterminada en R. Debería serlo, como en cualquier otro idioma.

TMS
fuente
1
Para obtener información general sobre esta opción, consulte stat.ethz.ch/R-manual/R-devel/library/base/html/options.html
R Yoda
1
Esto solía funcionar, pero se desactivó porque no es confiable. Creo que es un intento de obligarte a usar RStudio, que eventualmente no será gratuito.
Eric Leschinski
6
Lo dudo. R core y RStudio son organizaciones muy diferentes, y R core en particular son firmes desarrolladores de código abierto.
Ben Bolker
Trabajó en CentOS 6.9, R-3.4.2
irritable_phd_synd desde el
Quizás vale la pena mencionarlo, debe configurar las opciones por adelantado, antes de obtener cualquier código.
JAponte
3

Especificar la opción R global para manejar errores no catastróficos funcionó para mí, junto con un flujo de trabajo personalizado para retener información sobre el error y examinar esta información después de la falla. Actualmente estoy ejecutando R versión 3.4.1. A continuación, incluí una descripción del flujo de trabajo que me funcionó, así como un código que usé para configurar la opción de manejo de errores global en R.

Como lo tengo configurado, el manejo de errores también crea un archivo RData que contiene todos los objetos en la memoria de trabajo en el momento del error. Este volcado se puede leer de nuevo en R usando load()y luego los diversos entornos tal como existían en el momento del error se pueden inspeccionar de forma interactiva usando debugger(errorDump).

Notaré que pude obtener números de línea en la traceback()salida de cualquier función personalizada dentro de la pila, pero solo si usé la keep.source=TRUEopción al llamar source()a las funciones personalizadas utilizadas en mi script. Sin esta opción, la configuración de la opción de manejo de errores globales como se muestra a continuación envió la salida completa de traceback()a un registro de errores llamado error.log, pero los números de línea no estaban disponibles.

Estos son los pasos generales que tomé en mi flujo de trabajo y cómo pude acceder al volcado de memoria y al registro de errores después de una falla de R no interactiva.

  1. Puse lo siguiente en la parte superior del script principal al que estaba llamando desde la línea de comandos. Esto establece la opción de manejo de errores global para la sesión R. Mi guión principal se llamó myMainScript.R. Las diversas líneas del código tienen comentarios después de ellas que describen lo que hacen. Básicamente, con esta opción, cuando R encuentra un error que se activa stop(), creará un archivo de volcado RData (* .rda) de la memoria de trabajo en todos los entornos activos en el directorio ~/myUsername/directoryForDumpy también escribirá un registro de errores error.logcon información útil para el mismo directorio. Puede modificar este fragmento para agregar otro manejo en caso de error (por ejemplo, agregar una marca de tiempo al archivo de volcado y nombres de archivo de registro de errores, 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. Asegúrese de que desde el script principal y cualquier llamada de función subsiguiente, siempre que se obtenga una función, keep.source=TRUEse utilice la opción . Es decir, para obtener una función, usaría source('~/path/to/myFunction.R', keep.source=TRUE). Esto es necesario para que la traceback()salida contenga números de línea. Parece que también puede configurar esta opción globalmente usando options( keep.source=TRUE ), pero no lo he probado para ver si funciona. Si no necesita números de línea, puede omitir esta opción.

  3. Desde el terminal (fuera de R), llame al script principal en modo por lotes usando Rscript myMainScript.R. Esto inicia una nueva sesión de R no interactiva y ejecuta el script myMainScript.R. El fragmento de código proporcionado en el paso 1 que se colocó en la parte superior de myMainScript.Restablece la opción de manejo de errores para la sesión R no interactiva.
  4. Encuentra un error en algún lugar de la ejecución de myMainScript.R. Esto puede estar en el propio script principal o anidar varias funciones en profundidad. Cuando se encuentra el error, el manejo se realizará como se especifica en el paso 1 y la sesión de R terminará.
  5. Se crea un archivo de volcado RData denominado errorDump.rday un registro de errores denominado error.logen el directorio especificado por '~/myUsername/directoryForDump'en la configuración de la opción de manejo de errores globales.
  6. En su tiempo libre, inspeccione error.logpara revisar la información sobre el error, incluido el mensaje de error y el seguimiento de la pila completa que condujo al error. Aquí hay un ejemplo del registro que se genera en caso de error; tenga en cuenta que los números después del #carácter son los números de línea del error en varios puntos de la pila de llamadas:

    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. En su tiempo libre, puede cargar errorDump.rdaen una sesión R interactiva usando load('~/path/to/errorDump.rda'). Una vez cargado, llame debugger(errorDump)para examinar todos los objetos R en la memoria en cualquiera de los entornos activos. Consulte la ayuda de R debugger()para obtener más información.

Este flujo de trabajo es enormemente útil cuando se ejecuta R en algún tipo de entorno de producción en el que se inician sesiones de R no interactivas en la línea de comandos y desea que se conserve información sobre errores inesperados. La capacidad de volcar la memoria en un archivo que puede usar para inspeccionar la memoria de trabajo en el momento del error, junto con tener los números de línea del error en la pila de llamadas, facilita la depuración post mortem rápida de lo que causó el error.

bmosov01
fuente
0

Primero options(show.error.locations = TRUE)y luego traceback(). El número de línea de error se mostrará después de #

den2042
fuente