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>.yaml →
config/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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5b2fa7f75e05f2d1a14e3c4d3a224a10f6060240518f74434e2cff9c7bc43ee0
|
|
| MD5 |
67a5aa3fd0f2728d7da8511e52840ac1
|
|
| BLAKE2b-256 |
d0070e9f74d589660e3d73a3c830257f18096e894c8ab758e9e1aa86b1d0bf25
|
File details
Details for the file pyforge_framework-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pyforge_framework-0.1.0-py3-none-any.whl
- Upload date:
- Size: 94.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dfa3b2cd7fba30e3e3f4d16276b23152ab9f85d71def5da0d6b34287ab3021c6
|
|
| MD5 |
732a0fdb3738f18b063fb9aa2a1257c7
|
|
| BLAKE2b-256 |
b3026c6cacf0cc25e930bdfeed94ccfe13a18b9ce1538d25257a45b9955a726b
|