callr icon indicating copy to clipboard operation
callr copied to clipboard

Feature: user hooks

Open klmr opened this issue 3 years ago • 4 comments

This is a WIP PR to implement #200. It

  • adds a function add_hook with semantics roughly as discussed
  • invokes the user hooks in setup_context
  • adds a unit test for the new feature
  • sets an environment variable CALLR_IS_RUNNING=true, which signals to a subprocess that it is being invoked via ‘callr’.

The last point was also discussed in #200 but is not necessarily related to the hook feature. I’m happy to pull it out, but it feels too small for its own PR. The convention to use the lower-case value true for the environment variable mirrors several other packages, including ‘testthat’.

At the moment the implementation puts no restrictions on the user’s modifications of the options that are used to construct the subprocess. This makes the implementation simple, but it also leaks implementation details and increases the maintenance burden (because future changes to the options structure would be breaking changes).

I don’t suggest keeping this structure, I’m only using it here to start a discussion about the desired signature of the hook functions. What should the hook be allowed to see and to modify (and at what point during the launching of the subprocess)?

There’s probably other stuff to improve in the documentation of the function, and maybe its implementation.

klmr avatar May 16 '21 21:05 klmr

I'm thinking that hooks would be a good addition to callr. When using R in a Singularity container transferring the environment to the child process can become challenging. For example, wanting to override a variable set in the container. This is trivial for the initial running container, using renv as an example.

# R is a symlink to the R-4.1.1.sif file
env SINGULARITYENV_RENV_PATHS_CACHE=/trusted/cache R

This is translated (prefix removed) by singularity so the R process has RENV_PATHS_CACHE=/trusted/cache. However, a child process does not pass on the prefixed value, but the unprefixed 'target' environment variable.

One can imagine renv would do similarly to callr:::setup_context for the renv_* variables and prefix them for the child process.

# test for running in container
if(file.exists("/.singularity.d)") {
  callr::add_hook(renv_singularity = function (options) {
    full_env <- Sys.getenv()
    renv_set <- names(full_env)[grep("^RENV_", names(full_env))]
    sing_set <- paste0("SINGULARITY_", renv_set)
    ...
  })
}

One workaround is to ensure the project .Renviron has the prefixed versions, but this relies on keeping the values in step with the starting of the container.

kiwiroy avatar Sep 03 '21 04:09 kiwiroy

@klmr Do you plan to finish this? No rush at all, but also, feel free to close it if you don't need it or want it any more.

gaborcsardi avatar Jul 12 '22 07:07 gaborcsardi

@gaborcsardi Ah, I was actually waiting for some feedback, sorry for not making that clear.

In principle I’d be happy if the PR were merged as-is (it certainly fits my specific purpose). But as mentioned it leaks some undocumented implementation details of ‘callr’ into the public API (namely, the options structure).

klmr avatar Jul 13 '22 10:07 klmr

Oh, sorry. I'll try look at it soon.

gaborcsardi avatar Jul 13 '22 11:07 gaborcsardi

Thanks!

gaborcsardi avatar Aug 22 '22 12:08 gaborcsardi

Awesome! Thanks for merging it.

klmr avatar Aug 22 '22 20:08 klmr