Claude Code Plugins

Community-maintained marketplace

Feedback

streamlit-uv-development

@bossjones/streamlit-replicate-boss
0
0

Use this skill when developing Streamlit applications with uv package manager. Covers project setup, running apps, testing (unit/e2e), and development workflows. Trigger when user mentions "streamlit", "uv run streamlit", "streamlit testing", or asks about building data apps with Python.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name streamlit-uv-development
description Use this skill when developing Streamlit applications with uv package manager. Covers project setup, running apps, testing (unit/e2e), and development workflows. Trigger when user mentions "streamlit", "uv run streamlit", "streamlit testing", or asks about building data apps with Python.

Streamlit Development with uv

Overview

This skill enables rapid Streamlit application development using uv as the package manager. It covers project initialization, running apps, testing strategies, and production-ready development patterns.

Quick Start

Create New Streamlit Project

# Create project directory
mkdir my-streamlit-app && cd my-streamlit-app

# Initialize with uv
uv init --name my-streamlit-app

# Add streamlit dependency
uv add streamlit

# Create main app file
cat > app.py << 'EOF'
import streamlit as st

st.set_page_config(page_title="My App", page_icon="📊", layout="wide")

st.title("Hello, Streamlit!")
st.write("Welcome to your new app.")
EOF

# Run the app
uv run streamlit run app.py

Run Existing Streamlit App

# With uv (preferred)
uv run streamlit run app.py

# With options
uv run streamlit run app.py --server.port 8501 --server.headless true

# With browser disabled (for CI/testing)
uv run streamlit run app.py --server.headless true --browser.serverAddress localhost

Project Structure

Recommended Layout

my-streamlit-app/
├── pyproject.toml          # Project config with uv
├── uv.lock                  # Lock file (commit this)
├── .python-version          # Python version pin
├── app.py                   # Main entry point (or src/app.py)
├── pages/                   # Multi-page app pages
│   ├── 1_📊_Dashboard.py
│   ├── 2_📈_Analytics.py
│   └── 3_⚙️_Settings.py
├── components/              # Custom components
│   └── sidebar.py
├── utils/                   # Helper functions
│   ├── __init__.py
│   ├── data.py
│   └── charts.py
├── tests/                   # Test files
│   ├── conftest.py
│   ├── test_utils.py
│   └── e2e/
│       └── test_app.py
├── .streamlit/              # Streamlit config
│   ├── config.toml
│   └── secrets.toml         # (gitignored)
└── data/                    # Static data files

pyproject.toml Example

[project]
name = "my-streamlit-app"
version = "0.1.0"
description = "A Streamlit data application"
requires-python = ">=3.10"
dependencies = [
    "streamlit>=1.40.0",
    "pandas>=2.0.0",
    "plotly>=5.0.0",
]

[project.optional-dependencies]
dev = [
    "pytest>=8.0.0",
    "pytest-playwright>=0.5.0",
    "ruff>=0.8.0",
]

[tool.uv]
dev-dependencies = [
    "pytest>=8.0.0",
    "pytest-playwright>=0.5.0",
    "ruff>=0.8.0",
]

[tool.ruff]
line-length = 100
target-version = "py310"

[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]

Testing Strategies

Unit Testing (Logic & Utils)

Test business logic without Streamlit context:

# tests/test_utils.py
import pytest
from utils.data import process_data, validate_input

def test_process_data_with_valid_input():
    data = {"value": 10, "multiplier": 2}
    result = process_data(data)
    assert result == 20

def test_validate_input_rejects_negative():
    with pytest.raises(ValueError, match="must be positive"):
        validate_input(-5)

Run unit tests:

uv run pytest tests/test_utils.py -v

Testing Streamlit Components with AppTest

Use Streamlit's built-in AppTest for headless testing:

# tests/test_app.py
import pytest
from streamlit.testing.v1 import AppTest

def test_app_loads():
    """Test that the app loads without errors."""
    at = AppTest.from_file("app.py")
    at.run()
    assert not at.exception

def test_sidebar_selection():
    """Test sidebar widget interactions."""
    at = AppTest.from_file("app.py")
    at.run()

    # Interact with selectbox
    at.selectbox[0].select("Option B").run()
    assert at.session_state["selected"] == "Option B"

def test_button_click_updates_state():
    """Test button click behavior."""
    at = AppTest.from_file("app.py")
    at.run()

    # Click button
    at.button[0].click().run()
    assert "result" in at.session_state

def test_form_submission():
    """Test form with multiple inputs."""
    at = AppTest.from_file("app.py")
    at.run()

    # Fill form fields
    at.text_input[0].input("John Doe").run()
    at.number_input[0].set_value(25).run()
    at.button("Submit").click().run()

    # Check success message appears
    assert len(at.success) > 0

E2E Testing with Playwright

For full browser-based testing:

# tests/e2e/test_app.py
import pytest
from playwright.sync_api import Page, expect
import subprocess
import time

@pytest.fixture(scope="module")
def streamlit_app():
    """Start Streamlit app for testing."""
    proc = subprocess.Popen(
        ["uv", "run", "streamlit", "run", "app.py",
         "--server.headless", "true", "--server.port", "8599"],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
    )
    time.sleep(5)  # Wait for startup
    yield "http://localhost:8599"
    proc.terminate()
    proc.wait()

def test_homepage_loads(page: Page, streamlit_app: str):
    """Test that homepage renders correctly."""
    page.goto(streamlit_app)
    expect(page.locator("h1")).to_contain_text("Hello")

def test_sidebar_navigation(page: Page, streamlit_app: str):
    """Test sidebar navigation works."""
    page.goto(streamlit_app)
    page.click("text=Dashboard")
    expect(page).to_have_url(f"{streamlit_app}/Dashboard")

def test_data_input_and_display(page: Page, streamlit_app: str):
    """Test data entry workflow."""
    page.goto(streamlit_app)
    page.fill("input[aria-label='Enter value']", "42")
    page.click("button:has-text('Calculate')")
    expect(page.locator(".stSuccess")).to_be_visible()

Run E2E tests:

# Install playwright browsers first
uv run playwright install chromium

# Run E2E tests
uv run pytest tests/e2e/ -v

Test Fixtures for Streamlit

# tests/conftest.py
import pytest
import pandas as pd
from streamlit.testing.v1 import AppTest

@pytest.fixture
def sample_dataframe():
    """Provide sample data for tests."""
    return pd.DataFrame({
        "name": ["Alice", "Bob", "Charlie"],
        "value": [100, 200, 300],
    })

@pytest.fixture
def app_test():
    """Provide fresh AppTest instance."""
    at = AppTest.from_file("app.py")
    at.run()
    return at

@pytest.fixture
def app_with_session_state():
    """Provide AppTest with preset session state."""
    at = AppTest.from_file("app.py")
    at.session_state["user_authenticated"] = True
    at.session_state["username"] = "testuser"
    at.run()
    return at

Development Workflows

Live Development with Hot Reload

# Start with auto-reload (default)
uv run streamlit run app.py

# Disable for production testing
uv run streamlit run app.py --server.runOnSave false

Multi-Page App Development

# pages/1_📊_Dashboard.py
import streamlit as st

st.set_page_config(page_title="Dashboard", page_icon="📊")
st.title("Dashboard")

# Access shared session state
if "data" not in st.session_state:
    st.session_state.data = None

# Page-specific content
col1, col2 = st.columns(2)
with col1:
    st.metric("Total Users", 1234, delta=56)
with col2:
    st.metric("Revenue", "$45,678", delta="12%")

Environment & Secrets Management

# .streamlit/secrets.toml (gitignored)
[database]
host = "localhost"
port = 5432
username = "admin"
password = "secret123"

[api]
key = "sk-..."
# Access in app
import streamlit as st

db_host = st.secrets["database"]["host"]
api_key = st.secrets["api"]["key"]

Docker Deployment

# Dockerfile
FROM python:3.11-slim

# Install uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv

WORKDIR /app

# Copy dependency files
COPY pyproject.toml uv.lock ./

# Install dependencies
RUN uv sync --frozen --no-dev

# Copy app code
COPY . .

# Expose port
EXPOSE 8501

# Health check
HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health

# Run app
CMD ["uv", "run", "streamlit", "run", "app.py", \
     "--server.port=8501", "--server.address=0.0.0.0", \
     "--server.headless=true"]

Build and run:

docker build -t my-streamlit-app .
docker run -p 8501:8501 my-streamlit-app

Common Patterns

Session State Management

import streamlit as st

# Initialize state
if "counter" not in st.session_state:
    st.session_state.counter = 0

# Update with callback
def increment():
    st.session_state.counter += 1

st.button("Increment", on_click=increment)
st.write(f"Count: {st.session_state.counter}")

Caching Data and Resources

import streamlit as st
import pandas as pd

@st.cache_data(ttl=3600)  # Cache for 1 hour
def load_data(url: str) -> pd.DataFrame:
    return pd.read_csv(url)

@st.cache_resource  # Cache across sessions
def get_database_connection():
    return create_connection()

# Use cached data
df = load_data("https://example.com/data.csv")

Error Handling in Apps

import streamlit as st

try:
    result = risky_operation()
    st.success(f"Operation completed: {result}")
except ValueError as e:
    st.error(f"Invalid input: {e}")
except ConnectionError:
    st.warning("Connection failed. Please try again.")
    if st.button("Retry"):
        st.rerun()
except Exception as e:
    st.exception(e)  # Shows full traceback

Forms for Batch Input

import streamlit as st

with st.form("user_form"):
    name = st.text_input("Name")
    age = st.number_input("Age", min_value=0, max_value=120)
    email = st.text_input("Email")

    submitted = st.form_submit_button("Submit")

    if submitted:
        if not name or not email:
            st.error("Name and email are required")
        else:
            st.success(f"Welcome, {name}!")
            # Process form data

Troubleshooting

Common Issues

Port already in use:

# Find and kill process
lsof -i :8501 | grep LISTEN
kill -9 <PID>

# Or use different port
uv run streamlit run app.py --server.port 8502

Import errors with uv:

# Ensure dependencies are synced
uv sync

# Check installed packages
uv pip list

Widget state issues:

# Use key parameter for dynamic widgets
for i, item in enumerate(items):
    st.text_input(f"Item {i}", key=f"input_{i}")

Slow app reload:

# Move expensive operations outside main flow
@st.cache_data
def expensive_computation():
    # This runs once and is cached
    return compute_result()

Resources