shiny icon indicating copy to clipboard operation
shiny copied to clipboard

shiny.autoreload does not work when runApp is given shinyApp result or a list

Open TymekDev opened this issue 6 months ago • 1 comments

Description

Hi! Today I have noticed that running an application with shinyApp, i.e. relying on its print, does not enable autoreload.

See Reproduction and Cause below.

Reproduction

Use the following app.R:

options(shiny.port = 8080, shiny.autoreload = TRUE)

shiny::shinyApp(
  "Hello world!",
  function(input, output, session) {}
)
  1. Run app with Rscript app.R (or just interactively). Notice that autoreload is not working.
  2. Run app with Rscript -e shiny::runApp("app.R"). Notice that auroreload is working.

https://github.com/rstudio/shiny/assets/38053499/79bfcb63-a496-4d6c-a006-011f356a14e7

Cause

I went down the rabbit hole to find a root cause of this, and here are my findings.

shinyApp is printed by print.shiny.appobj that calls runApp:

print.shiny.appobj <- function(x, ...) {
  runApp(x)
}

Our object created with shinyApp (of class shiny.appobj) eventually, within runApp, gets passed to as.shiny.appobj:

appParts <- as.shiny.appobj(appDir)

appDir, according to its documentation supports three different classes of objects: characters (directories and file names), a list, and shiny.appobj. The implementations of as.shiny.appobj are as follows:

as.shiny.appobj.shiny.appobj <- function(x) {
  x
}

as.shiny.appobj.list <- function(x) {
  shinyApp(ui = x$ui, server = x$server)
}

as.shiny.appobj.character <- function(x) {
  if (identical(tolower(tools::file_ext(x)), "r"))
    shinyAppFile(x)
  else
    shinyAppDir(x)
}

Taking a look where initAutoReloadMonitor, the function responsible for watching files, is being called leads us to two places: shinyAppDir_serverR and shinyAppDir_appR. Checking references of these two functions we see that:

  • shinyAppFile calls shinyAppDir_appR
  • shinyAppDir calls either of these

This leads to a conclusion that only as.shiny.appobj.character spawns file watcher. Therefore, passing either a list or result of shinyApp makes the autoreload functionality not work.

Summary

I see a point in having it this way as shinyApp and its print are likely to be used within interactive sessions, and then tying a directory to that feels clunky. However, given that autoreload is an opt-in feature, then I think it would be safe to run watcher on getwd() directory - just like runApp() does by default. With that in mind I think it might be a bug to have these inconsistent (that's quite confusing!).

At the very least, I think this warrants a mention in the documentation as I haven't seen it documented anywhere between shinyOptions, shinyApp, and runApp - a venture into source code made me discover the findings described above.

TymekDev avatar Jan 05 '24 13:01 TymekDev

Here a related issue regarding the www folder mapping for shiny app objects was discussed.

ismirsehregal avatar Jan 10 '24 10:01 ismirsehregal