Skip to main content
PyFrame Logo

PyFrame - Full-Stack Python Web Framework

A modern, reactive web framework that allows developers to write both frontend and backend entirely in Python, with automatic compilation to efficient JavaScript for the browser.

Python 3.8+ License: MIT

🚀 What Makes PyFrame Special

PyFrame revolutionizes web development by eliminating the traditional frontend/backend divide. Write your entire application in Python - from reactive UI components to database models - and PyFrame handles the rest.

from pyframe import PyFrameApp, Component, Model, Field, FieldType

# Define your data model
class User(Model):
    name: str = Field(FieldType.STRING, max_length=100)
    email: str = Field(FieldType.EMAIL, unique=True)
    # ↳ Automatically generates database tables, migrations, and REST APIs!

# Create reactive UI components in Python
class UserProfile(Component):
    def render(self):
        user = self.props.get("user")
        return f"""
        <div class="profile">
            <h1>Welcome, {user.name}!</h1>
            <p>Email: {user.email}</p>
        </div>
        """
        # ↳ Automatically compiles to JavaScript for the browser!

# Set up your app
app = PyFrameApp()

@app.component_route("/profile/<user_id>")
class ProfilePage(UserProfile):
    pass

app.run()  # 🎉 Full-stack app running!

✨ Key Features

🐍 Unified Python Development

  • Write frontend UI components entirely in Python
  • Automatic compilation to optimized JavaScript
  • Reactive state management with Python syntax
  • No JavaScript, TypeScript, or build tools required

🔄 Real-Time by Default

  • Built-in WebSocket and Server-Sent Events support
  • Automatic data synchronization between server and clients
  • Optimistic UI updates with conflict resolution
  • Live component updates without page refreshes

📊 Zero-Boilerplate Data Layer

  • Define models with Python classes and type hints
  • Automatic database schema generation and migrations
  • Auto-generated REST and GraphQL APIs
  • Built-in validation and form generation

📱 Context-Aware Adaptivity

  • Automatic device type and network speed detection
  • Adaptive content delivery and optimization
  • User preference detection (dark mode, reduced motion)
  • Progressive enhancement for accessibility

🔌 Plugin-Based Architecture

  • Lightweight core (~10-20KB) with optional plugins
  • Built-in plugins for auth, caching, analytics
  • Easy custom plugin development
  • Hook system for extending functionality

⚡ Modern Development Experience

  • Hot module replacement with file watching
  • Zero-config development server
  • Detailed error reporting and debugging
  • AI-assisted code suggestions (optional)

🏗️ Architecture Overview

PyFrame uses a layered architecture where each component can be used independently:

┌─────────────────────────────────────────────────────────────┐
│  🎨 Frontend (Python → JavaScript)                         │
│  • Reactive components written in Python                   │
│  • Automatic compilation to efficient JavaScript           │
│  • Client-side hydration and state management              │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│  ⚙️  Core Runtime                                          │
│  • Component lifecycle management                          │
│  • Routing and navigation                                  │
│  • Server-side rendering (SSR)                            │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│  🗄️  Data Layer                                           │
│  • Model definitions with automatic migrations             │
│  • Auto-generated REST/GraphQL APIs                       │
│  • Built-in validation and relationships                   │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│  🔌 Plugin System                                          │
│  • Authentication and authorization                        │
│  • Caching and performance optimization                    │
│  • Analytics and monitoring                               │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│  🌐 Server & Deployment                                    │
│  • Context-aware adaptive rendering                        │
│  • Multiple deployment targets (serverless, edge, VPS)     │
│  • Development server with hot reload                      │
└─────────────────────────────────────────────────────────────┘

🚀 Quick Start

Installation

pip install pyframe-web

Using the CLI (Recommended)

PyFrame includes a command-line tool to help you get started quickly:

# Create a new project
pyframe-web create my-awesome-app
cd my-awesome-app

# Install dependencies
pip install -r requirements.txt

# Run the development server
python main.py
# or
pyframe-web run

Your app will be available at http://localhost:3000 with hot reload enabled!

Create Your First App Manually

# app.py
from pyframe import PyFrameApp, Component, StatefulComponent

class Counter(StatefulComponent):
    def __init__(self, props=None, children=None):
        super().__init__(props, children)
        self.set_state("count", 0)
    
    def increment(self):
        count = self.get_state("count", 0)
        self.set_state("count", count + 1)
    
    def render(self):
        count = self.get_state("count", 0)
        return f"""
        <div>
            <h1>Count: {count}</h1>
            <button onclick="this.component.increment()">
                Click me!
            </button>
        </div>
        """

app = PyFrameApp()

@app.component_route("/")
class HomePage(Counter):
    pass

if __name__ == "__main__":
    app.run()

Run Your App

python app.py

Visit http://localhost:3000 to see your reactive counter in action! 🎉

📖 Learn by Example

Reactive Components

class TodoList(StatefulComponent):
    def __init__(self, props=None, children=None):
        super().__init__(props, children)
        self.set_state("todos", [])
        self.set_state("input_value", "")
    
    def add_todo(self):
        input_value = self.get_state("input_value", "")
        if input_value.strip():
            todos = self.get_state("todos", [])
            todos.append({"id": len(todos), "text": input_value, "done": False})
            self.set_state("todos", todos)
            self.set_state("input_value", "")
    
    def toggle_todo(self, todo_id):
        todos = self.get_state("todos", [])
        for todo in todos:
            if todo["id"] == todo_id:
                todo["done"] = not todo["done"]
        self.set_state("todos", todos)
    
    def render(self):
        todos = self.get_state("todos", [])
        input_value = self.get_state("input_value", "")
        
        todo_items = ""
        for todo in todos:
            checked = "checked" if todo["done"] else ""
            todo_items += f"""
            <li>
                <input type="checkbox" {checked} 
                       onchange="this.component.toggle_todo({todo['id']})">
                <span class="{'done' if todo['done'] else ''}">{todo['text']}</span>
            </li>
            """
        
        return f"""
        <div class="todo-app">
            <h1>Todo List</h1>
            <div class="add-todo">
                <input type="text" value="{input_value}" 
                       placeholder="Add a todo..."
                       onchange="this.component.set_state('input_value', this.value)">
                <button onclick="this.component.add_todo()">Add</button>
            </div>
            <ul class="todo-list">{todo_items}</ul>
        </div>
        """

Data Models with Auto-Generated APIs

from pyframe.data.models import Model, Field, FieldType
from datetime import datetime

class User(Model):
    username: str = Field(FieldType.STRING, unique=True, max_length=50)
    email: str = Field(FieldType.EMAIL, unique=True)
    first_name: str = Field(FieldType.STRING, max_length=50, required=False)
    last_name: str = Field(FieldType.STRING, max_length=50, required=False)
    is_active: bool = Field(FieldType.BOOLEAN, default=True)
    created_at: datetime = Field(FieldType.DATETIME, auto_now_add=True)

class Post(Model):
    title: str = Field(FieldType.STRING, max_length=200)
    content: str = Field(FieldType.TEXT)
    author_id: str = Field(FieldType.UUID, foreign_key="User")
    published: bool = Field(FieldType.BOOLEAN, default=False)
    tags: list = Field(FieldType.JSON, default=list)

# Automatically generates:
# GET /api/users - List users
# POST /api/users - Create user  
# GET /api/users/:id - Get user
# PUT /api/users/:id - Update user
# DELETE /api/users/:id - Delete user
# (Same for posts)

# Use in Python:
user = User.create(username="john", email="john@example.com")
post = Post.create(title="Hello World", content="...", author_id=user.id)

Real-Time Live Updates

class ChatRoom(StatefulComponent):
    def __init__(self, props=None, children=None):
        super().__init__(props, children)
        self.set_state("messages", [])
        self.set_state("input_value", "")
        
        # Subscribe to real-time updates
        room_id = props.get("room_id", "general")
        live_sync_manager.subscribe_to_channel(f"chat:{room_id}")
    
    def send_message(self):
        input_value = self.get_state("input_value", "")
        if input_value.strip():
            message = ChatMessage.create(
                content=input_value,
                room_id=self.props.get("room_id"),
                user_id=self.props.get("user_id")
            )
            # ↳ Automatically broadcasts to all connected clients!
            self.set_state("input_value", "")
    
    def render(self):
        messages = self.get_state("messages", [])
        input_value = self.get_state("input_value", "")
        
        message_list = ""
        for msg in messages:
            message_list += f"""
            <div class="message">
                <strong>{msg['username']}:</strong> {msg['content']}
            </div>
            """
        
        return f"""
        <div class="chat-room">
            <div class="messages">{message_list}</div>
            <div class="input-area">
                <input type="text" value="{input_value}"
                       onchange="this.component.set_state('input_value', this.value)"
                       onkeypress="if(event.key==='Enter') this.component.send_message()">
                <button onclick="this.component.send_message()">Send</button>
            </div>
        </div>
        """

Plugins and Authentication

from pyframe.plugins.auth_plugin import AuthPlugin, require_auth

# Configure authentication
app.register_plugin(AuthPlugin({
    "jwt_secret": "your-secret-key",
    "password_min_length": 8
}))

# Protected routes
@app.component_route("/dashboard")
@require_auth
class Dashboard(Component):
    def render(self):
        user = self.props.get("user")
        return f"""
        <div class="dashboard">
            <h1>Welcome, {user.username}!</h1>
            <p>This is your private dashboard.</p>
        </div>
        """

# Login component
class LoginForm(StatefulComponent):
    def __init__(self, props=None, children=None):
        super().__init__(props, children)
        self.set_state("username", "")
        self.set_state("password", "")
        self.set_state("loading", False)
    
    async def handle_login(self):
        self.set_state("loading", True)
        
        username = self.get_state("username", "")
        password = self.get_state("password", "")
        
        # Make API call to login endpoint
        response = await fetch("/auth/login", {
            "method": "POST",
            "headers": {"Content-Type": "application/json"},
            "body": json.dumps({"username": username, "password": password})
        })
        
        if response.ok:
            # Redirect to dashboard
            window.location.href = "/dashboard"
        else:
            self.set_state("loading", False)
            # Show error message
    
    def render(self):
        username = self.get_state("username", "")
        password = self.get_state("password", "")
        loading = self.get_state("loading", False)
        
        return f"""
        <form class="login-form" onsubmit="this.component.handle_login(); return false;">
            <h2>Login</h2>
            <input type="text" placeholder="Username" value="{username}"
                   onchange="this.component.set_state('username', this.value)">
            <input type="password" placeholder="Password" value="{password}"
                   onchange="this.component.set_state('password', this.value)">
            <button type="submit" {'disabled' if loading else ''}>
                {'Logging in...' if loading else 'Login'}
            </button>
        </form>
        """

🌟 Live Demo

Explore a complete blog application built with PyFrame:

git clone https://github.com/pyframe/pyframe.git
cd pyframe/examples/blog_app
pip install -r requirements.txt
python main.py

Visit http://localhost:3000 to see:

  • Reactive Python components
  • Real-time comments
  • Auto-generated APIs
  • Adaptive rendering
  • Authentication system
  • Hot reload in action

📚 Documentation

Core Concepts

Advanced Topics

API Reference

🔧 Development

Requirements

  • Python 3.8+
  • Modern web browser with JavaScript enabled

Dependencies

  • asyncio - Asynchronous programming
  • websockets - Real-time communication
  • watchdog - File system monitoring
  • PyJWT - JSON Web Tokens

Development Setup

# Clone the repository
git clone https://github.com/pyframe/pyframe.git
cd pyframe

# Install dependencies
pip install -r requirements.txt

# Run tests
python -m pytest

# Run the example app
cd examples/blog_app
python main.py

🚢 Deployment

PyFrame applications can be deployed anywhere Python runs:

Traditional Servers

from pyframe.deployment.wsgi import create_wsgi_app

# WSGI for Apache, Gunicorn, uWSGI
application = create_wsgi_app(app)

ASGI Servers

from pyframe.deployment.asgi import create_asgi_app

# ASGI for Uvicorn, Daphne, Hypercorn
application = create_asgi_app(app)

Serverless Functions

# AWS Lambda, Vercel, Netlify Functions
def lambda_handler(event, context):
    return app.handle_serverless_request(event, context)

Edge Computing

# Cloudflare Workers, Deno Deploy
app.configure_for_edge_deployment()

🤝 Contributing

We welcome contributions! Please see our Contributing Guide for details.

Development Philosophy

  • Python-First: Everything should be writable in Python
  • Zero Config: Sensible defaults with optional customization
  • Performance: Fast development, fast runtime
  • Accessibility: Progressive enhancement and WCAG compliance
  • Developer Experience: Great error messages and debugging tools

📄 License

MIT License - see the LICENSE file for details.

🙏 Acknowledgments

PyFrame is inspired by:

  • React - Component-based architecture
  • Django - Batteries-included philosophy
  • FastAPI - Modern Python web framework design
  • Next.js - Full-stack development experience
  • Phoenix LiveView - Real-time server-rendered UI

💬 Community


Ready to build the future of web development with Python? 🐍✨

pip install pyframe-web

Get Started | Examples | API Docs

Metadata

Release files for pyframe-web 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pyframe-web 0.1.0
File Size Uploaded
pyframe_web-0.1.0.tar.gz 122.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pyframe-web 0.1.0
File Interpreter ABI Platform
pyframe_web-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 220.6 kB

Release files / pyframe_web-0.1.0.tar.gz

Download URL pyframe_web-0.1.0.tar.gz
Size 122.1 kB
Tags Source
SHA-256 checksum
How to use checksums
55e884f1fd49b7c493938d17b12ef10fe7dd2e43647e9f0e2568ed124afc7f21
BLAKE2b-256 checksum
How to use checksums
14bc1683bbd4a445f3ee748df88a96c81b459fb84a313ed7c2e93706e7249fe2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.10.11

Release files / pyframe_web-0.1.0-py3-none-any.whl

Download URL pyframe_web-0.1.0-py3-none-any.whl
Size 98.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
fda2dc83809479ddd93f0bd264aa8513c4ae27205d6831563e1e36f52dcea993
BLAKE2b-256 checksum
How to use checksums
d2a6a7e8282a589ab6707f8dd3c3f6b91be940af4563cce62132187816b717fb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.10.11

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page