[Rd] Determining the exit code of an "almost finished" R script

Gergely Daróczi d@rocz|g @end|ng |rom r@pporter@net
Thu Jul 11 23:48:47 CEST 2019


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 using 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 using 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
>



More information about the R-devel mailing list