Hi everyone! This is probably more a question around futures and package structure than ExtendedTask and Shiny but I'm making a Shiny app as a package and encountering an error when I try to use ExtendedTask in that future cannot find a function from the package the app is within so I can't library it in because that'd be self referential if that makes sense? I made a package called { testextended} with an app.R in the root directory and two files in the R folder by building on the Extended task example (all im doing here is adding x and y in a different file):
app.R
if (!interactive()) {
sink(stderr(), type = "output")
tryCatch({
library(testextended)
}, error = function(e) {
devtools::load_all()
})
} else {
devtools::load_all()
}
# Increase max file size to 1GB
options(shiny.maxRequestSize=1000*1024^2)
# Launch the app
testextended::test_app(runApp = interactive())
R/func.R
custom_function <- function(x, y) {
x + y
}
R/test_app.R
#' @import shiny
#' @import future
#' @import promises
#' @import bslib
#' @export
test_app <- function(user = NULL, box_token = NULL, meta_yaml_id = NULL, runApp = TRUE) {
future::plan(future::multisession)
app <- shiny::shinyApp(
ui = {
fluidPage(
numericInput("x", "x", value = 1),
numericInput("y", "y", value = 2),
actionButton("btn", "Add numbers"),
textOutput("sum")
)
},
server = function(input, output, session) {
sum_values <- ExtendedTask$new(function(x, y) {
future_promise({
custom_function(x, y)
})
})
observeEvent(input$btn, {
sum_values$invoke(input$x, input$y)
})
output$sum <- renderText({
sum_values$result()
})
}
)
# run build app
if (runApp)
runApp(app)
else
app
}
I currently get the error: Warning: Error in : there is no package called ‘testextended’ but since I'm IN that package I'm hesitant to use a library call inside the future call?
Anyone have any advice here?
Have you tried passing the function explicitly via future_promise
's globals
parameter?
Hi @ismirsehregal thanks for the suggestion! I tried like the code below but still get the same error? Do I need to leverage the packages argument or maybe envir? Is there something else I'm missing?
sum_values <- ExtendedTask$new(function(x, y) {
future::future(
{ custom_function(x, y) },
seed = 3,
globals = list(custom_function = testextended::custom_function),
)
})
I'd try to source("func.R", local = TRUE)
inside the shiny app and pass the customfunction
without referring to the package namespace (testextended::
)
This should work also without passing the globals
parameter. However, I currently can't test it.
Thank you for this! It does seem to work but I'm curious why I need to explicitly source the function to have this work when all other code/functions reliant on other functions are able to utilize the { testextended } package namespace? Is this best practices to be sourcing a file inside a package?
Code that works below:
#' @import shiny
#' @import future
#' @import promises
#' @import bslib
#' @export
test_app <- function(user = NULL, box_token = NULL, meta_yaml_id = NULL, runApp = TRUE) {
future::plan(future::multisession)
# Explicitly source the func.R code
source("R/func.R", local = TRUE)
app <- shiny::shinyApp(
ui = {
fluidPage(
numericInput("x", "x", value = 1),
numericInput("y", "y", value = 2),
actionButton("btn", "Add numbers"),
textOutput("sum")
)
},
server = function(input, output, session) {
sum_values <- ExtendedTask$new(function(x, y) {
promises::future_promise(
{ custom_function(x, y) },
seed = 3,
globals = c("custom_function", "x", "y")
)
})
observeEvent(input$btn, {
sum_values$invoke(input$x, input$y)
})
output$sum <- renderText({
sum_values$result()
})
}
)
# Run the app
if (runApp)
runApp(app)
else
app
}
I'm not sure regarding the best practice for package development in this context. However, you have to somehow, make the custom function available for the "fresh" R process running the future, once the future function fails to collect it on its own. I guess this is the issue. The custom function resides in an environment which isn't looked up or isn't available for the future (all based on assumptions as I havn't run your code).
I've use ExtendedTask
inside a packaged app without needing to make explicit calls to the package, but the architecture is a bit different to yours. Everything app-related is in inst/shiny
and server.R
contains library(package)
. There's an example here: shinyscholar/inst/shiny/modules/select_async.R at master · simon-smart88/shinyscholar · GitHub