Geyser Developer Guide
Welcome to the developer guide for geyser. This repository is a hybrid R package (geyser v0.7.2) and Python package (geyser v0.2) demonstrating Shiny module patterns using the Old Faithful geyser dataset as a reference tool.
This guide clarifies the codebase structure, module boundaries, reactivity flow, cross-language parity, and local development workflows for human maintainers and AI pair-programming assistants.
Table of Contents
- Architecture Overview
- Repository Directory Layout
- Shiny Module Design Patterns
- Environment Setup & Development Workflows
- Tutorial & Connecting Modules Layout
- Verification, Testing, and Publishing
- Sub-Guides & Further Reading
Architecture Overview
geyser serves as an educational laboratory for modularizing Shiny web applications. It implements identical interactive components across both R (Shiny) and Python (Shiny for Python).
+---------------------------------------+
| User Interface |
+-------------------+-------------------+
|
+---------------------+---------------------+
| |
v v
+-------------------+ +-------------------+
| R Package | | Python Package |
| (geyser R) | | (geyser Python) |
+---------+---------+ +---------+---------+
| |
v v
+-------------------+ +-------------------+
| R Shiny Modules | | Python Modules |
| (5-func pattern) | | (@module.server) |
+---------+---------+ +---------+---------+
| |
+---------------------+---------------------+
|
v
+---------------------------------------+
| Default Dataset (faithful) |
+---------------------------------------+
Key features of the architecture: - R Package: Built following standard R package structures using R/ source files, DESCRIPTION, and NAMESPACE. - Python Package: Built as a Python package using pyproject.toml with module logic in geyser/. - Educational Progression: Source code progress from monoliths to refactored functions, callModule(), modern moduleServer(), and Python equivalents.
Repository Directory Layout
geyser/
├── R/ # R package source code (one file per module)
│ ├── dataApp.R # Data viewer / table module
│ ├── datanames.R # Utility to list datasets with numeric columns
│ ├── datasetsApp.R # Dataset selector module
│ ├── gghistApp.R # ggplot2 histogram module
│ ├── ggpointApp.R # ggplot2 scatter plot module
│ ├── histApp.R # Base R histogram module
│ ├── rowsApp.R # Multi-module row layout wrapper
│ ├── switchApp.R # Module switcher module
│ └── wrapperApp.R # Wrapper module demo
├── geyser/ # Python package source code
│ ├── README.md # Python package developer reference
│ ├── data.py # Python data table module
│ ├── datanames.py # Python dataset listing helper
│ ├── datasets.py # Python dataset selector module
│ ├── gghist.py # Python plotnine/seaborn histogram module
│ ├── ggpoint.py # Python scatter plot module
│ ├── hist.py # Python matplotlib histogram module
│ ├── io.py # Helper utilities (app_run, r_object)
│ ├── rows.py # Python row layout module
│ └── switch.py # Python module switcher module
├── inst/ # Installed package assets & tutorials
│ ├── build_module/ # 5-stage progressive module tutorial
│ │ ├── 1_oldFaithful/ # Stage 1: Monolithic single-file app
│ │ ├── 2_newFaithful/ # Stage 2: Refactored into UI/Server functions
│ │ ├── 3_callModule/ # Stage 3: Deprecated callModule approach
│ │ ├── 4_moduleServer/ # Stage 4: Modern moduleServer approach
│ │ └── 5_python/ # Stage 5: Python equivalent implementations
│ └── connect_modules/ # Advanced multi-module layout examples
│ ├── app.R # Multi-module navigation bar app
│ ├── appPages.R # Multi-page layout
│ ├── appTwin.R # Two identical modules sharing reactive data
│ ├── appFlip.R # Modules with shared/flipped controls
│ ├── appDupe.R # Duplicate module ID conflict demonstration
│ ├── python/ # Multi-module Python scripts
│ └── quarto/ # Quarto document versions
├── docs/ # Documentation site & Quarto assets
│ ├── devel/ # Detailed developer sub-guides
│ │ └── python.md # Extensive Python Developer Guide
│ ├── geyserShinyR.qmd # R Shiny Modules presentation slides
│ ├── geyserShinyPython.qmd # Python Shiny Modules presentation slides
│ └── gallery.html # Interactive Shinylive gallery
├── vignettes/ # R package vignettes
│ └── DeveloperGuide.Rmd # Master R Vignette Developer Guide
├── DESCRIPTION # R package metadata
├── NAMESPACE # R exported function declarations
├── pyproject.toml # Python package configuration
├── module.md # Comprehensive Shiny module tutorial text
├── publish.md # Deployment & GitHub Pages instructions
├── README.md # Main repository landing page
└── AGENTS.md # Project rules & AI instructions
Shiny Module Design Patterns
R 5-Function Module Pattern
Every R module in R/ strictly follows a standardized 5-function pattern:
| Function Signature | Role & Scope | Namespace Requirement |
|---|---|---|
moduleNameInput("id") |
UI control elements | Wrapped with NS(id) |
moduleNameOutput("id") |
Output display placeholders | Wrapped with NS(id) |
moduleNameUI("id") |
Combined or dynamic UI layout | Wrapped with NS(id) |
moduleNameServer("id", data) |
Reactive server business logic | Uses moduleServer(id, function(input, output, session) {...}) |
moduleNameApp(data) |
Standalone test wrapper app | Standard shinyApp(ui, server) (no ns() at top level) |
R Module Code Skeleton (R/histApp.R Example)
# 1. Input UI
histInput <- function(id) {
ns <- NS(id)
tagList(
sliderInput(ns("bins"), "Number of bins:", min = 1, max = 50, value = 30)
)
}
# 2. Output UI
histOutput <- function(id) {
ns <- NS(id)
plotOutput(ns("distPlot"))
}
# 3. Combined UI
histUI <- function(id) {
ns <- NS(id)
sidebarLayout(
sidebarPanel(histInput(id)),
mainPanel(histOutput(id))
)
}
# 4. Server Logic
histServer <- function(id, dataset = reactive(faithful)) {
moduleServer(id, function(input, output, session) {
output$distPlot <- renderPlot({
x <- dataset()[, 1]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
hist(x, breaks = bins, col = "darkgray", border = "white")
})
})
}
# 5. Standalone App Wrapper
histApp <- function(dataset = faithful) {
ui <- fluidPage(histUI("hist"))
server <- function(input, output, session) {
histServer("hist", dataset = reactive(dataset))
}
shinyApp(ui, server)
}Python Shiny Module Pattern
Python Shiny modules use function-based UI functions and the @module.server decorator from shiny.module.
Python Module Code Skeleton (geyser/hist.py Example)
from shiny import module, ui, render, reactive
import matplotlib.pyplot as plt
from geyser.io import app_run
# 1. 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")
)
# 2. Module Server
@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 fig
# 3. Standalone App Runner
def app_hist(dataset=None):
from geyser.io import r_object
if dataset is None:
dataset = r_object('faithful')
app_ui = ui.page_fluid(hist_ui("hist"))
def server(input, output, session):
hist_server("hist", dataset=reactive.value(dataset))
app = ui.ShinyApp(app_ui, server)
app_run(app)
return appR and Python Module Mapping
| Component | R Implementation (R/) |
Python Implementation (geyser/) |
Primary Purpose |
|---|---|---|---|
| Base Histogram | histApp.R |
hist.py |
Base graphics histogram |
| ggplot Histogram | gghistApp.R |
gghist.py |
Grammar of graphics histogram |
| Scatter Plot | ggpointApp.R |
ggpoint.py |
Bivariate scatter plot |
| Dataset Selector | datasetsApp.R |
datasets.py |
Select numeric datasets |
| Data Viewer | dataApp.R |
data.py |
Interactive data table (DT / render.data_frame) |
| Row Layout | rowsApp.R |
rows.py |
Multi-module horizontal row layout |
| Module Switcher | switchApp.R |
switch.py |
Dynamic module navigation / tab switching |
| Wrapper Module | wrapperApp.R |
— | Example of module wrapping another module |
| Dataset Utility | datanames.R |
datanames.py |
List numeric dataset names |
| I/O & Port Helper | — | io.py |
app_run() free port discovery & r_object() |
Environment Setup & Development Workflows
R Package Development
Requirements
- R >= 4.2.0
- Dependencies:
bslib,dplyr,DT,ggplot2,graphics,rlang,shiny,stats,stringr,tibble
Interactive R Workflow
# Load all functions into local environment during dev
devtools::load_all()
# Run a specific module standalone app
histApp()
datasetsApp()
# Re-generate documentation in man/
devtools::document()
# Run full package checks
devtools::check()Python Package Development
Requirements
- Python >= 3.8 (Python 3.12 recommended)
- Dependencies:
shiny,matplotlib,numpy,scipy,pandas,nest_asyncio,rpy2
Local Python Setup
# Clone and install package in editable mode
git clone https://github.com/byandell/geyser.git
cd geyser
pip install -e .
# Test Python modules directly
python -c "import geyser; geyser.hist.app_hist()"Cross-Language Interoperability & Gotchas
- Port Discovery (
geyser.io.app_run):- In Python, running multiple Shiny apps within interactive sessions can conflict on default ports.
- Use
geyser.io.app_run(app)which uses standard socket binding (s.bind(('', 0))) to auto-allocate an available free port.
- Reserved Filenames in Quarto:
- Do NOT name Python Shiny scripts
app.pyif stored in directories that contain Quarto documents. Quarto reservesapp.pyfor Python Shinylive rendering and will throw deployment conflicts. Use names likeapp_hist.pyorapp_demo.py.
- Do NOT name Python Shiny scripts
- Namespace Scoping:
- R uses
ns <- NS(id)explicitly for inputs and outputs in UI functions. - Python
@module.uiautomatically scopes input IDs inside the decorated function.
- R uses
- Data Exchange (
rpy2):- The Python utility
geyser.io.r_object('faithful')usesrpy2to fetch R built-in datasets directly intopandas.DataFrameobjects for feature parity.
- The Python utility
Tutorial & Connecting Modules Layout
Progressive Tutorial (inst/build_module)
inst/build_module/ guides developers through the 5 architectural stages of Shiny module refactoring:
1_oldFaithful/: Monolithic app (uiandserverin single script).2_newFaithful/: Logic extracted into custom functions (without module namespaces).3_callModule/: Historical module approach using deprecatedcallModule().4_moduleServer/: Modern module approach usingmoduleServer()andNS().5_python/: Python implementations matching Stage 4.
Multi-Module Layouts (inst/connect_modules)
inst/connect_modules/ demonstrates advanced composition patterns: - Central Data Routing (app.R): Connecting datasetsServer to histServer, gghistServer, and ggpointServer. - Multi-page navigation (appPages.R): Rendering modules inside tabbed panels. - Shared Reactive State (appTwin.R): Two independent visualizer modules consuming the same reactive dataset. - Duplicate ID Traps (appDupe.R): Illustrates common mistake when identical module IDs are instantiated twice in one session.
Verification, Testing, and Publishing
Testing Modules
- R Modules: Test individual modules by calling
moduleNameApp(). - Python Modules: Run standalone test launchers
python -m geyser.histor scripts ininst/build_module/5_python/.
Documentation Site & Shinylive
The repository documentation and interactive Shinylive demos are rendered in docs/ using Quarto:
# Render Quarto site locally
quarto render docs/See publish.md for GitHub Pages publishing details.
Sub-Guides & Further Reading
For deeper language-specific sub-guides and detailed documentation: - 📖 R Vignette Developer Guide (vignettes/DeveloperGuide.Rmd) | (GitHub Source) - 🐍 Python Developer Guide (docs/devel/python.md) | (GitHub Source) - 📦 Python Package README (geyser/README.md) | (GitHub Source) - 🎓 Shiny Module Tutorial (module.md) | (GitHub Source) - 🚀 Publishing & GitHub Pages Guide (publish.md) | (GitHub Source)