The opinionated Indie Maker Python web framework for shipping MVPs stupid-fast.
Project description
Shipy
The opinionated Indie Maker Python web framework for shipping MVPs stupid-fast.
- App, Request, Response, routing (imperative)
- Jinja2 templates via
shipy.render.render() - HTMX support for interactive UI without complex JavaScript
- SQLite helpers in
shipy.sql(query,one,execute,tx) - Built-in auth with users/sessions +
@login_requireddecorator - Request middleware for per-request data and short-circuiting
- Signed-cookie sessions with CSRF + flash in
shipy.session - CLI:
shipy new,shipy dev,shipy db init,shipy deploy
Note: The SQL write helper is named execute (not exec, since exec is a Python keyword).
Quick start
pip install shipy-web
shipy new myapp && cd myapp
shipy db init
shipy dev
Visit http://localhost:8000 and sign up to get started!
Example
See examples/hello/app/main.py:1 and examples/hello/app/views/home/index.html:1.
CLI
shipy new <name>— creates an auth-first scaffolded project in./<name>shipy dev [--app app.main:app] [--host 127.0.0.1] [--port 8000]shipy db init [--db ./data/app.db] [--schema data/schema.sql]— initialize databaseshipy db backup [--db ./data/app.db] [--out data/backups]— create database backupshipy db run <path.sql> [--db ./data/app.db]— execute SQL scriptshipy db make-migration <name> [--dir data/migrations]— create timestamped migration fileshipy db ls [--dir data/migrations]— list migration filesshipy db shell [--db ./data/app.db]— open interactive sqlite3 shellshipy deploy— deployment helpers
Templates
Default template root is app/views. Use shipy.render.render('path/to/template.html', ctx, request=req).
HTMX Support
Shipy includes built-in HTMX support for interactive UI without complex JavaScript:
from shipy.render import render_htmx, is_htmx_request
# Use render_htmx for HTMX-enhanced templates
def home(req):
todos = query("SELECT * FROM todos ORDER BY created_at DESC")
return render_htmx(req, "home/index.html", todos=todos)
# Check if request is from HTMX
def todo_create(req):
if is_htmx_request(req):
# Return partial for HTMX
return render_htmx(req, "todos/list.html", todos=todos)
else:
# Return full page
return render_req(req, "home/index.html", todos=todos)
HTMX templates have access to htmx context:
htmx.request- true if request is from HTMXhtmx.target- HTMX target elementhtmx.trigger- HTMX trigger event
<!-- HTMX form with partial updates -->
<form hx-post="/todos" hx-target="#todo-list" hx-swap="innerHTML">
<input name="title" placeholder="New todo" />
<button>Add</button>
</form>
<div id="todo-list">
{% for todo in todos %}
<div hx-delete="/todos/{{ todo.id }}" hx-target="this" hx-swap="outerHTML">
{{ todo.title }}
<button>Delete</button>
</div>
{% endfor %}
</div>
HTMX is included by default in scaffolded apps via CDN.
Middleware
Request middleware runs on every request after the Request is constructed but before route handlers. Use @app.middleware("request") to register middleware functions.
# Attach commonly used data to req.state
@app.middleware("request")
def attach_user(req):
req.state.user = current_user(req)
req.state.csrf_token = get_csrf_token(req)
# Short-circuit with a Response (e.g., maintenance mode)
@app.middleware("request")
def maintenance_mode(req):
if maintenance_enabled:
return Response.text("Site under maintenance", 503)
# Use in route handlers
def home(req):
user = req.state.user # Available via middleware
return render_req(req, "home.html", user=user)
Middleware can:
- Attach data to
req.statefor easy access in handlers - Short-circuit requests by returning a Response
- Handle errors and logging
- Add per-request objects (DB connections, etc.)
Sessions
Session data is stored in a signed cookie using itsdangerous. Set SHIPY_SECRET in your environment for production.
Flash messages are available via req.session.flash(message, category) and req.session.get_flashed_messages() in requests. CSRF token via req.session.get_csrf_token().
Authentication
Shipy includes built-in authentication helpers:
from shipy.auth import current_user, login_required, login, logout
# Check if user is logged in
user = current_user(req)
# Protect routes with @login_required decorator
@login_required()
def secret(req):
# req.state.user is guaranteed to exist here
return render_req(req, "secret.html", user=req.state.user)
# Custom redirect for unauthenticated users
@login_required(redirect_to="/custom-login")
def admin(req):
return render_req(req, "admin.html", user=req.state.user)
# Manual authentication (for custom logic)
def manual_auth(req):
user = current_user(req)
if not user:
return Response.redirect("/login")
return render_req(req, "page.html", user=user)
The @login_required decorator:
- Redirects unauthenticated users to
/login(or custom path) - Automatically attaches user to
req.state.user - Works with middleware for additional per-request data
Database
SQLite helpers in shipy/sql.py:1 use ./data/app.db by default (created as needed).
Override the database path with SHIPY_DB environment variable:
SHIPY_DB=/path/to/custom.db shipy dev
query(sql, params=()) -> list[dict]one(sql, params=()) -> dict | Noneexecute(sql, params=()) -> intwith tx() as conn: conn.execute(...)
Apply schema with shipy db init --schema data/schema.sql.
Migrations
For one-off database changes, use the simple migration system:
# Create a migration file
shipy db make-migration "add user profiles"
# Edit the generated file in data/migrations/
# Then apply it
shipy db run data/migrations/20250926185037_add_user_profiles.sql
# List all migrations
shipy db ls
# Open interactive sqlite3 shell for debugging
shipy db shell
Migrations are plain SQL files wrapped in transactions for atomicity. No complex migration engine - just timestamped files you can read and understand.
License
MIT.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file shipy_web-0.3.2.tar.gz.
File metadata
- Download URL: shipy_web-0.3.2.tar.gz
- Upload date:
- Size: 19.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
684cecfc69dd95ae1fd95d8ab7fb3c8d790aa83abe6d954aed862aac5854fc9f
|
|
| MD5 |
c70c449dd4db70b4b0a23e5895ba70bb
|
|
| BLAKE2b-256 |
c34f69a65c80d5ff42f3a1b9ffe9841cf25363c24decdb9a139f884a4800b7fb
|
File details
Details for the file shipy_web-0.3.2-py3-none-any.whl.
File metadata
- Download URL: shipy_web-0.3.2-py3-none-any.whl
- Upload date:
- Size: 23.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ce812c4b13e334646d4eb232c3c2f5d7f3eb9604a354adf8725e627fa2d2d359
|
|
| MD5 |
d3533be4c6176b15c89d153434f3c344
|
|
| BLAKE2b-256 |
781e2775df7973ed089e08e7d7a9253292b5deb8da5b76f5ea22df9f8b0e571e
|