Skip to main content

A modern, lightweight Python web framework with type safety, dependency injection, middleware pipelines, and auto-generated API documentation.

Project description

Pure Framework

A lightweight, modern Python web framework built with type safety, dependency injection, and clean architecture principles. Pure Framework provides everything you need to build robust web APIs with minimal overhead and maximum developer experience.

Python Version License Version

✨ Features

  • 🔒 Full Type Safety: Built with Python protocols and generics for comprehensive type checking
  • 💉 Advanced Dependency Injection: Automatic parameter resolution with singleton, transient, and scoped lifecycles
  • 🔄 Pipeline-based Middleware: Chain of responsibility pattern with error handling
  • 🛡️ Guard-based Authorization: Flexible authorization system with clean interfaces
  • 🚀 High-Performance Routing: Regex-compiled routes with parameter extraction
  • 📚 Auto-Generated Documentation: Built-in OpenAPI/Swagger documentation
  • 🎯 Decorator-based Routing: Clean, intuitive route definitions
  • 🏗️ SOLID Principles: Clean separation of concerns and maintainable architecture
  • 🐍 Pure Python: No external dependencies, works with standard library only

🚀 Quick Start

Installation

pip install pure-framework

Basic Example

from pure_framework import PureFramework, get, post
from pure_framework.framework_types import IRequest, IResponse

app = PureFramework()

@get('/hello')
def hello_world(req: IRequest, res: IResponse) -> None:
    res.json({'message': 'Hello, World!'})

@get('/users/:id')
def get_user(req: IRequest, res: IResponse, id: int) -> None:
    # Automatic parameter injection and type conversion
    res.json({'user_id': id, 'name': f'User {id}'})

@post('/users')
def create_user(req: IRequest, res: IResponse) -> None:
    user_data = req.json
    res.json({'created': user_data}, status_code=201)

if __name__ == '__main__':
    app.run()  # Starts server at http://127.0.0.1:8000

Visit http://127.0.0.1:8000/docs to see the auto-generated API documentation!

🏛️ Architecture Overview

Pure Framework is built around several core concepts:

1. Application (PureFramework)

The main application class that orchestrates all components:

from pure_framework import PureFramework
from pure_framework.framework_types import ApplicationConfig

# Basic setup
app = PureFramework()

# Advanced configuration
config = ApplicationConfig(
    host="0.0.0.0",
    port=8080,
    enable_docs=True,
    docs_path="/api-docs"
)
app = PureFramework(config=config)

2. Routing System

High-performance routing with parameter extraction:

from pure_framework import get, post, put, delete, route
from pure_framework.framework_types import HTTPMethod

# Simple routes
@get('/health')
def health_check(req, res):
    res.json({'status': 'healthy'})

# Multiple methods
@route('/api/data', methods=[HTTPMethod.GET, HTTPMethod.POST])
def handle_data(req, res):
    if req.method == HTTPMethod.GET:
        res.json({'data': 'value'})
    else:
        res.json({'created': req.json})

# Parameter routes with type conversion
@get('/posts/:id/comments/:comment_id')
def get_comment(req, res, id: int, comment_id: int):
    res.json({'post_id': id, 'comment_id': comment_id})

3. Controller Classes

Organize related routes into controller classes:

from pure_framework import controller, get, post
from pure_framework.framework_types import IRequest, IResponse

@controller('/api/users')
class UserController:
    
    @get('/')
    def list_users(self, req: IRequest, res: IResponse):
        res.json({'users': []})
    
    @get('/:id')
    def get_user(self, req: IRequest, res: IResponse, id: int):
        res.json({'user_id': id})
    
    @post('/')
    def create_user(self, req: IRequest, res: IResponse):
        user_data = req.json
        res.json({'created': user_data})

4. Dependency Injection

Type-safe dependency injection with automatic resolution:

from pure_framework import PureFramework, get, inject
from pure_framework.dependency_injection import DependencyContainer, LifecycleType

# Define services
class DatabaseService:
    def get_user(self, user_id: int):
        return {'id': user_id, 'name': f'User {user_id}'}

class UserService:
    def __init__(self, db: DatabaseService):
        self.db = db
    
    def find_user(self, user_id: int):
        return self.db.get_user(user_id)

# Configure container
app = PureFramework()
app.configure_container(lambda container: (
    container.register_type(DatabaseService, DatabaseService, LifecycleType.SINGLETON)
    .register_type(UserService, UserService, LifecycleType.SINGLETON)
))

# Use in routes with automatic injection
@get('/users/:id')
def get_user(req, res, id: int, user_service: UserService = inject()):
    user = user_service.find_user(id)
    res.json(user)

5. Middleware System

Pipeline-based middleware with error handling:

from pure_framework import PureFramework
from pure_framework.middleware import BaseMiddleware
from pure_framework.framework_types import IRequest, IResponse

class LoggingMiddleware(BaseMiddleware):
    def process(self, request: IRequest, response: IResponse) -> None:
        print(f"{request.method} {request.path}")

class AuthMiddleware(BaseMiddleware):
    def process(self, request: IRequest, response: IResponse) -> None:
        auth_header = request.get_header('authorization')
        if not auth_header:
            response.status_code = 401
            response.json({'error': 'Unauthorized'})
            return

# Global middleware
app = PureFramework()
app.add_middleware(LoggingMiddleware())

# Route-specific middleware
@get('/protected', middlewares=[AuthMiddleware()])
def protected_route(req, res):
    res.json({'message': 'Access granted'})

6. Guards for Authorization

Clean authorization with guard classes:

from pure_framework.middleware import BaseGuard
from pure_framework.framework_types import IRequest

class AdminGuard(BaseGuard):
    def can_activate(self, request: IRequest) -> bool:
        user_role = request.get_header('user-role')
        return user_role == 'admin'

@get('/admin/users', guards=[AdminGuard()])
def admin_users(req, res):
    res.json({'admin_data': 'sensitive'})

📖 API Reference

Request Object (IRequest)

from pure_framework.framework_types import IRequest

def my_handler(req: IRequest, res):
    # Path and method
    path = req.path          # "/api/users/123"
    method = req.method      # HTTPMethod.GET
    
    # Headers (case-insensitive)
    auth = req.get_header('authorization')
    content_type = req.headers.get('content-type')
    
    # Query parameters
    page = req.get_query('page', '1')        # Single value
    tags = req.query.get('tags', [])         # List of values
    
    # Path parameters (from route pattern)
    user_id = req.params.get('id')           # From /users/:id
    
    # Request body
    raw_body = req.body                      # Raw string
    json_data = req.json                     # Parsed JSON

Response Object (IResponse)

from pure_framework.framework_types import IResponse

def my_handler(req, res: IResponse):
    # Set status code
    res.status_code = 201
    
    # Set headers
    res.set_header('X-Custom', 'value')
    
    # Send responses
    res.json({'key': 'value'})                    # JSON response
    res.html('<h1>Hello</h1>')                    # HTML response
    res.text('Plain text')                        # Text response
    res.send(b'Binary data')                      # Raw bytes
    
    # With custom status
    res.json({'error': 'Not found'}, status_code=404)

Route Decorators

from pure_framework import route, get, post, put, delete, patch
from pure_framework.framework_types import HTTPMethod

# Basic decorators
@get('/path')           # GET only
@post('/path')          # POST only
@put('/path')           # PUT only
@delete('/path')        # DELETE only
@patch('/path')         # PATCH only

# Multiple methods
@route('/path', methods=[HTTPMethod.GET, HTTPMethod.POST])

# With middleware and guards
@get('/path', 
     middlewares=[LoggingMiddleware()],
     guards=[AuthGuard()],
     name='custom_name',
     description='Route description')

Dependency Injection

from pure_framework.dependency_injection import (
    DependencyContainer, LifecycleType, inject
)

# Container setup
container = DependencyContainer()

# Register types
container.register_type(IService, ServiceImpl, LifecycleType.SINGLETON)
container.register_type(IRepo, RepoImpl, LifecycleType.TRANSIENT)

# Register instances
container.register_instance(IConfig, config_instance)

# Register factories
container.register_factory(IService, lambda: create_service())

# Use in route handlers
@get('/users')
def get_users(req, res, service: IService = inject()):
    users = service.get_all_users()
    res.json(users)

🔧 Configuration

Application Configuration

from pure_framework import PureFramework
from pure_framework.framework_types import ApplicationConfig

config = ApplicationConfig(
    host="0.0.0.0",              # Bind host
    port=8080,                   # Bind port  
    enable_docs=True,            # Enable Swagger docs
    docs_path="/docs",           # Docs endpoint path
    log_level="INFO"             # Logging level
)

app = PureFramework(config=config)

Error Handling

from pure_framework import PureFramework

app = PureFramework()

# Global error handler
def handle_validation_error(error: ValueError, req, res):
    res.json({'error': str(error)}, status_code=400)
    return True  # Mark as handled

app.add_error_handler(ValueError, handle_validation_error)

📊 Examples

Complete REST API

from pure_framework import PureFramework, controller, get, post, put, delete
from pure_framework.framework_types import IRequest, IResponse
from pure_framework.dependency_injection import inject, LifecycleType

# Data models
class User:
    def __init__(self, id: int, name: str, email: str):
        self.id = id
        self.name = name
        self.email = email
    
    def to_dict(self):
        return {'id': self.id, 'name': self.name, 'email': self.email}

# Services
class UserService:
    def __init__(self):
        self.users = {}
        self.next_id = 1
    
    def create_user(self, name: str, email: str) -> User:
        user = User(self.next_id, name, email)
        self.users[self.next_id] = user
        self.next_id += 1
        return user
    
    def get_user(self, user_id: int) -> User:
        return self.users.get(user_id)
    
    def get_all_users(self) -> list[User]:
        return list(self.users.values())
    
    def update_user(self, user_id: int, name: str = None, email: str = None) -> User:
        user = self.users.get(user_id)
        if user:
            if name: user.name = name
            if email: user.email = email
        return user
    
    def delete_user(self, user_id: int) -> bool:
        return self.users.pop(user_id, None) is not None

# Controllers
@controller('/api/users')
class UserController:
    
    @get('/')
    def list_users(self, req: IRequest, res: IResponse, 
                  user_service: UserService = inject()):
        users = user_service.get_all_users()
        res.json([user.to_dict() for user in users])
    
    @get('/:id')
    def get_user(self, req: IRequest, res: IResponse, id: int,
                user_service: UserService = inject()):
        user = user_service.get_user(id)
        if user:
            res.json(user.to_dict())
        else:
            res.json({'error': 'User not found'}, status_code=404)
    
    @post('/')
    def create_user(self, req: IRequest, res: IResponse,
                   user_service: UserService = inject()):
        data = req.json
        user = user_service.create_user(data['name'], data['email'])
        res.json(user.to_dict(), status_code=201)
    
    @put('/:id')
    def update_user(self, req: IRequest, res: IResponse, id: int,
                   user_service: UserService = inject()):
        data = req.json
        user = user_service.update_user(id, data.get('name'), data.get('email'))
        if user:
            res.json(user.to_dict())
        else:
            res.json({'error': 'User not found'}, status_code=404)
    
    @delete('/:id')
    def delete_user(self, req: IRequest, res: IResponse, id: int,
                   user_service: UserService = inject()):
        if user_service.delete_user(id):
            res.json({'message': 'User deleted'})
        else:
            res.json({'error': 'User not found'}, status_code=404)

# Application setup
app = PureFramework()

# Configure dependencies
app.configure_container(lambda container:
    container.register_type(UserService, UserService, LifecycleType.SINGLETON)
)

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

🧪 Testing

Pure Framework is designed to be easily testable:

import unittest
from pure_framework import PureFramework, get
from pure_framework.framework_types import IRequest, IResponse

class TestAPI(unittest.TestCase):
    def setUp(self):
        self.app = PureFramework()
        
        @get('/test')
        def test_endpoint(req: IRequest, res: IResponse):
            res.json({'test': True})
    
    def test_endpoint_response(self):
        # Test your endpoints using the application instance
        # Note: Full testing utilities coming in future versions
        pass

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

Development Setup

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

# Create virtual environment
python -m venv .venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate

# Install in development mode
pip install -e .

# Run tests
python -m pytest tests/

# Build package
python -m build

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🔮 Roadmap

  • Async/await support
  • WebSocket support
  • Database ORM integration
  • Template engine integration
  • Built-in testing utilities
  • CLI tools for project scaffolding
  • Performance optimizations
  • Plugin system

📞 Support


Made with ❤️ by Hasan Ragab

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pure_framework-0.0.3.5.tar.gz (34.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pure_framework-0.0.3.5-py3-none-any.whl (33.4 kB view details)

Uploaded Python 3

File details

Details for the file pure_framework-0.0.3.5.tar.gz.

File metadata

  • Download URL: pure_framework-0.0.3.5.tar.gz
  • Upload date:
  • Size: 34.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for pure_framework-0.0.3.5.tar.gz
Algorithm Hash digest
SHA256 c4aa665510c3b1995e51688aeab5571032b9b244001c2af9cdb38a1926935aa1
MD5 767c1c1ebfe324b540f12268b658db44
BLAKE2b-256 f9c147c74a6769fc225528024a855292ac79ee46a541a6b88b45a25d5cea16b2

See more details on using hashes here.

File details

Details for the file pure_framework-0.0.3.5-py3-none-any.whl.

File metadata

File hashes

Hashes for pure_framework-0.0.3.5-py3-none-any.whl
Algorithm Hash digest
SHA256 a54656059ede3901e52ee76a8e831930221db78bccef781eebc3d79ff09aabbe
MD5 cc95af1581d3e5b0ca2318a87cacff45
BLAKE2b-256 329616da84ce29e6cf40e17cd556ee004713a2420e03cdb7f5c78bedb99d2fd5

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page