High-Performance Web Framework for Python
Powered by Rust and Axum, Rupy delivers blazing-fast performance with the simplicity and elegance of Python. Build modern web applications with ease.
Combining the performance of Rust with the simplicity of Python
Built on Rust and Axum, Rupy delivers exceptional performance that rivals the fastest web frameworks available.
Intuitive Python API that feels natural. Write clean, readable code with decorators and type hints.
Full support for GET, POST, PUT, PATCH, DELETE, HEAD, and OPTIONS with convenient decorators.
Define dynamic path parameters with ease using angle brackets. Perfect for RESTful APIs.
Powerful middleware system for authentication, CORS, logging, rate limiting, and more.
Built-in observability with OpenTelemetry for metrics, tracing, and structured logging.
Handlebars template engine integration for dynamic HTML rendering with type-safe context.
Efficient file upload handling with streaming, MIME type filtering, and size limits.
Serve static files with automatic content-type detection and directory traversal protection.
Forward requests to backend services with full header and body preservation.
Automatic OpenAPI/Swagger documentation generation for your API endpoints.
Built-in cookie handling and Bearer token authentication support for secure applications.
Get started with Rupy in minutes
Add Rupy to your pyproject.toml:
[project]
dependencies = [
"rupy @ git+https://github.com/manoelhc/rupy.git"
]
Then install:
pip install .
For development or customization:
1. Install maturin:
pip install maturin
2. Build and install:
maturin develop
Or for production:
maturin build --release
pip install target/wheels/rupy-*.whl
Build your first Rupy application in seconds
from rupy import Rupy, Request, Response
app = Rupy()
@app.route("/", methods=["GET"])
def index(request: Request) -> Response:
return Response("Hello, World!")
@app.route("/user/<username>", methods=["GET"])
def get_user(request: Request, username: str) -> Response:
return Response(f"User: {username}")
@app.route("/echo", methods=["POST"])
def echo(request: Request) -> Response:
return Response(f"Echo: {request.body}")
if __name__ == "__main__":
app.run(host="127.0.0.1", port=8000)
python app.py
curl http://127.0.0.1:8000/
curl http://127.0.0.1:8000/user/alice
curl -X POST -d '{"name": "test"}' http://127.0.0.1:8000/echo
Everything you need to build powerful web applications
Rupy provides convenient decorators for all HTTP methods:
@app.get("/items")
def list_items(request: Request) -> Response:
return Response("List of items")
@app.post("/items")
def create_item(request: Request) -> Response:
return Response(f"Created: {request.body}")
@app.put("/items/<item_id>")
def update_item(request: Request, item_id: str) -> Response:
return Response(f"Updated item {item_id}")
@app.patch("/items/<item_id>")
def patch_item(request: Request, item_id: str) -> Response:
return Response(f"Patched item {item_id}")
@app.delete("/items/<item_id>")
def delete_item(request: Request, item_id: str) -> Response:
return Response(f"Deleted item {item_id}")
Define dynamic path parameters using angle brackets:
@app.route("/user/<username>", methods=["GET"])
def user_profile(request: Request, username: str) -> Response:
return Response(f"User Profile: {username}")
@app.route("/blog/<author>/<post_id>", methods=["GET"])
def blog_post(request: Request, author: str, post_id: str) -> Response:
return Response(f"Blog post {post_id} by {author}")
@app.route("/products/<category>/<product_id>", methods=["GET"])
def product_details(request: Request, category: str, product_id: str) -> Response:
return Response(f"Product {product_id} in category {category}")
Middlewares execute before route handlers and can inspect, modify, or block requests:
@app.middleware
def logging_middleware(request: Request):
print(f"Processing {request.method} {request.path}")
return request # Continue to next middleware/handler
@app.middleware
def auth_middleware(request: Request):
if request.path.startswith("/admin"):
return Response("Unauthorized", status=401)
return request
Handle cross-origin requests with CORS middleware:
@app.middleware
def cors_middleware(request: Request):
# Handle preflight OPTIONS requests
if request.method == "OPTIONS":
return Response("", status=204)
return request
Protect routes with JWT authentication:
@app.middleware
def jwt_auth_middleware(request: Request):
# Skip auth for public routes
if request.path in ["/", "/login", "/public"]:
return request
# Check for protected routes
if request.path.startswith("/protected"):
# Validate JWT token from headers
return Response(
'{"error": "Unauthorized"}',
status=401
)
return request
Programmatically enable OpenTelemetry:
app = Rupy()
# Enable telemetry with optional configuration
app.enable_telemetry(
endpoint="http://localhost:4317", # OTLP gRPC endpoint
service_name="my-service" # Service name for traces
)
# Check telemetry status
is_enabled = app.is_telemetry_enabled()
# Disable telemetry
app.disable_telemetry()
Configure telemetry using environment variables:
export OTEL_ENABLED=true
export OTEL_SERVICE_NAME=my-service
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
export RUST_LOG=info
python app.py
http.server.requests - Total HTTP requests counterhttp.server.duration - Request duration histogramhttp.method - HTTP method (GET, POST, etc.)http.route - Matched route patternhttp.scheme - Protocol scheme (http/https)Set, get, and delete cookies with full option support:
@app.route("/login", methods=["POST"])
def login(request: Request) -> Response:
resp = Response('{"message": "Login successful"}')
# Set a cookie with options
resp.set_cookie(
"session_id",
"abc123",
max_age=3600, # Expires in 1 hour
http_only=True, # Not accessible via JavaScript
secure=True, # Only sent over HTTPS
same_site="Lax" # CSRF protection
)
return resp
@app.route("/profile", methods=["GET"])
def profile(request: Request) -> Response:
# Read a cookie
session_id = request.get_cookie("session_id")
if not session_id:
return Response("Not logged in", status=401)
return Response(f"Session: {session_id}")
Built-in support for Bearer token authentication:
@app.route("/protected", methods=["GET"])
def protected(request: Request) -> Response:
# Get Bearer token from Authorization header
token = request.auth_token
if not token:
return Response("Unauthorized", status=401)
if token == "valid-token":
return Response("Access granted")
else:
return Response("Invalid token", status=401)
Handle file uploads efficiently with streaming support:
from rupy import Rupy, Request, Response, UploadFile
from typing import List
app = Rupy()
# Basic file upload
@app.upload("/upload")
def handle_upload(request: Request, files: List[UploadFile]) -> Response:
for file in files:
print(f"Uploaded: {file.filename}")
print(f"Size: {file.size} bytes")
print(f"MIME type: {file.content_type}")
print(f"Saved at: {file.path}")
return Response("Files uploaded successfully")
# Upload with MIME type filtering and size limit
@app.upload(
"/upload-images",
accepted_mime_types=["image/*"],
max_size=5*1024*1024 # 5MB limit
)
def upload_images(request: Request, files: List[UploadFile]) -> Response:
return Response(f"Uploaded {len(files)} images")
Serve static files from a directory:
# Serve files from ./public directory at /static path
@app.static("/static", "./public")
def static_files():
pass
# Files are accessible at /static/<filename>
# Example: ./public/style.css -> http://localhost:8000/static/style.css
Features:
Proxy requests to backend services:
# Proxy all /api/* requests to backend service
@app.proxy("/api", "http://backend:8080")
def api_proxy():
pass
# Requests to /api/* are forwarded to http://backend:8080/*
# Example: /api/users -> http://backend:8080/users
Features:
Enable automatic API documentation:
app = Rupy()
# Enable OpenAPI endpoint
app.enable_openapi(
path="/openapi.json",
title="My API",
version="1.0.0",
description="API documentation"
)
@app.route("/users", methods=["GET"])
def list_users(request: Request) -> Response:
"""List all users - this docstring can be used for API docs"""
return Response('[{"id": 1, "name": "Alice"}]')
# Access the OpenAPI spec at http://localhost:8000/openapi.json
# To disable:
app.disable_openapi()
Use Handlebars templates for dynamic HTML rendering:
@app.template("/", template="hello.tpl")
def index(request: Request) -> dict:
"""Render template with context data"""
return {
"title": "Welcome Page",
"greeting": "Hello",
"name": "World",
"message": "Rendered with Handlebars!"
}
@app.template("/user/<username>", template="user.tpl")
def user_profile(request: Request, username: str) -> dict:
"""Dynamic routes work with templates too"""
return {
"username": username,
"user_id": 12345
}
Access request headers and set custom response headers:
@app.route("/", methods=["GET"])
def index(request: Request) -> Response:
# Access headers like a dictionary
user_agent = request.headers.get('user-agent', 'Unknown')
host = request.headers.get('host', 'Unknown')
# Set custom response headers
resp = Response(f"User-Agent: {user_agent}")
resp.set_header('X-Custom-Header', 'MyValue')
resp.set_header('X-Powered-By', 'Rupy')
return resp
Learn from real-world examples
Simple GET and POST routes demonstrating the basics of Rupy.
examples/basic_app.py
Complete RESTful API with all HTTP methods (GET, POST, PUT, PATCH, DELETE).
examples/rest_api.py
Demonstrates dynamic path parameters with multiple examples.
examples/dynamic_routes.py
Comprehensive example showing all supported HTTP methods.
examples/all_methods.py
OpenTelemetry integration for metrics, tracing, and logging.
examples/telemetry_example.py
Cross-Origin Resource Sharing (CORS) middleware example.
examples/cors_middleware.py
JWT-based authentication middleware with protected routes.
examples/jwt_middleware.py
Using convenient method-specific decorators (@app.get, @app.post, etc.).
examples/method_decorators.py
Multiple middlewares working together: logging, rate limiting, and auth.
examples/combined_middlewares.py
Handlebars template engine for dynamic HTML rendering.
examples/template_example.py
Cookie handling and Bearer token authentication examples.
examples/cookies_auth_example.py
Access and set request/response headers with dict-like syntax.
examples/headers_example.py
Serve static files from a directory with auto content-type detection.
examples/static_files_example.py
Forward requests to backend services with full header preservation.
examples/reverse_proxy_example.py
Automatic OpenAPI/Swagger documentation for your API.
examples/openapi_example.py
Efficient file uploads with streaming, MIME filtering, and size limits.
examples/upload_example.py
IP-based geographical access control middleware.
examples/geo_blocking_middleware.py
Rate limiting by IP and User-Agent for API protection.
examples/rate_limiting_middleware.py
Powered by Rust and Axum for unmatched speed
Rust-powered backend delivers exceptional performance
Handle thousands of requests with minimal overhead
Battle-tested Axum framework under the hood
wrk -t12 -c400 -d30s http://127.0.0.1:8000/
Designed to outperform traditional Python frameworks like FastAPI and Flask