You are viewing archived documentation for pyrith v0.1.0. The latest version is v0.4.0.
pyrith
v0.1.0Initial release. Core HTTP routing, middleware, HTML DSL, SQLite ORM, session auth, form validation, WebSocket, and CLI.
Overview
pyrith v0.1.0 is the initial release — a minimal but complete Python web framework built entirely from the standard library. No external runtime dependencies: no Flask, no Django, no SQLAlchemy, no Jinja2. Everything — HTTP server, routing, HTML rendering, SQLite ORM, session management, form validation, WebSocket — is implemented from scratch in pure Python.
This is the initial release. We recommend upgrading to v0.4.0 (latest) which adds ASGI, OAuth2, MFA, background tasks, payments, AI/LLM streaming, admin panel, telemetry, and 26 new modules — with full backwards compatibility.
Installation
pip install pyrith==0.1.0
# Requires Python 3.9+. No external runtime dependencies.Quick Start
from pyrith import Pyrith
from pyrith.response import HTMLResponse, JSONResponse
app = Pyrith(__name__)
@app.get('/')
def index(request):
return HTMLResponse('<h1>Hello from pyrith!</h1>')
@app.get('/api/health')
def health(request):
return JSONResponse({'status': 'ok', 'version': '0.1.0'})
if __name__ == '__main__':
app.run(host='127.0.0.1', port=8080, debug=True)Routing
app = Pyrith(__name__)
# HTTP methods
@app.get('/articles')
def list_articles(request):
return JSONResponse({'articles': []})
@app.post('/articles')
def create_article(request):
data = request.json()
return JSONResponse({'created': True}, status=201)
# Path parameters
@app.get('/articles/{article_id}')
def get_article(request):
article_id = request.params['article_id']
return JSONResponse({'id': article_id})
# Delete / put / patch
@app.delete('/articles/{article_id}')
def delete_article(request):
return JSONResponse({'deleted': True})
# Route groups
from pyrith.router import Router
api = Router(prefix='/api')
@api.get('/users')
def list_users(request):
return JSONResponse({'users': []})
app.include(api)Middleware
from pyrith.middleware import CORSMiddleware, LoggingMiddleware, StaticFilesMiddleware, GZipMiddleware
app.use(LoggingMiddleware())
app.use(CORSMiddleware(allow_origins=['*']))
app.use(StaticFilesMiddleware('./public', '/static'))
app.use(GZipMiddleware(min_size=500))Response
from pyrith.response import HTMLResponse, JSONResponse, RedirectResponse
HTMLResponse('<h1>Hello</h1>')
JSONResponse({'id': 1}, status=201)
RedirectResponse('/login')
# Set headers/cookies
resp = JSONResponse({'ok': True})
resp.set_header('X-Custom', 'value')
resp.set_cookie('session', token, max_age=86400, http_only=True)HTML DSL
Write HTML as Python function calls. Every HTML5 element is available as a function. Nest them naturally; they stringify to valid HTML.
from pyrith import ui
page = ui.Page(title='My App')
page.body_add(
ui.div(
ui.h1('Welcome', class_='title'),
ui.p('Built with pyrith.'),
ui.ul(
ui.li('No HTML templates'),
ui.li('No Jinja2'),
ui.li('Pure Python'),
),
ui.a('Learn more', href='/about', class_='link'),
)
)
return HTMLResponse(page.to_html())SQLite ORM
ActiveRecord-style ORM backed by SQLite. No migrations in v0.1.0 — table is created on first use.
from pyrith.orm import Model, CharField, IntegerField, BooleanField, DateTimeField
class Article(Model):
title = CharField(max_length=200)
body = CharField(max_length=10000)
views = IntegerField(default=0)
published = BooleanField(default=False)
created = DateTimeField(auto_now=True)
# Create
article = Article.create(title='Hello', body='Content here')
# Query
all_articles = Article.all()
published_ones = Article.filter(published=True)
one = Article.get(id=1)
recent = Article.order_by('-created').limit(10).fetch()
# Update / Delete
article.title = 'Updated'
article.save()
article.delete()Session Auth
from pyrith.auth import login_required
from pyrith.session import SessionStore
app.use(SessionStore(secret='change-me'))
@app.post('/login')
def login(request):
data = request.json()
# validate credentials …
request.session.set('user_id', user.id)
return JSONResponse({'ok': True})
@app.get('/profile')
@login_required
def profile(request):
user_id = request.session.get('user_id')
return JSONResponse({'user_id': user_id})
@app.post('/logout')
def logout(request):
request.session.clear()
return RedirectResponse('/login')Form Validation
from pyrith.forms import Form, CharField, EmailField, IntegerField, BooleanField
class SignupForm(Form):
username = CharField(min_length=3, max_length=30)
email = EmailField()
age = IntegerField(min_value=13, required=False)
agree = BooleanField(required=True)
@app.post('/signup')
def signup(request):
form = SignupForm(data=request.json())
if not form.is_valid():
return JSONResponse({'errors': form.errors}, status=422)
# form.cleaned_data is safe to use
user = User.create(**form.cleaned_data)
return JSONResponse({'id': user.id}, status=201)WebSocket
@app.websocket('/ws/chat/{room}')
async def chat(ws, request):
room = request.params['room']
await ws.accept()
try:
while True:
msg = await ws.receive()
# broadcast to same room (pub/sub in v0.1.0 is manual)
await ws.send(f'Echo: {msg}')
except Exception:
await ws.close()CLI
# Create new project
pyrith new myproject
cd myproject
# Start dev server (auto-reload)
pyrith run --debug
# Show registered routes
pyrith version