2015-09-28 7 views
6

Ho una funzione generica per catturare tutte le eccezioni inclusa nel mio pacchetto logR::tryCatch2 definito come:Invoke interrompere dal codice R

tryCatch2 <- function(expr){ 
    V=E=W=M=I=NULL 
    e.handler = function(e){ 
     E <<- e 
     NULL 
    } 
    w.handler = function(w){ 
     W <<- c(W, list(w)) 
     invokeRestart("muffleWarning") 
    } 
    m.handler = function(m){ 
     attributes(m$call) <- NULL 
     M <<- c(M, list(m)) 
    } 
    i.handler = function(i){ 
     I <<- i 
     NULL 
    } 
    V = suppressMessages(withCallingHandlers(
     tryCatch(expr, error = e.handler, interrupt = i.handler), 
     warning = w.handler, 
     message = m.handler 
    )) 
    list(value=V, error=E, warning=W, message=M, interrupt=I) 
} 

Come si può vedere nell'ultima riga viene restituito un elenco che è più o meno auto-descrivendo.
Rende la reazione reale per le eccezioni ritardati dopo la tryCatch2 chiamata semplice !is.null:

f = function(){ warning("warn1"); warning("warn2"); stop("err") } 
r = tryCatch2(f()) 
if(!is.null(r$error)) cat("Error detected\n") 
# Error detected 
if(!is.null(r$warning)) cat("Warning detected, count", length(r$warning), "\n") 
# Warning detected, count 2 

funziona come previsto, mi può reagire con il mio codice. In alcuni casi, tuttavia, non desidero interrompere il processo di interruzione che viene rilevato. Al momento sembra che avrei bisogno di aggiungere ulteriori parametri a tryCatch2 che controllerebbero se gli interrupt dovessero essere catturati o meno. Quindi la domanda chiede di alcune invokeInterrupt funzione che ho potuto utilizzare nel seguente modo:

g = function(){ Sys.sleep(60); f() } 
r = tryCatch2(g()) 
# interrupt by pressing ctrl+c/stop while function is running! 
if(!is.null(r$interrupt)) cat("HERE I would like to invoke interrupt\n") 
# HERE I would like to invoke interrupt 

penso che se R è in grado di prendere uno che dovrebbe essere anche in grado di avvalersi di uno.
Come posso ottenere la funzionalità invokeInterrupt?

+0

Perché non cambiare l'interfaccia? Invece di 'if (! Is.null (r $ interrupt))', fornisci una funzione in modo che l'utente debba fare 'if (r $ has_interrupt())', e puoi invocare l'interrupt in quella funzione. –

+0

@KonradRudolph per fare questo Ho ancora bisogno di sapere come richiamare l'interrupt dal codice R, giusto? – jangorecki

+1

Ah, ho frainteso la domanda allora. In realtà non sono a conoscenza di un modo in cui R lo faccia e, se capisco correttamente alcuni documenti, potrebbe non essere possibile. –

risposta

3

Posso proporre una soluzione parziale, che si basa sul pacchetto tools.

invokeInterrupt <- function() { 
    require(tools) 
    processId <- Sys.getpid() 
    pskill(processId, SIGINT) 
} 

Tuttavia, essere consapevoli che gettando il segnale di interruzione (SIGINT) con pskill non sembra essere molto robusta. Ho eseguito alcuni test con l'invio di eccezione e la cattura con la funzione, in questo modo:

will_interrupt <- function() { 
    Sys.sleep(3) 
    invokeInterrupt() 
    Sys.sleep(3) 
} 

r = tryCatch2(will_interrupt()) 

su Linux, questo ha funzionato bene quando eseguito da linea di comando R. Su Windows, la linea di comando R e R Gui si chiudevano durante l'esecuzione di questo codice. C'è di peggio: su entrambi linux e windows, questo codice ha bloccato Rstudio all'istante ...

Quindi, se il codice deve essere eseguito dalla riga di comando R su Linux, questa soluzione dovrebbe essere OK. Altrimenti potresti essere sfortunato ...

+0

Bel trucco. Nel mio caso non funzionerà, voglio invocare * interrupt * dalla funzione di basso livello, ma in seguito potrò catturare alcuni fotogrammi più in alto nello stack di chiamate usando un'altra chiamata 'tryCatch2'. – jangorecki

+0

Non ho notato 'SIGINT' prima (non sapevo che il segnale esista). L'ho provato e sembra non catturare correttamente l'interrupt superiore. La metà della taglia sembra essere giusta. Mi chiedo perché l'interrupt in-R sia diverso dal segnale di 'SIGINT', e se non dovrebbe essere lo stesso? – jangorecki

+0

Mi piacerebbe vedere una soluzione migliore di quella che ho postato (che è molto specifica in quanto funziona solo con linux), quindi sì, non accettare questa soluzione e aspettiamo di vedere se qualcuno può risolvere questo problema :) – Jealie