Gergely Daróczi
2019-Jun-08 13:55 UTC
[Rd] Determining the exit code of an "almost finished" R script
On Sat, Jun 8, 2019 at 2:13 PM Duncan Murdoch <murdoch.duncan at gmail.com> wrote:> > On 08/06/2019 7:42 a.m., Gergely Dar?czi wrote: > > Dear All, > > > > I'm using "reg.finalizer" in a function that is to be called in R scripts > > to do some cleanup on success. I have not found a way to run the function > > only if the script run without errors, so when the exit code is expected to > > be 0. > > > > What I've tried is checking "geterrmessage()", but unfortunately it's not > > perfect: if an error was handled with eg "tryCatch" previously, that > > message still shows up there. > > > > Is there any better way of figuring out in "reg.finalizer" or ".Last" if > > the session run OK or exiting with !0 exit code? > > Can't you just put the line to run it as the last line of your script? > You won't get there if there was an error.Thank you very much, Duncan, but unfortunately, that's not feasible. "reg.finalizer" is called from a function early in the script, which function first checks when the same script last run and optionally exists early. If that optional early exit (within the helper function) doesn't happen, then the "reg.finalizer" call sets a status update when the R script has finished, but that should only happen if the script run without any problems. Of course, this helper could be split into two -- (1) function call at the beginning of the script and (2) another at the end, but it would be much less elegant and error-prone. I know I'm trying to hack-in some features in my R scripts that should be better handled by an external job scheduler, but I hope this is doable.> > > The point of reg.finalizer is to run code even if there was an error; > your situation is much simpler. > > Duncan Murdoch
Duncan Murdoch
2019-Jun-08 16:50 UTC
[Rd] Determining the exit code of an "almost finished" R script
On 08/06/2019 9:55 a.m., Gergely Dar?czi wrote:> On Sat, Jun 8, 2019 at 2:13 PM Duncan Murdoch <murdoch.duncan at gmail.com> wrote: >> >> On 08/06/2019 7:42 a.m., Gergely Dar?czi wrote: >>> Dear All, >>> >>> I'm using "reg.finalizer" in a function that is to be called in R scripts >>> to do some cleanup on success. I have not found a way to run the function >>> only if the script run without errors, so when the exit code is expected to >>> be 0. >>> >>> What I've tried is checking "geterrmessage()", but unfortunately it's not >>> perfect: if an error was handled with eg "tryCatch" previously, that >>> message still shows up there. >>> >>> Is there any better way of figuring out in "reg.finalizer" or ".Last" if >>> the session run OK or exiting with !0 exit code? >> >> Can't you just put the line to run it as the last line of your script? >> You won't get there if there was an error. > > Thank you very much, Duncan, but unfortunately, that's not feasible. > > "reg.finalizer" is called from a function early in the script, which > function first checks when the same script last run and optionally > exists early.> If that optional early exit (within the helper function) doesn't > happen, then the "reg.finalizer" call sets a status update when the R > script has finished, but that should only happen if the script run > without any problems. > > Of course, this helper could be split into two -- (1) function call at > the beginning of the script and (2) another at the end, but it would > be much less elegant and error-prone. > > I know I'm trying to hack-in some features in my R scripts that should > be better handled by an external job scheduler, but I hope this is > doable.I still think you're using reg.finalizer() in a way it's not designed to work, and this makes it more complicated than necessary. The strategy of splitting into two seems safer to me: you never know when a finalizer will be called, because it is triggered by garbage collections, and those can happen asynchronously, not under your control. It is nice to have all code for some purpose in one place, so if you really want that, you could put together your own explicitly called finalizer, something like this: finalizers <- list() addFinalizer <- function(fn) { finalizers <<- c(finalizers, list(fn)) } runFinalizers <- function() { for (i in rev(seq_along(finalizers))) { # Run in reverse order finalizers[[i]]() # Call the finalizer finalizers[[i]] <- NULL # Allow related objects to be released } } In the place you now call reg.finalizer(), you call addFinalizer() instead; so all code specific to that task remains local. At the end of your script if things have been successful, you call runFinalizers(). Duncan Murdoch> >> >> >> The point of reg.finalizer is to run code even if there was an error; >> your situation is much simpler. >> >> Duncan Murdoch
Gergely Daróczi
2019-Jul-11 21:48 UTC
[Rd] Determining the exit code of an "almost finished" R script
Sorry for the late reply, it took a couple of iterations (and some days off) to find a feasible solution without splitting the helper function into two -- please see below if interested. On Sat, Jun 8, 2019 at 6:50 PM Duncan Murdoch <murdoch.duncan at gmail.com> wrote:> > On 08/06/2019 9:55 a.m., Gergely Dar?czi wrote: > > On Sat, Jun 8, 2019 at 2:13 PM Duncan Murdoch <murdoch.duncan at gmail.com> wrote: > >> > >> On 08/06/2019 7:42 a.m., Gergely Dar?czi wrote: > >>> Dear All, > >>> > >>> I'm using "reg.finalizer" in a function that is to be called in R scripts > >>> to do some cleanup on success. I have not found a way to run the function > >>> only if the script run without errors, so when the exit code is expected to > >>> be 0. > >>> > >>> What I've tried is checking "geterrmessage()", but unfortunately it's not > >>> perfect: if an error was handled with eg "tryCatch" previously, that > >>> message still shows up there. > >>> > >>> Is there any better way of figuring out in "reg.finalizer" or ".Last" if > >>> the session run OK or exiting with !0 exit code? > >> > >> Can't you just put the line to run it as the last line of your script? > >> You won't get there if there was an error. > > > > Thank you very much, Duncan, but unfortunately, that's not feasible. > > > > "reg.finalizer" is called from a function early in the script, which > > function first checks when the same script last run and optionally > > exists early. > > > If that optional early exit (within the helper function) doesn't > > happen, then the "reg.finalizer" call sets a status update when the R > > script has finished, but that should only happen if the script run > > without any problems. > > > > Of course, this helper could be split into two -- (1) function call at > > the beginning of the script and (2) another at the end, but it would > > be much less elegant and error-prone. > > > > I know I'm trying to hack-in some features in my R scripts that should > > be better handled by an external job scheduler, but I hope this is > > doable. > > I still think you're using reg.finalizer() in a way it's not designed to > work, and this makes it more complicated than necessary. The strategy > of splitting into two seems safer to me: you never know when a > finalizer will be called, because it is triggered by garbage > collections, and those can happen asynchronously, not under your control.Yes, you are absolutely right -- still, I really wanted to make this work by calling only one function at the beginning of an R script (actually, hundreds of R scripts and without an explicit R function call, but doing that in a package's .onLoad function). What I came up with is probably not too elegant ... and might have some edge cases, but seems to do the trick, so thus turned out to be a reasonable solution for now: - overriding the default "error" option to set an env var - check that env var at the end of the script via "reg.finalizer" triggered on exit Quick example: ```r library(logger) options(error = function() { Sys.setenv(R_ERROR_HAPPENED = TRUE) quit(status = 1) }) reg.finalizer(e = environment(), f = function(...) { if (Sys.getenv('R_ERROR_HAPPENED') != '') { log_error(skip_formatter(paste('Seems like there was an error previously:', geterrmessage()))) } else { log_info('All good!') } }, onexit = TRUE) 1 + dasf ```> > It is nice to have all code for some purpose in one place, so if you > really want that, you could put together your own explicitly called > finalizer, something like this: > > finalizers <- list() > > addFinalizer <- function(fn) { > finalizers <<- c(finalizers, list(fn)) > } > > runFinalizers <- function() { > for (i in rev(seq_along(finalizers))) { # Run in reverse order > finalizers[[i]]() # Call the finalizer > finalizers[[i]] <- NULL # Allow related objects to be released > } > } > > > In the place you now call reg.finalizer(), you call addFinalizer() > instead; so all code specific to that task remains local. At the end of > your script if things have been successful, you call runFinalizers(). > > Duncan Murdoch > > > > > > > > >> > >> > >> The point of reg.finalizer is to run code even if there was an error; > >> your situation is much simpler. > >> > >> Duncan Murdoch >