Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.rs.Env or similar are not available from R startup, and no post-RStudio startup interface #1579

Closed
mystatistic opened this issue Oct 11, 2017 · 16 comments

Comments

@mystatistic
Copy link

commented Oct 11, 2017

Sometimes it is helpful to invoke some RStudio-specific startup code in .Rprofile.

To give an example (though this issue is more about access to RStudio additions from R startup, not about the specific objective in the snippet), one might want to check that the default library path is writeable:

if (interactive() && (Sys.getenv("RSTUDIO") == "1")) {
    if (!.rs.defaultLibPathIsWriteable()) {
        warning("Default library path is not writeable!")
    }
}

However, this fails from .Rprofile with the error could not find function ".rs.defaultLibPathIsWriteable". This is because .rs.Env/tools:rstudio (and thus .rs.defaultLibPathIsWriteable as in the example above) is not available when .Rprofile is invoked. I could not find this documented anywhere, and neither could I find any suitable place to hook some startup code to be executed following initialisation of RStudio.

(Tested with RStudio.Version()$version == '1.1.383')

@jmcphers

This comment has been minimized.

Copy link
Member

commented Oct 11, 2017

The .rs. family of functions should generally be considered private; we can (and do) change their implementation between releases. There's a public API which can be used to access these methods safely in the rstudioapi package:

https://cran.rstudio.com/web/packages/rstudioapi/index.html

If you want to run startup code after RStudio has injected its content, you can use R's addTaskCallback. E.g. in .Rprofile:

addTaskCallback(function(...) {
    if (!.rs.defaultLibPathIsWriteable()) {
        warning("Default library path is not writeable!")
    }
    return(FALSE)
}
@mystatistic

This comment has been minimized.

Copy link
Author

commented Oct 16, 2017

Thanks, but the callback doesn't get executed appear until I execute something manually in RStudio. For example, put the following in the .Rprofile file:

invisible(addTaskCallback(function(...) {
    cat(format(Sys.time()), "task callback executing!\n")
    cat("tools:rstudio in search():", "tools:rstudio" %in% search(), "\n")
    cat("rstudioapi::isAvailable():", rstudioapi::isAvailable(), "\n")
    return(FALSE)
}))

Now, restart the R session in RStudio, note the time, wait a few seconds and then type invisible(NULL).

You will note that the cat output does not appear until running the invisible(NULL) line, and the time is that of running invisible(NULL), not of when RStudio finishes loading. Also, suppose you run some actual code, eg 'hello world' in the RStudio console instead of invisible(NULL) just after launching a new R session: then, the [1] "hello world" output appears before the output from the task callback. This is certainly better than before, but not quite ideal for me. Can we do better? Perhaps RStudio should explicitly invoke any function/file called something like RStudio_profile when it is ready?

@jmcphers

This comment has been minimized.

Copy link
Member

commented Oct 16, 2017

Interesting, in my testing it actually runs before the user types anything. It may be a difference in my environment (I'm on macOS) or that project's configuration.

Having an RStudio-specific startup file is a nice idea, though. Ideally we'd want a site-wide version to be available as well, just like .Rprofile. We'll leave this issue open to track that suggestion.

@janfreyberg

This comment has been minimized.

Copy link

commented Apr 10, 2018

I'm just chiming in here because this would be very useful for me. I'd really like to be able to run code using rstudioapi at startup.

Like @mystatistic, code added with addTaskCallback doesn't get run until the user runs some code.

@garrettgman

This comment has been minimized.

Copy link
Member

commented Apr 20, 2018

An RStudio-specific startup file would provide a nice way to share RStudio Cloud projects that come with a pre-loaded tutorial in the viewer window. I'm working on this and I think I have everything worked out except for that.

FWIW, I see the behavior that @mystatistic describes, callback doesn't get executed until I run code, and I'm on a Mac.

@jmcphers jmcphers added this to the v1.3 milestone Sep 24, 2018
@jmcphers

This comment has been minimized.

Copy link
Member

commented Oct 11, 2018

As a followup from another customer request, we should probably have two distinct hooks:

  • One that executes when the R session has started (startup)
  • One that executes when the UI is fully loaded and ready for use (deferred initialization)
@kevinushey kevinushey removed the low label Nov 14, 2018
@garrettgman

This comment has been minimized.

Copy link
Member

commented Jan 28, 2019

Has there been any progress on these hooks?

@jmcphers

This comment has been minimized.

Copy link
Member

commented Mar 27, 2019

Notes from #4498 -

After testing different options, all of RStudio's hooks seem to be established after R's own startup procedure that runs through .Renviron, .Rprofile, and .Rhistory. As a result, there isn't a way to mask code or load a package automatically that won't be overridden by RStudio claimed functions.

It would be helpful to have an injection point after the initial delayed registration.

@kevinushey

This comment has been minimized.

Copy link
Contributor

commented Mar 27, 2019

An alternative would be to just alter the order in which things are run on startup -- ie, run the user's .Rprofile only after we've finished our regular initialization. That feels like a riskier change than just providing a hook, but it would be more consistent with user expectations, I think?

This also ties in with loading of the R workspace on startup -- ideally, we'd defer that as well so that we could at least load the RStudio UI and prepare the workbench before restoring the user workspace.

@jmcphers

This comment has been minimized.

Copy link
Member

commented Mar 27, 2019

run the user's .Rprofile only after we've finished our regular initialization

This is a larger change than it seems because right now many of our own startup scripts (the .R modules, etc.) presume that any user options typically set in .Rprofile have already been established. For example, we don't attach our own error handler on startup if you are using a custom error handler set in your .Rprofile.

SEXP currentHandler = r::options::getOption("error");
*pHandleUserErrorsOnly = userSettings().handleErrorsInUserCodeOnly();
// This runs after ~/.RProfile, so don't change the error handler if
// there's already one assigned, or if we're aware of a custom error
// handler.

Same deal with some repos settings, etc. So we're going to need to split the startup R code into "pre Rprofile" and "post Rprofile" code. And of course if we have a "pre .Rprofile" and "post .Rprofile" set of startup scripts, inevitably people are going to also want to run stuff when we're truly done messing with things and the IDE is ready for action -- i.e. after the "post .Rprofile" step. I think that this implies that we need either a separate set of startup scripts (as is being discussed here) or an new 'on RStudio deferred init' function you can add to .Rprofile.

@jmcphers

This comment has been minimized.

Copy link
Member

commented May 14, 2019

Also note that there should be a global (as well as per user) version of each of these scripts. In addition to adding flexibility, this would fill a hole: there's currently no way to supply R code to be run system-wide (i.e. for all R installations on the system), since the "site-wide" .Rprofile is in R_HOME and applies only to one installation of R.

Requested e.g. here: rstudio/rsconnect#356

@jmcphers

This comment has been minimized.

Copy link
Member

commented May 24, 2019

It turns out that I did this five years ago and have since forgotten about it.

fc86699#diff-9825eb7f38c890c5ed3121779b9f993e

To use it, set something like the below in your .Rprofile:

setHook("rstudio.sessionInit", function(newSession) {
  if (newSession)
    message("Welcome to RStudio ", rstudioapi::getVersion())
}, action = "append")
@coatless

This comment has been minimized.

Copy link

commented Aug 12, 2019

It turns out that I did this five years ago and have since forgotten about it.

fc86699#diff-9825eb7f38c890c5ed3121779b9f993e

To use it, set something like the below in your .Rprofile:

setHook("rstudio.sessionInit", function(newSession) {
  if (newSession)
    message("Welcome to RStudio ", rstudioapi::getVersion())
}, action = "append")

To clarify, this can be used to override the existing RStudio hook on install.packages()?

@kevinushey

This comment has been minimized.

Copy link
Contributor

commented Aug 12, 2019

No, this is a separate user-defined hook. (RStudio hooks install.packages() by injecting its own shim)

@coatless

This comment has been minimized.

Copy link

commented Aug 12, 2019

@kevinushey So, there still is no way to override an RStudio-specific hook in .Rprofile? Thus, #4498 is still an active issue?

@kevinushey

This comment has been minimized.

Copy link
Contributor

commented Aug 12, 2019

You could override the work RStudio has done by injecting your own install.packages() shim; e.g. by directly modifying the install.packages() function (either in the package:utils environment on the search path, or in the utils namespace itself).

You could do this in the rstudio.sessionInit hook so that your overrides are applied after whatever RStudio does.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
7 participants
You can’t perform that action at this time.