Skip to content

Instantly share code, notes, and snippets.

@andfanilo
Created July 13, 2024 09:32
Show Gist options
  • Save andfanilo/d1619cb9dd82b4dcb41c19075381ae28 to your computer and use it in GitHub Desktop.
Save andfanilo/d1619cb9dd82b4dcb41c19075381ae28 to your computer and use it in GitHub Desktop.
Pseudo Next.js file-based routing with Streamlit Multipage v2
"""
File-system based router inspired from Next.js, where folders are used to define routes.
Each folder with a page.py maps to a URL segment.
The title for the page in the navigation menu is the argument of the first `st.title` call.
Example Directory Structure:
├── app
│ ├── page.py <- @/
│ ├── admin
│ | └── page.py <- @/admin
│ └── dashboard
│ ├── page.py <- @/dashboard
│ └── settings
│ └── page.py <- @/dashboard/settings
|
└── streamlit_app.py <- Router
Inspiration: https://nextjs.org/docs/app/building-your-application/routing/defining-routes
"""
import ast
from pathlib import Path
from typing import List
import streamlit as st
def find_all_pages() -> List[Path]:
"""Finds all files named `page.py` in the `app` directory."""
app_path = Path("./app")
return list(app_path.rglob("page.py"))
@st.cache_data
def extract_page_uri(path_str: Path):
"""Extracts the URI from a file path for Streamlit multipage."""
parts = path_str.parts
# first part is app, last part is page.py
uri = "/".join(parts[1:-1])
return uri
@st.cache_data
def find_first_st_title_call(file_path: Path):
"""Finds the first `st.title` call in a Python file using ast."""
with open(file_path, "r") as f:
source_code = f.read()
tree = ast.parse(source_code)
for node in ast.walk(tree):
if isinstance(node, ast.Call):
if isinstance(node.func, ast.Attribute):
if (
node.func.attr == "title"
and isinstance(node.func.value, ast.Name)
and node.func.value.id == "st"
):
return node.args[0].s # Extract the title string
return "Page" # No st.title call found, put default title
all_page_files = find_all_pages()
all_pages = [
st.Page(
page,
title=find_first_st_title_call(page),
url_path=extract_page_uri(page),
default=(extract_page_uri(page) == ""),
)
for page in all_page_files
]
pg = st.navigation(all_pages)
pg.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment