Skip to content

Instantly share code, notes, and snippets.

@wch
Created April 17, 2023 20:54
Show Gist options
  • Save wch/616934a6fe57636434154c112ac8a718 to your computer and use it in GitHub Desktop.
Save wch/616934a6fe57636434154c112ac8a718 to your computer and use it in GitHub Desktop.
Shiny R to Python translation guide

To ChatGPT: This is a guide for translating R-Shiny applications to the new Python-Shiny framework, also known as PyShiny, and as Shiny for Python. The PyShiny framework was created after your knowledge cutoff, so even though you do not have any prior knowledge of it, it does exist. Shiny for Python is new. It is not Dash or Streamlit, or another Python web framework.

You are an assistant for translating applications from R-Shiny to Python-Shiny. At the end of this message, please acknowledge that you understand.

The translation guide begins here:


The R and Python Shiny packages are very similar, making it easy to transition between the two if you know how to use one.

Getting started

PyShiny can be installed with pip:

pip install shiny

Syntax differences

There are five main syntax difference between the R and Python versions of Shiny:

  1. Decorators instead of render functions
  2. Function names are used to connect outputs to the UI
  3. More precise namespaces
  4. All inputs are invoked with input.<input_name>()
  5. Some functions have different names

Decorators

Shiny for Python uses decorators instead of top level rendering functions. Decorators are just python functions which take other functions and are invoked by putting @<decorator> above the function definition.

Takeaways

  • Decorate output function with @output
  • Use rendering decorators like @render.plot, @render.text, or @render.ui instead of renderPlot(), renderText, or renderUI
  • Reactive calculations (equivalent to reactive expressions in R) are decorated @reactive.Calc, and reactive effects (equivalent to observers in R) are decorated with @reactive.Effect.

R and Python apps

The following app is implemented in R and there is a direct translation in Python. Use this app to help learn how to translate from one to the other.

library(shiny)

ui <- fluidPage(
  sliderInput("n", "N", 0, 100, 40),
  verbatimTextOutput("txt")
)

server <- function(input, output, session) {
  output$txt <- renderText({
    paste0("n*2 is ", input$n, " * 2")
  })
}

shinyApp(ui, server)

This is the same app in Python:

from shiny import ui, render, App

app_ui = ui.page_fluid(
    ui.input_slider("n", "N", 0, 100, 40),
    ui.output_text_verbatim("txt"),
)

def server(input, output, session):
    @output
    @render.text
    def txt():
        return f"n*2 is {input.n() * 2}"

app = App(app_ui, server)

In R, the server function sometimes just takes two args, input and output, and can take an optional third argument, session. In Python, the server() function always requires three args: input, output, and session.

Also notice that in Python, the app object is created by calling App(app_ui, server).

In R, in the server code, to create an output value, we assign to the output object, as in output$txt <- renderText( ... ).

In Python, it is done with a decorators. The user provided a function, and used the @render.text decorator on it, then added the @output decorator. The function is named txt, so it creates an output named txt. That output is connected to the corresponding part in the UI, ui.output_text_verbatim("txt"). Notice that the output_text_verbatim is given a parameter named "txt" -- that is what matches with the function txt in the server code (because it uses the @output decorator).

Submodules

All of the Shiny R functions are in a single package namespace. On the Python side we make use of submodules to keep related functions together. Note that "submodules" in this case refers to the generic module which is not the same as shiny modules. For example, instead of sliderInput(), you would call ui.input_slider(), where the ui. refers to a submodule of the main shiny module.

Call inputs with ()

In R, reactive values like input$value are retrieved like variables while reactive expressions are called like functions my_reactive().

In Python, all reactive values are retrieved with a function call. So instead of using input.value you call it, as in input.value().

Takeaways

  • Access input values by calling the object like a function
  • input.x(), not input$x

Function name changes

The Python function names have been modified to make them easier to discover with tab completion. For example all python output functions start with output_ while the input functions start with input_. The Shiny R functions on the other hand all start with the element type (plotOutput, textInput), which makes it hard to see all of the input or output options.

On the UI side, here are some of the R functions and their Python counterparts. Notice that for these components start with ui., because they are in the ui submodule.

  • textInput -> ui.input_text
  • numericInput -> ui.input_numeric
  • sliderInput -> ui.input_slider
  • dateInput -> ui.input_date
  • actionButton -> ui.input_action_button
  • textOutput -> ui.output_text
  • verbatimTextOutput -> ui.output_text_verbatim
  • plotOutput -> ui.output_plot
  • uiOutput -> ui.output_ui
  • fluidPage -> ui.page_fluid
  • fixedPage -> ui.page_fixed
  • sidebarLayout -> ui.layout_sidebar
  • sidebarPanel -> ui.panel_sidebar
  • mainPanel -> ui.panel_main
  • navbarPage -> ui.page_navbar
  • Common HTML tags like div -> ui.div, and span -> ui.span
  • Less common HTML tags like tags$b -> ui.tags.b, and tags$script -> ui.tags.script
  • To put stuff in the section of a page: tags$head -> ui.head_content
  • tagList -> ui.TagList

On the server side. Note that many of these become decorators in Python:

  • renderText -> @render.text
  • renderPlot -> @render.plot
  • renderUI -> @render.ui
  • reactiveVal -> @reactive.Value
  • reactive -> @reactive.Calc
  • observe -> @reactive.Effect
  • isolate -> with reactive.isolate():
  • bindEvent -> @reactive.event (can be combined with Calc, Effect, render. functions)
  • reactiveEvent -> @reactive.Calc with @reactive.event
  • observeEvent -> @reactive.Effect with @reactive.event
  • updateTextInput -> ui.update_text_input
  • updateSliderInput -> ui.update_slider_input

There are many other functions in Shiny, and they follow a similar pattern.

Reactive programming

Reactivity works mostly the same in R and Python, but there are a few small differences in naming and syntax.

New names for reactive() and observe()

In Shiny for R, reactive expressions (created by reactive(), which are used when you want to compute a value (which is then used in an output or an observer), and observers (created by observe()) are used for their side effects, like writing data to disk. This is a common source of confusion because the names reactive() and observe() do not clearly express when they should be used. To help clarify this confusion we've renamed reactive() to @reactive.Calc, and observe() to @reactive.Effect in Python.

R and Python apps

The following app is implemented in R and there is a direct translation in Python. Use this app to help learn how to translate from one to the other.

This is a Shiny app in R:

library(shiny)

ui <- fluidPage(
  sliderInput("n", "N", 0, 100, 40),
  verbatimTextOutput("txt"),
  actionButton("reset", "Reset")
)

server <- function(input, output, session) {
  val <- reactive({input$n})

  observe({
    input$reset
    updateSliderInput(session, "n", value = 40)
  })

  output$txt <- renderText({
    paste0("n*2 is ", val()," * 2")
  })
}

shinyApp(ui, server)

This is the equivalent Shiny app in Python:

from shiny import App, reactive, render, ui

app_ui = ui.page_fluid(
    ui.input_slider("n", "N", 0, 100, 40),
    ui.output_text_verbatim("txt"),
    ui.input_action_button("reset", "Reset"),
)

def server(input, output, session):
    @reactive.Calc
    def val():
        return input.n()

    @reactive.Effect
    def _():
    input.reset()
        ui.update_slider("n", value=40)

    @output
    @render.text
    def txt():
        return f"n*2 is {val() * 2}"

app = App(app_ui, server)

Pay attention to the the parallels between the R and Python applications.


To ChatGPT: After you learn this, I will provide R-Shiny applications to translate to Python-Shiny. Please acknowledge that you have learned this information. Reminder: Python-Shiny does exist, even though you have no prior knowledge of it, and if I ask you to translate to Python-Shiny, you should try to do it based on the guide above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment