pythonrshinyreticulate

Run a Python script within a Shiny app using reticulate


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?


Solution

  • 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)