Skip to main content

PoridhiFrame Python Web Framework built for learning purposes.

Project description

PoridhiWeb

PoridhiWeb is a small, educational WSGI-compatible Python web framework built from scratch to help you understand how web frameworks work. It's intentionally lightweight and opinionated for learning and experimentation.

Features

  • WSGI Compatible: The framework exposes a callable application usable with any WSGI server.
  • Routing: Supports both automatic (path pattern) and explicit route registration.
  • Handlers: Function-based and class-based handlers are supported.
  • Middlewares: Compose request/response processing via middleware classes. Includes ErrorHandlerMiddleware and helpers.
  • Templating: Provides a templating system accessible via the template() helper on the app.
  • Static Files: Static files (CSS/JS/images) can be served from a static/ directory.
  • Error Handling: Built-in ResponseError and optional middleware to convert exceptions into JSON responses.
  • HTTP Method Control: Route definitions can restrict allowed HTTP methods.
  • ORM: Lightweight Object-Relational Mapping with SQLite support, featuring model definitions, CRUD operations, and foreign key relationships.
  • Published: The package is available on PyPI for easy installation.

Installation

  • From PyPI: pip install poridhiweb

Quick Start

  • Create a minimal app
from poridhiweb.framework import PoridhiFrame
app = PoridhiFrame()

@app.route('/')
def index(request):
    return app.templates_env.get_template('dashboard.html').render()

if __name__ == '__main__':
    from wsgiref.simple_server import make_server
    server = make_server('0.0.0.0', 8080, app)
    print('Serving on http://0.0.0.0:8080')
    server.serve_forever()

Function-based handler

from poridhiweb.framework import PoridhiFrame
from poridhiweb.models.responses import JSONResponse

app = PoridhiFrame()

@app.route('/hello')
def hello(request):
    return JSONResponse({"message": "Hello from PoridhiWeb"})

Class-based Handler

Poridhiweb support both automatic and manually registered Class Based handlers

Self registered Class-based Handler

The function names should matched with HTTP request method.

@app.route('/items')
class ItemHandler:
    def __init__(self):
        self.service = ItemService()

    # get all products
    def get(self, request):
        items: list[dict] = self.service.get_items()
        return JSONResponse(items)
    
    # create a product
    def post(self, request):
        items: dict = self.service.create_items()
        return JSONResponse(items)

Notes:

  • Self registered Class-based handlers are registered as classes. The framework will instantiate the class and call the method matching the HTTP method name (e.g., get, post).

Manually registerd Class-based Handler

If you need both custom handlers in a class then you can register routes manually

from poridhiweb.framework import PoridhiFrame
from poridhiweb.models.responses import JSONResponse

app = PoridhiFrame()

class ItemHandlerCustomRouting:
    def get_by_id(self, request, item_id=None):
        return JSONResponse({"item_id": item_id})
    
    def get_by_category(self, request, category=None):
        # JSONResponse also supports list of classes
        items: list[Item] = items_service.get_by_category()
        return JSONResponse(items)

handler = ItemHandlerCustomRouting()
app.add_route('/items/{item_id:d}', handler.get_by_id)
app.add_route('/items/{category}', handler.get_by_category)

Routing & Path Variables

  • Paths can include variables using the {name} syntax. The framework will parse them and pass as kwargs to your handler.
  • Example: app.add_route('/users/{user_id}', handler) — handler will receive user_id as a keyword argument.

Middlewares & Error Handling

  • Built-in middlewares: ErrorHandlerMiddleware, ReqResLoggingMiddleware, and ExecutionTimeMiddleware are provided in poridhiweb.middlewares package.
  • ResponseError: Raise poridhiweb.exceptions.ResponseError (or its subclasses) from handlers to return structured JSON error responses. The ErrorHandlerMiddleware converts ResponseError into an appropriate JSON response with the specified HTTP status.

Example — adding middleware and a simple error:

from poridhiweb.framework import PoridhiFrame
from poridhiweb.middlewares import ErrorHandlerMiddleware
from poridhiweb.exceptions import ResponseError

app = PoridhiFrame()
app.add_middleware(ErrorHandlerMiddleware)

@app.route('/fail')
def fail(request):
    raise ResponseError('This is a custom error', 400)
  • Response JSON
{
    "message": "This is a custom error"
}

Templating

  • The app provides a templating system. Templates are loaded from the templates directory by default. Use app.template(template_name, context) to generate view from a template dynamically.

Example: Register the template if you have a customer template directory

from poridhiweb.framework import PoridhiFrame
from poridhiweb.models.responses import HTMLResponse

app = PoridhiFrame(template_dir=f"{cwd}/templates")

@app.route('/dashboard')
def dashboard(request) -> Response:
    name = "Hello User"
    title = "Dashboard View"
    html_content = app.template(
        "dashboard.html",
        context={"name": name, "title": title}
    )
    return HTMLResponse(html_content)

Static Files

  • Static assets under the static directory are served automatically. Place CSS/JS/images in static/ and reference them from your templates.

ORM (Object-Relational Mapping)

PoridhiWeb includes a lightweight ORM for database interactions. Currently supports SQLite with a factory pattern for future database dialect support.

Database Connection

Use DatabaseFactory to create a database connection:

from poridhiweb.orm.db_factory import DatabaseFactory, Dialect

db = DatabaseFactory(dialect=Dialect.SQLITE).get_connection("mydb.sqlite")

Defining Models

Define your models by extending the Table class and using column types:

from poridhiweb.orm.table import Table
from poridhiweb.orm.column import Column, PrimaryKey, ForeignKey

class Author(Table):
    id = PrimaryKey(int, auto_increment=True)
    name = Column(str)
    age = Column(int)

class Book(Table):
    id = PrimaryKey(int, auto_increment=True)
    title = Column(str)
    author = ForeignKey(Author)  # Foreign key relationship

Supported Column Types

Python Type SQLite Type
int INTEGER
str TEXT
float REAL
bool INTEGER
bytes BLOB

CRUD Operations

Create Tables

db.create(Author)
db.create(Book)

Insert Records

author = Author(name="John Doe", age=30)
db.save(author)
print(author.id)  # Auto-generated ID is set after save

Query Records

# Get all records
authors = db.get_all(Author)

# Get by ID
author = db.get_by_id(Author, id=1)

Update Records

author = db.get_by_id(Author, id=1)
author.name = "Jane Doe"
db.update(author)

Delete Records

db.delete(Author, id=1)

Foreign Key Relationships

Foreign keys are automatically resolved when querying:

# Create and save an author
author = Author(name="Jane Doe", age=28)
db.save(author)

# Create a book with author reference
book = Book(title="My First Book", author=author)
db.save(book)

# When fetching, foreign key is automatically loaded
fetched_book = db.get_by_id(Book, id=1)
print(fetched_book.author.name)  # "Jane Doe"

Exception Handling

The ORM provides a RecordNotFound exception for missing records:

from poridhiweb.orm.exceptions import RecordNotFound

try:
    author = db.get_by_id(Author, id=999)
except RecordNotFound as e:
    print(f"Error: {e}")

WSGI Compatibility

  • The PoridhiFrame instance is a valid WSGI application. You can run it with any WSGI server (uWSGI, Gunicorn, or wsgiref during development).

You can run the demo application with Gunicorn service using

make run

Tests & Examples

  • See the tests/ and demo_app/ directories in this repository for usage examples and test coverage.

Contributing

  • This project is intended for learning. Contributions that improve docs, add examples, or clarify internals are welcome.

License

  • See the LICENSE file in this repository.

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

poridhiweb-0.0.5.tar.gz (26.8 kB view details)

Uploaded Source

Built Distribution

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

poridhiweb-0.0.5-py3-none-any.whl (30.7 kB view details)

Uploaded Python 3

File details

Details for the file poridhiweb-0.0.5.tar.gz.

File metadata

  • Download URL: poridhiweb-0.0.5.tar.gz
  • Upload date:
  • Size: 26.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.13

File hashes

Hashes for poridhiweb-0.0.5.tar.gz
Algorithm Hash digest
SHA256 95001b272d1a721e569eac22335711e3bb72c2774cba256e41555abe62813394
MD5 4fa71d6a1a277ebc705a338f7da43394
BLAKE2b-256 2f3244104bf4721b360b0d31fa120c2b1dcc10aba5ee969751989f4ee324b0b2

See more details on using hashes here.

File details

Details for the file poridhiweb-0.0.5-py3-none-any.whl.

File metadata

  • Download URL: poridhiweb-0.0.5-py3-none-any.whl
  • Upload date:
  • Size: 30.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.13

File hashes

Hashes for poridhiweb-0.0.5-py3-none-any.whl
Algorithm Hash digest
SHA256 244c22ab117efd8606764e286d8f7aac160f89729151bd3e962e38829274fa13
MD5 52a588b32759afe2cdf99b819dd7dc35
BLAKE2b-256 808c033060117bb3811cdc563d168e08c570edd0b0dcacb041c6d6c8d8010a2c

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