Skip to main content

PyForge - an AI-first, async Python web framework. Flask simplicity, FastAPI speed, Django batteries.

Project description

PyForge ⚒️

An AI-first, async Python web framework. Flask simplicity · FastAPI performance · Django batteries — plus built-in AI, document processing, and workflow automation (rolling out phase by phase).

from pyforge import PyForge, Router

app = PyForge(title="My API")
api = Router(prefix="/api")

@api.get("/users/{user_id:int}")
async def get_user(user_id: int) -> dict:
    return {"id": user_id}

app.include_router(api)
pyforge new myapp
cd myapp
pyforge run

Why PyForge?

Flask FastAPI Django PyForge
Async-first ASGI
Zero-boilerplate routing
Layered config (.env + YAML + profiles + secrets)
Dependency injection
AI module (LLMs, RAG, agents) 🔜 Phase 4
Document AI (OCR, PDF extraction) 🔜 Phase 5
Workflow automation 🔜 Phase 6
Auto admin dashboard 🔜 Phase 7

Installation

pip install "pyforge-framework[server]"
# or from source:
pip install -e ".[dev]"

Requires Python 3.12+.

Quickstart

1. Scaffold a project

pyforge new myapp
myapp/
├── app/
│   ├── __init__.py
│   └── main.py
├── config/
│   ├── settings.yaml
│   └── settings.production.yaml
├── tests/
│   └── test_app.py
├── conftest.py
├── .env
├── .gitignore
├── requirements.txt
└── README.md

2. Run it

cd myapp
pyforge run                 # http://127.0.0.1:8000
pyforge run --reload        # auto-reload in development
pyforge run app.main:app --host 0.0.0.0 --port 9000

Core concepts (Phase 1)

Routing

from pyforge import PyForge, Router

app = PyForge()

@app.get("/")
async def index() -> dict:
    return {"hello": "world"}

# Typed path converters: str (default), int, float, uuid, slug, path
@app.get("/files/{filepath:path}")
async def download(filepath: str) -> dict:
    return {"path": filepath}

# Route groups with prefixes and group middleware
v1 = Router(prefix="/v1")

@v1.get("/items")
async def items() -> list:
    return [1, 2, 3]

api = Router(prefix="/api")
api.include_router(v1)
app.include_router(api)      # -> GET /api/v1/items

Handlers may return a Response, dict/list (JSON), str (plain text), None (204), or a (body, status_code) tuple.

Dependency injection

from pyforge import Depends, PyForge, Request

app = PyForge()

class Database:
    async def fetch_user(self, user_id: int) -> dict:
        return {"id": user_id}

app.container.register(Database)            # constructor-injected singleton

async def current_user(request: Request) -> dict:
    return {"token": request.headers.get("authorization")}

@app.get("/users/{user_id:int}")
async def get_user(
    user_id: int,                            # path param (typed)
    db: Database,                            # container-resolved service
    user: dict = Depends(current_user),      # request-scoped dependency
    verbose: bool = False,                   # query param with default
) -> dict:
    return await db.fetch_user(user_id)

Middleware

from pyforge.middleware import BaseHTTPMiddleware, CORSMiddleware

class TimingMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        response = await call_next(request)
        response.headers["x-powered-by"] = "PyForge"
        return response

app.add_middleware(CORSMiddleware, allow_origins=["https://myapp.dev"])
app.add_middleware(TimingMiddleware)

# Middleware can also be scoped to a route group:
admin = Router(prefix="/admin", middleware=[TimingMiddleware()])

Configuration

Layered lookup, highest priority first: os.environ.env file → config/settings.<profile>.yamlconfig/settings.yaml → defaults.

from pyforge.config import Config

config = Config()                       # profile from PYFORGE_ENV, default "development"
config.get("database.url")             # env override key: DATABASE__URL
config.get("app.debug", False, cast=bool)
config.require("app.name")            # raises ConfigError if missing
config.secret("db_password")          # supports Docker-style DB_PASSWORD_FILE

YAML values support ${ENV_VAR} and ${ENV_VAR:default} interpolation.

Lifecycle & errors

@app.on_startup
async def connect() -> None: ...

@app.on_shutdown
async def disconnect() -> None: ...

@app.exception_handler(KeyError)
async def missing(request, exc):
    return {"detail": "missing key"}, 400

Testing

from pyforge.testing import TestClient
from app.main import app

client = TestClient(app)

def test_health():
    assert client.get("/health").json() == {"status": "ok"}

CLI

pyforge new <project>    Scaffold a new project
pyforge run [app]        Run the ASGI server (uvicorn)
pyforge version          Print the installed version

generate model|crud|api, migrate, makemigrations, test, and build ship with Phases 2–3 (ORM & codegen).

Roadmap

See CONTRIBUTING.md for the full phase plan: ORM → Auth → AI → Document AI → Workflows → Admin & Monitoring.

License

MIT — see LICENSE.

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

pyforge_framework-0.1.0.tar.gz (85.6 kB view details)

Uploaded Source

Built Distribution

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

pyforge_framework-0.1.0-py3-none-any.whl (94.1 kB view details)

Uploaded Python 3

File details

Details for the file pyforge_framework-0.1.0.tar.gz.

File metadata

  • Download URL: pyforge_framework-0.1.0.tar.gz
  • Upload date:
  • Size: 85.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.0

File hashes

Hashes for pyforge_framework-0.1.0.tar.gz
Algorithm Hash digest
SHA256 5b2fa7f75e05f2d1a14e3c4d3a224a10f6060240518f74434e2cff9c7bc43ee0
MD5 67a5aa3fd0f2728d7da8511e52840ac1
BLAKE2b-256 d0070e9f74d589660e3d73a3c830257f18096e894c8ab758e9e1aa86b1d0bf25

See more details on using hashes here.

File details

Details for the file pyforge_framework-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for pyforge_framework-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 dfa3b2cd7fba30e3e3f4d16276b23152ab9f85d71def5da0d6b34287ab3021c6
MD5 732a0fdb3738f18b063fb9aa2a1257c7
BLAKE2b-256 b3026c6cacf0cc25e930bdfeed94ccfe13a18b9ce1538d25257a45b9955a726b

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