Skip to content

Pyweber Class Documentation

The Pyweber class is the main application class for the PyWeber web framework. It inherits from multiple manager classes to provide comprehensive web application functionality including routing, middleware, cookies, and error handling.

Dependencies

import inspect
import json
import os
import re
import webbrowser
from typing import Union, Callable, Any
from dataclasses import dataclass
from pyweber.core.element import Element
from pyweber.core.template import Template
from pyweber.models.request import Request
from pyweber.models.response import Response
from pyweber.utils.types import ContentTypes, StaticFilePath, HTTPStatusCode
from pyweber.utils.loads import LoadStaticFiles
from pyweber.core.window import window
from pyweber.models.middleware import MiddlewareManager
from pyweber.models.error_pages import ErrorPages
from pyweber.models.cookies import CookieManager
from pyweber.models.routes import Route, RedirectRoute, RouteManager
from pyweber.models.openapi import OpenApiProcessor
from pyweber.utils.utils import PrintLine

Pyweber Class

Class Inheritance

class Pyweber(
    ErrorPages,
    CookieManager,
    MiddlewareManager,
    RouteManager
):

The Pyweber class inherits from multiple manager classes, providing: - ErrorPages: Custom error page handling - CookieManager: Cookie management functionality - MiddlewareManager: Request/response middleware processing - RouteManager: Route registration and resolution

Constructor

def __init__(self, **kwargs):

Parameters: - update_handler: Optional callback function for file change handling - **kwargs: Additional configuration options

Initialization: - Initializes all parent manager classes - Adds framework-specific routes (admin, docs, static files) - Sets up internal state tracking - Configures request handling

Properties

request (Read-only Property)

Returns the current request object being processed.

Returns: Current Request instance or None

Core Methods

async get_response(request: Request) -> Response

Main method for processing HTTP requests and generating responses.

Parameters: - request: Request instance to process

Returns: Response instance

Process Flow: 1. Validates request type 2. Handles OpenAPI route registration 3. Processes before-request middleware 4. Gets template for the route 5. Converts template to bytes 6. Processes after-request middleware 7. Returns final response

Raises: - TypeError: If request is not a Request instance

template_to_bytes(template, content_type, title, process_response) -> ContentResult

Converts various template types to bytes for HTTP response.

Parameters: - template: Template content (Template, Element, dict, list, str, bytes) - content_type: ContentTypes enum value - title: Page title for HTML responses - process_response: Whether to wrap content in full template

Returns: ContentResult with processed content

Supported Template Types: - Template objects: Processed with title and built to HTML - Element objects: Converted to HTML, optionally wrapped in template - Dict/List/Set: Serialized to JSON - Bytes: Used directly with specified content type - Strings: Encoded to bytes, optionally wrapped in template

async get_template(route: str, method: str = 'GET') -> TemplateResult

Resolves and processes a route to get the template result.

Parameters: - route: URL path to resolve - method: HTTP method (default: 'GET')

Returns: TemplateResult with processed template

Process Flow: 1. Resolves route path and extracts parameters 2. Checks if route exists and method is allowed 3. Handles redirects if configured 4. Processes route middleware 5. Handles static files if applicable 6. Returns 404 for non-existent routes

get_content_type(route: str) -> ContentTypes

Determines content type based on file extension in route.

Parameters: - route: URL path to analyze

Returns: ContentTypes enum value

Logic: - Extracts file extension from route - Maps extension to ContentTypes - Returns ContentTypes.unknown for unrecognized extensions - Defaults to ContentTypes.html for routes without extensions

Template Processing Methods

_process_template_object(template: Template, title: str, content_type: ContentTypes) -> ContentResult

Processes Template objects into ContentResult.

_process_element_object(element: Element, title: str, content_type: ContentTypes, process_template: bool) -> ContentResult

Processes Element objects into ContentResult.

_process_json_object(template: Union[dict, list, set]) -> ContentResult

Processes JSON-serializable objects into ContentResult.

_process_byte_object(data: bytes, content_type: ContentTypes) -> ContentResult

Processes byte data into ContentResult.

_process_string_object(data: str, title: str, content_type: ContentTypes, process_response: bool) -> ContentResult

Processes string data into ContentResult.

Advanced Processing Methods

async _process_templates(state_result: StateResult) -> TemplateResult

Processes templates through the complete pipeline including callable resolution and redirects.

Parameters: - state_result: Current processing state

Returns: Final TemplateResult

Features: - Handles callable templates (functions, lambdas) - Processes redirect routes - Manages recursion detection - Supports async and sync callables - Prepares callback arguments from request body and route parameters

async _process_redirect_route(state: StateResult, redirect_route: RedirectRoute, redirect_path: str, **kwargs) -> StateResult

Processes redirect routes with middleware support.

async process_route_middleware(resp: str, middlewares: list[Callable], status_code: int)

Processes route-specific middleware.

File Handling Methods

is_file_requested(route: str) -> bool

Checks if the route requests a file based on file extension pattern.

is_static_file(route: str) -> bool

Checks if the requested file exists in the filesystem.

normaize_path(route: str) -> str

Normalizes route path for filesystem access.

load_static_files(path: os.path) -> bytes

Loads static file content from filesystem.

Framework Integration Methods

async clone_template(route: str) -> Template

Creates a clone of a template for a specific route.

update(changed_file: str = None)

Triggers update handler for file change notifications.

launch_url(url: str, new_page: bool = False)

Opens URL in web browser.

to_url(url: str, new_page: bool = False, message: str = None) -> Element

Creates a redirect element with optional message.

run(target: Callable = None, **kwargs)

Runs the application with specified configuration.

async __call__(scope, receive, send)

ASGI application interface for deployment.

Internal Methods

_check_recursion(route: str)

Prevents infinite recursion in route processing.

__add_framework_routes()

Adds built-in framework routes (admin, docs, static files).

async __add_openapi_route()

Dynamically adds OpenAPI documentation route.

__get_routes() -> dict

Generates OpenAPI schema for all registered routes.

Usage Examples

Basic Application Setup

from pyweber import Pyweber
from pyweber.utils.types import ContentTypes

# Create application instance
app = Pyweber()

# Add simple route
app.add_route(
    route="/",
    template="<h1>Welcome to PyWeber!</h1>",
    title="Home Page"
)

# Add API route
app.add_route(
    route="/api/users",
    template=lambda: {"users": [], "total": 0},
    content_type=ContentTypes.json,
    methods=["GET", "POST"]
)

print(app)  # Pyweber(routes=2)

Using Route Decorator

app = Pyweber()

@app.route("/users/{user_id}", methods=["GET"])
def get_user(user_id: int):
    return f"<h1>User {user_id}</h1>"

@app.route("/api/posts", methods=["GET", "POST"], 
           content_type=ContentTypes.json)
async def handle_posts():
    return {"posts": []}

Template Processing

from pyweber.core.template import Template
from pyweber.core.element import Element

app = Pyweber()

# Template object route
@app.route("/template-page")
def template_page():
    return Template(
        template="<h1>{{title}}</h1><p>{{content}}</p>",
        title="Template Page",
        context={"title": "Hello", "content": "World"}
    )

# Element object route
@app.route("/element-page")
def element_page():
    return Element(
        tag="div",
        content=[
            Element(tag="h1", content="Element Page"),
            Element(tag="p", content="Generated with Element")
        ]
    )

# JSON API route
@app.route("/api/data", content_type=ContentTypes.json)
def api_data():
    return {"message": "Hello API", "data": [1, 2, 3]}

Middleware Integration

def auth_middleware(request):
    if not request.headers.get("Authorization"):
        return Response(
            content="<h1>Unauthorized</h1>",
            status_code=401,
            process_response=True
        )
    return None

def logging_middleware(request):
    print(f"Request: {request.method} {request.path}")
    return None

# Add global middleware
app.add_before_request_middleware(auth_middleware)
app.add_before_request_middleware(logging_middleware)

# Add route-specific middleware
@app.route("/protected", middlewares=[auth_middleware])
def protected_route():
    return "<h1>Protected Content</h1>"

Error Handling

app = Pyweber()

# Custom 404 page
app.page_not_found = "<h1>Custom 404 Page</h1><p>Page not found!</p>"

# Route with error handling
@app.route("/may-fail")
def may_fail():
    try:
        # Some operation that might fail
        result = risky_operation()
        return f"<h1>Success: {result}</h1>"
    except Exception as e:
        return f"<h1>Error: {str(e)}</h1>"

Static File Serving

app = Pyweber()

# Static files are automatically served if they exist
# /static/style.css -> serves static/style.css if file exists
# /images/logo.png -> serves images/logo.png if file exists

# Custom static file route
@app.route("/custom-css", content_type=ContentTypes.css)
def custom_css():
    return "body { background-color: #f0f0f0; }"

Redirects

app = Pyweber()

# Add target route
app.add_route("/new-page", template="<h1>New Page</h1>", name="new_page")

# Create redirect
app.redirect("/old-page", "new_page", status_code=301)

# Programmatic redirect in route
@app.route("/login-required")
def login_required():
    if not user_authenticated():
        return app.to_route("login_page")
    return "<h1>Welcome!</h1>"

Request Processing

app = Pyweber()

@app.route("/form-handler", methods=["POST"])
def handle_form(name: str, email: str, age: int = 18):
    # Parameters automatically extracted from request body
    return f"<h1>Hello {name}!</h1><p>Email: {email}, Age: {age}</p>"

@app.route("/user-info")
def user_info():
    # Access current request
    request = app.request
    user_agent = request.headers.get("User-Agent", "Unknown")
    return f"<h1>Your User Agent: {user_agent}</h1>"

Running the Application

app = Pyweber()

# Add routes...
app.add_route("/", template="<h1>Hello World</h1>")

# Run development server
app.run(host="127.0.0.1", port=8000, debug=True)

# Or run with custom target function
def custom_server():
    print("Starting custom server...")
    # Custom server logic

app.run(target=custom_server, host="0.0.0.0", port=5000)

ASGI Deployment

# app.py
from pyweber import Pyweber

app = Pyweber()

@app.route("/")
def home():
    return "<h1>Production App</h1>"

# Deploy with ASGI server (uvicorn, gunicorn, etc.)
# uvicorn app:app --host 0.0.0.0 --port 8000

Advanced Features

OpenAPI Documentation

app = Pyweber()

@app.route("/api/users/{user_id}", methods=["GET"])
def get_user(user_id: int):
    """Get user by ID"""
    return {"user_id": user_id, "name": "John Doe"}

# OpenAPI spec automatically available at:
# /_pyweber/{uuid}/openapi.json
# Documentation UI available at: /docs

Template Cloning

app = Pyweber()

@app.route("/template")
def template_route():
    return Template(template="<h1>{{title}}</h1>", title="Original")

# Clone template for modification
async def clone_example():
    cloned = await app.clone_template("/template")
    cloned.context["title"] = "Cloned"
    return cloned.build_html()

File Change Handling

def handle_file_change(module):
    print(f"File changed: {module}")
    # Reload logic here

app = Pyweber(update_handler=handle_file_change)

# Trigger update
app.update("my_module.py")

Browser Integration

app = Pyweber()

@app.route("/open-external")
def open_external():
    app.launch_url("https://example.com", new_page=True)
    return "<h1>External page opened</h1>"

@app.route("/redirect-with-message")
def redirect_with_message():
    return app.to_url(
        url="/target-page",
        new_page=False,
        message="Redirecting to target page..."
    )

Framework Routes

PyWeber automatically adds several built-in routes:

Admin Routes

  • /admin - Admin panel interface
  • /_pyweber/admin/{uuid}/.css - Admin CSS files
  • /_pyweber/admin/{uuid}/.js - Admin JavaScript files

Documentation Routes

  • /docs - PyWeber documentation
  • /_pyweber/{uuid}/openapi.json - OpenAPI specification

Static Asset Routes

  • /_pyweber/static/favicon.ico - Framework favicon
  • /_pyweber/static/{uuid}/.css - Framework CSS files
  • /_pyweber/static/{uuid}/.js - Framework JavaScript files

Error Handling

Built-in Error Pages

app = Pyweber()

# Customize error pages
app.page_not_found = "<h1>Custom 404</h1>"
app.internal_server_error = "<h1>Custom 500</h1>"

Middleware Error Handling

def error_handling_middleware(request):
    try:
        # Process request
        return None
    except Exception as e:
        return Response(
            content=f"<h1>Middleware Error: {str(e)}</h1>",
            status_code=500,
            process_response=True
        )

app.add_before_request_middleware(error_handling_middleware)

Performance Considerations

Template Processing

  • Templates are processed on each request
  • Use caching for expensive template operations
  • Consider static file serving for unchanging content

Route Resolution

  • Route resolution uses pattern matching
  • Large numbers of routes may impact performance
  • Use route groups for organization

Middleware Chain

  • Middleware is processed in order for each request
  • Keep middleware lightweight
  • Use early returns to avoid unnecessary processing

Static File Serving

  • Static files are served directly from filesystem
  • Consider using a reverse proxy for production static file serving
  • File existence is checked on each request

Best Practices

Application Structure

# Organize routes logically
app = Pyweber()

# API routes
@app.route("/api/users", methods=["GET", "POST"], group="api")
def users_api():
    return {"users": []}

# Web pages
@app.route("/", name="home")
def home():
    return Element('h1', content='Home Page')

# Admin routes
@app.route("/admin/dashboard", middlewares=[admin_auth])
def admin_dashboard():
    return Element('h1', content='Dashboard Page')

Error Handling

# Centralized error handling
def global_error_handler(request):
    try:
        return None  # Continue processing
    except Exception as e:
        logger.error(f"Request error: {e}")
        return Response(
            content="<h1>Something went wrong</h1>",
            status_code=500,
            process_response=True
        )

app.add_before_request_middleware(global_error_handler)

Security

# Security middleware
def security_middleware(response):
    response.headers.update({
        "X-Content-Type-Options": "nosniff",
        "X-Frame-Options": "DENY",
        "X-XSS-Protection": "1; mode=block"
    })
    return response

app.add_after_request_middleware(security_middleware)

Development vs Production

import os

app = Pyweber()

# Environment-specific configuration
if os.getenv("ENVIRONMENT") == "development":
    app.run(debug=True, reload=True)
else:
    # Production deployment with ASGI server
    # uvicorn app:app --host 0.0.0.0 --port 8000 --workers 4
    pass

Thread Safety

  • Pyweber instances are not inherently thread-safe
  • Each request should be processed independently
  • Avoid shared mutable state between requests
  • Use proper synchronization for shared resources

Integration Examples

Database Integration

import sqlite3
from pyweber import Element

app = Pyweber()

def get_db():
    return sqlite3.connect("app.db")

@app.route("/users/{user_id}")
def get_user(user_id: int):
    db = get_db()
    user = db.execute("SELECT * FROM users WHERE id = ?", (user_id,)).fetchone()
    db.close()

    if user:
        return Element('h1', content=f'User: {user[1]}')
    else:
        return app.page_not_found

Template Engine Integration

from jinja2 import Environment, FileSystemLoader

app = Pyweber()
jinja_env = Environment(loader=FileSystemLoader("templates"))

@app.route("/jinja-template")
def jinja_template():
    template = jinja_env.get_template("page.html")
    return template.render(title="Jinja Page", content="Hello from Jinja!")

API Integration

import requests

app = Pyweber()

@app.route("/api/external-data", content_type=ContentTypes.json)
async def external_data():
    response = requests.get("https://api.example.com/data")
    return response.json()