Rupy

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.

Why Choose Rupy?

Combining the performance of Rust with the simplicity of Python

Lightning Fast

Built on Rust and Axum, Rupy delivers exceptional performance that rivals the fastest web frameworks available.

Simple API

Intuitive Python API that feels natural. Write clean, readable code with decorators and type hints.

All HTTP Methods

Full support for GET, POST, PUT, PATCH, DELETE, HEAD, and OPTIONS with convenient decorators.

Dynamic Routes

Define dynamic path parameters with ease using angle brackets. Perfect for RESTful APIs.

Middleware Support

Powerful middleware system for authentication, CORS, logging, rate limiting, and more.

OpenTelemetry

Built-in observability with OpenTelemetry for metrics, tracing, and structured logging.

Template Rendering

Handlebars template engine integration for dynamic HTML rendering with type-safe context.

File Upload

Efficient file upload handling with streaming, MIME type filtering, and size limits.

Static Files

Serve static files with automatic content-type detection and directory traversal protection.

Reverse Proxy

Forward requests to backend services with full header and body preservation.

OpenAPI Support

Automatic OpenAPI/Swagger documentation generation for your API endpoints.

Cookies & Auth

Built-in cookie handling and Bearer token authentication support for secure applications.

Installation

Get started with Rupy in minutes

Add as Dependency

Add Rupy to your pyproject.toml:

[project]
dependencies = [
    "rupy @ git+https://github.com/manoelhc/rupy.git"
]

Then install:

pip install .

Build from Source

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

Quick Start

Build your first Rupy application in seconds

app.py

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)

Run your application:

python app.py

Test with curl:

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

Documentation

Everything you need to build powerful web applications

HTTP Methods

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}")

Dynamic Routes

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}")

Middleware

Basic Middleware

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

CORS Middleware

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

JWT Authentication

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

OpenTelemetry Support

Enable Telemetry

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()

Environment Variables

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

Collected Metrics

Automatic Metrics:
  • http.server.requests - Total HTTP requests counter
  • http.server.duration - Request duration histogram
Tracing Attributes:
  • http.method - HTTP method (GET, POST, etc.)
  • http.route - Matched route pattern
  • http.scheme - Protocol scheme (http/https)

Cookies and Authentication

Cookie Handling

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}")

Bearer Token Authentication

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)

File Upload

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")

Static File Serving

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:

  • Automatic content-type detection
  • Directory traversal protection
  • Support for all common file types

Reverse Proxy

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:

  • Forwards all HTTP methods (GET, POST, PUT, PATCH, DELETE)
  • Preserves request headers and body
  • Returns response headers and body from backend

OpenAPI/Swagger Support

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()

Template Rendering

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
    }

Response Headers

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

Examples

Learn from real-world examples

1

Basic App

Simple GET and POST routes demonstrating the basics of Rupy.

examples/basic_app.py
2

REST API

Complete RESTful API with all HTTP methods (GET, POST, PUT, PATCH, DELETE).

examples/rest_api.py
3

Dynamic Routes

Demonstrates dynamic path parameters with multiple examples.

examples/dynamic_routes.py
4

All HTTP Methods

Comprehensive example showing all supported HTTP methods.

examples/all_methods.py
5

Telemetry

OpenTelemetry integration for metrics, tracing, and logging.

examples/telemetry_example.py
6

CORS Middleware

Cross-Origin Resource Sharing (CORS) middleware example.

examples/cors_middleware.py
7

JWT Authentication

JWT-based authentication middleware with protected routes.

examples/jwt_middleware.py
8

Method Decorators

Using convenient method-specific decorators (@app.get, @app.post, etc.).

examples/method_decorators.py
9

Combined Middlewares

Multiple middlewares working together: logging, rate limiting, and auth.

examples/combined_middlewares.py
10

Template Rendering

Handlebars template engine for dynamic HTML rendering.

examples/template_example.py
11

Cookies & Auth

Cookie handling and Bearer token authentication examples.

examples/cookies_auth_example.py
12

Response Headers

Access and set request/response headers with dict-like syntax.

examples/headers_example.py
13

Static Files

Serve static files from a directory with auto content-type detection.

examples/static_files_example.py
14

Reverse Proxy

Forward requests to backend services with full header preservation.

examples/reverse_proxy_example.py
15

OpenAPI

Automatic OpenAPI/Swagger documentation for your API.

examples/openapi_example.py
16

File Upload

Efficient file uploads with streaming, MIME filtering, and size limits.

examples/upload_example.py
17

Geo Blocking

IP-based geographical access control middleware.

examples/geo_blocking_middleware.py
18

Rate Limiting

Rate limiting by IP and User-Agent for API protection.

examples/rate_limiting_middleware.py

Built for Performance

Powered by Rust and Axum for unmatched speed

🚀

Lightning Fast

Rust-powered backend delivers exceptional performance

âš¡

Low Latency

Handle thousands of requests with minimal overhead

💪

Production Ready

Battle-tested Axum framework under the hood

Benchmark Example

wrk -t12 -c400 -d30s http://127.0.0.1:8000/

Designed to outperform traditional Python frameworks like FastAPI and Flask