Python Developer Guide for geyser
This guide provides extensive technical details for developers extending, debugging, or building Python Shiny applications and modules within the geyser hybrid repository.
Package Architecture (geyser/)
The Python package source code is housed in the geyser/ sub-directory:
geyser/
├── __init__.py # Package imports and exports
├── README.md # Sub-directory developer reference
├── datanames.py # Utility to list available dataset names
├── datasets.py # Dataset selector module (@module.ui & @module.server)
├── data.py # Data table viewer module
├── hist.py # Matplotlib histogram module
├── gghist.py # Plotnine/Seaborn histogram module
├── ggpoint.py # Scatter plot module
├── rows.py # Multi-module row layout container
├── switch.py # Dynamic module switcher container
└── io.py # I/O utilities (app_run & r_object)
Module Architecture & Reactivity
In Python Shiny, modules are constructed using two core decorators from shiny.module:
@module.ui: Defines the user interface layout. Input controls inside this decorated function are automatically namespaced.@module.server: Encapsulates reactive server logic.
1. The @module.ui Decorator
from shiny import module, ui
@module.ui
def hist_ui():
return ui.layout_sidebar(
ui.sidebar(
ui.input_slider("bins", "Number of bins:", 1, 50, 30)
),
ui.output_plot("distPlot")
)Scoping Note: Unlike R where
ns <- NS(id)must be explicitly wrapped around input IDs (ns("bins")), Python’s@module.uiautomatically scopes input IDs (binsbecomesmodule_id-binsinternally).
2. The @module.server Decorator
from shiny import module, render, reactive
import matplotlib.pyplot as plt
@module.server
def hist_server(input, output, session, dataset=reactive.value(None)):
@render.plot
def distPlot():
df = dataset()
if df is None:
return None
x = df.iloc[:, 0]
fig, ax = plt.subplots()
ax.hist(x, bins=input.bins(), color="darkgray", edgecolor="white")
return figI/O & Port Management (geyser.io)
Automatic Free Port Discovery (app_run)
When running interactive Python Shiny applications, port collisions can occur when launching multiple sessions. geyser.io.app_run binds to port 0 to dynamically ask the OS for an open port before starting the server.
from geyser.io import app_run
from shiny import ui
app = ui.ShinyApp(ui.page_fluid(), server_func)
app_run(app)Retrieving R Data into Python (r_object)
To ensure data parity with the R package, geyser.io.r_object utilizes rpy2 to pull R datasets (such as faithful) directly into pandas.DataFrame objects:
from geyser.io import r_object
faithful_df = r_object("faithful")
print(faithful_df.head())Standalone Module Launchers
Each module in geyser provides a standalone app runner function (e.g., app_hist(), app_datasets(), app_rows()).
To test a module standalone:
import geyser.hist
geyser.hist.app_hist()Quarto & Shinylive Integration
Reserved Filename Rules
When placing Python Shiny apps in directories that contain Quarto documents (.qmd), never name your script app.py. Quarto reserves app.py for its internal Python web deployment pipeline. Always use explicit names like app_hist.py or app_demo.py.
Shinylive Python Deployment
Python modules in geyser can be compiled to WebAssembly via Shinylive using:
quarto render docs/The resulting web assets are output to docs/ for GitHub Pages hosting.