Minimal ASGI app scaffold
Project description
yaaf
YAAF stands for "Yet Another ASGI Framework".
A minimal Python ASGI app scaffold that discovers routes from the filesystem. It includes a tiny router and a CLI wrapper around uvicorn.
Design Goals and Opinions
- Filesystem-first routing. Routes are inferred from the directory structure under
consumers/**/apirather than declared with decorators. This keeps routing discoverable by looking at the tree. - Explicit endpoint files. Each route has
_server.pyand_service.pyto separate request handling from domain logic. - Dependency injection without wiring. Services are registered automatically and injected by name/type, so handlers and services focus on behavior, not setup.
- Static-first routing precedence. Static routes always win over dynamic segments, with warnings when a dynamic route would overlap a static route.
- Minimal core. The framework is intentionally small and opinionated, leaving room for you to add auth, middleware, validation, etc.
Quickstart
python -m venv .venv
source .venv/bin/activate
pip install -e .
# Run the built-in example routes
yaaf --reload
Example routes:
GET /api/helloGET /api/<name>(dynamic segment)
Routing Model
Routes are inferred from the directory structure under any consumers/**/api directory.
- Every route directory must contain
_server.pyand_service.py. - The route path is
/api/...plus the sub-path afterapi/. - Dynamic segments use
[param]directory names and are exposed asparams/path_params.
Example layout:
consumers/
api/
users/
_server.py
_service.py
hello/
_server.py
_service.py
[name]/
_server.py
_service.py
Handlers and Services
In _server.py, export functions named after HTTP methods (lowercase): get, post, etc. The function signature is resolved via dependency injection:
requestgives you theyaaf.Requestobject.paramsorpath_paramsprovides dynamic route parameters.- Services are injected by type annotations.
Example _server.py:
from consumers.api import HelloService
from yaaf import Request
from yaaf.types import Params
async def get(request: Request, service: HelloService, params: Params):
return {"message": service.message(), "path": request.path, "params": params}
In _service.py, expose a module-level service instance (or a callable like Service or get_service). Services are registered and can be injected into other services or handlers:
from consumers.api import UsersService
class Service:
def __init__(self, users: UsersService) -> None:
self._users = users
def message(self) -> str:
user = self._users.get_user("1")
return f"Hello from yaaf, {user['name']}"
service = Service
Service-to-Service Injection
Services can depend on other services via type annotations. Example layout:
consumers/
api/
users/
_service.py
_server.py
hello/
_service.py
_server.py
consumers/api/users/_service.py
class Service:
def get_user(self, user_id: str) -> dict:
return {"id": user_id, "name": "Austin"}
service = Service()
consumers/api/hello/_service.py
from consumers.api import UsersService
class Service:
def __init__(self, users: UsersService) -> None:
self._users = users
def message(self) -> str:
user = self._users.get_user("1")
return f"Hello from yaaf, {user['name']}"
service = Service
consumers/api/hello/_server.py
from consumers.api import HelloService
from yaaf import Request
async def get(request: Request, service: HelloService):
return {"message": service.message(), "path": request.path}
Running Another App
yaaf --app your_package.app:app
Versioning
This project uses calendar-based versions with a timestamp (UTC). To bump the version:
python scripts/bump_version.py
Service Type Generation
Every yaaf command regenerates consumers/api/__init__.py for type-checking. You can also run it explicitly:
yaaf gen-services
Dynamic route segments like [name] get Protocol stubs in the generated file since they are not valid import paths.
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 yaafcli-2026.2.6.43348.tar.gz.
File metadata
- Download URL: yaafcli-2026.2.6.43348.tar.gz
- Upload date:
- Size: 15.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fe79e1c4074639cdea111d75b562a86ef2ad6d34e710af9f8f85cebedbd683ac
|
|
| MD5 |
57c5eef62ef45fe3d993bef375fd0625
|
|
| BLAKE2b-256 |
fb46aceed0c7349b9c9d430e9d2778553afc1f5577e29774e81efb1052ccbbab
|
File details
Details for the file yaafcli-2026.2.6.43348-py3-none-any.whl.
File metadata
- Download URL: yaafcli-2026.2.6.43348-py3-none-any.whl
- Upload date:
- Size: 14.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0b3e0a2a8826e4a006c435aad0a5bea9fc783d709485a69545da01d25540a246
|
|
| MD5 |
12db35a66ef2dc8da94c8fb802ba1509
|
|
| BLAKE2b-256 |
8194e8264a3ae9a8471a8c427a98ad13d4c650a2bfe4a85561c206bc3007aded
|