shiny
shiny copied to clipboard
shiny.autoreload does not work when runApp is given shinyApp result or a list
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) {}
)
- Run app with
Rscript app.R
(or just interactively). Notice that autoreload is not working. - 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
callsshinyAppDir_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.
Here a related issue regarding the www folder mapping for shiny app objects was discussed.