Run a Python script within a Shiny app using reticulate

220 views Asked by At

I want to create a Shiny app and within the app I want to run a Python script. This Python script should take parameters previously defined in the App. But the Python script does not seem to recognize the R-values. I have a textInput where I define the variable "year". This variable I want to use within the Python script.

This is my Shiny app

library(shiny)
library(reticulate)

# Define the user interface
ui <- fluidPage(
  tabsetPanel(
    tabPanel(textInput("year", "Year"),
             downloadButton("download_btn", "Download"))
  )
)


server <- function(input, output, session) {
  
  year <- reactive(input$year)

  adjusted_data <- reactive({
    source_python("python_script.py")
    data <- read_csv("tmp.csv")
    data
  })
  
  output$download_btn <- downloadHandler(
    filename = function() {
      file_name <- "temp.xlsx"
    },
    content = function(file) {
        openxlsx::write.xlsx(adjusted_data(), file)
    }
  )

}

shinyApp(ui, server)

With the python script ("python_script.py") just being

year = r.year()

But when I let the Shiny app run I get the error message:

Listening on http://127.0.0.1:7564 Warning: Error in eval: RuntimeError: object 'year' not found Run reticulate::py_last_error() for details. 4: runApp 3: print.shiny.appobj 1: source

I had to minimize what I actually wanted to do which is why the Shiny App currently does not makes sense. But can somebody maybe help me out? Why does this error occur? Is there a way to achieve what I want do?

1

There are 1 answers

1
margusl On BEST ANSWER

There's a good chance this problem arose only because you tried to start simple, but still -- instead of accessing R objects through r in your Python code, perhaps consider defining functions & classes in your Python script and pass user input as arguments from your R code.

It's also much easier to test and maintain pure Pyhon code. Using py_run_string() instead of source_python() here is just to keep the answer self-contained.

library(shiny)
library(reticulate)

# instead of accessing r.year, specify it as an argument:
py_run_string('
def do_something(year):
  return f"Python function called with: <{year}>"
')

ui <- fluidPage(
  tabsetPanel(
    tabPanel("panel_1",
             textInput("year", "Year"),
             textOutput("txt"))
  )
)

server <- function(input, output, session) {
  adjusted_data <- reactive({
    # pass value to python function as an argument, 
    # python code never needs to know about r & reticulate
    py$do_something(input$year)
  })
  
  output$txt <- renderText(adjusted_data())
}

shinyApp(ui, server)