🐍 Chilo
Chilo is a lightweight, form-meets-function, opinionated (yet highly configurable) API framework.
Chilo (short for chilorhinophis, the two-headed snake) auto-routes requests straight from your directory tree, applies OpenAPI or custom validation before your handlers run, and keeps REST + gRPC services declarative and composable.
📖 Documentation & Examples
Full Documentation · Examples · Community Discussions
🎯 Why Chilo?
- 🚀 Zero Route Boilerplate – File paths become URLs; dynamic segments are inferred from file names.
- ✅ Built-in Validation – Apply OpenAPI schemas (request + response) or lightweight requirement decorators.
- 🧱 Middlewares Everywhere –
before,after,when_auth_required, and global hooks keep cross-cutting logic centralized. - 🔔 Lifecycle Hooks –
on_startup/on_shutdownlet you register callables that fire once when the router boots and when it stops. - 🔁 REST + gRPC – Switch between HTTP and gRPC by flipping
api_typeand pointing at protobufs. - 📜 Spec Generation – Inspect handlers + requirements to emit
openapi.yml/openapi.jsonwith a single CLI command. - ⚙️ Deploy-Friendly – Works with gunicorn or the built-in CLI; supports TLS, CORS, hot reload, and custom executors.
Happy Path Programming
Validate the world up front, then write business logic as if everything is already correct.
# ❌ Without Chilo
def handler(environ, start_response):
body = json.loads(environ.get('body') or '{}')
if 'email' not in body:
return error(400, 'email required')
if '@' not in body['email']:
return error(400, 'invalid email')
# more guards ...
return ok({'user': create(body)})
# ✅ With Chilo
from chilo_api import requirements, Request, Response
@requirements(required_body='v1-create-user')
def post(request: Request, response: Response) -> Response:
response.body = {'user': create(request.body)} # already validated
return response
📦 Installation
pip install chilo_api
# pipenv install chilo_api
# poetry add chilo_api
Supports Python 3.8
🚀 Quick Start · REST
1. Configure the API (e.g., api/main.py)
from chilo_api import Chilo
from chilo_api import logger
def cleanup_connections():
# close DB pools, flush caches, etc.
pass
api = Chilo(
base_path='/',
handlers='api/handlers',
cors=True,
on_startup=[lambda: logger.log(level='INFO', log='API booted')],
on_shutdown=[cleanup_connections],
openapi='api/openapi.yml', # optional
openapi_validate_request=False, # flip on when ready
openapi_validate_response=False
)
2. Create a Handler (api/handlers/__init__.py)
from chilo_api import Request, Response, requirements
@requirements(required_query=['greeting'])
def get(request: Request, response: Response) -> Response:
response.body = {'hello': request.query_params['greeting']}
return response
3. Run It
python -m chilo_api serve --api=api.main --reload=true
Visit http://127.0.0.1:3000/?greeting=developer
Directory → Route Mapping
api/handlers
├── __init__.py → /
├── user/__init__.py → /user
├── user/_user_id.py → /user/{user_id}
└── reports/daily.py → /reports/daily
Dynamic segments are prefixed with _ in file names (or directories) and become {param} tokens at runtime.
📡 Quick Start · gRPC
1. Define the API (api/main_grpc.py)
from chilo_api import Chilo
api = Chilo(
api_type='grpc',
handlers='api/handlers',
protobufs='api/protobufs',
reflection=True,
port=50051
)
2. Author Your Proto (api/protobufs/calculator.proto)
syntax = "proto3";
package calculator;
service Calculator {
rpc Add(CalcRequest) returns (CalcResponse);
}
message CalcRequest { double num1 = 1; double num2 = 2; }
message CalcResponse { double result = 1; }
3. Implement the Handler (api/handlers/__init__.py)
from chilo_api import requirements, Request, Response
@requirements(protobuf='calculator.proto', service='Calculator', rpc='Add')
def add(request: Request, response: Response) -> Response:
response.body = {'result': request.body.get('num1', 0) + request.body.get('num2', 0)}
return response
4. Serve It
python -m chilo_api serve --api=api.main_grpc
🧰 CLI Highlights
| Command | Description |
|---|---|
python -m chilo_api serve --api=api.main |
Run REST or gRPC servers with hot reload, TLS, verbose logging, etc. |
python -m chilo_api generate-openapi --api=api.main --output=docs --format=yml,json |
Inspect handlers + requirements to (re)build OpenAPI docs. |
Both commands accept switches for host, port, reload, private key/cert, worker count, and validation flags.
🤝 Contributing
Issues and PRs are welcome! Check SECURITY.md before reporting vulnerabilities and open a discussion if you want to propose larger features. For docs fixes, see chilo-docs.
📜 License
Chilo is released under the MIT License.
Metadata
Release files for chilo-api 2.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| chilo_api-2.0.2.tar.gz | 84.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| chilo_api-2.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 198.7 kB
Release files / chilo_api-2.0.2.tar.gz
| Download URL | chilo_api-2.0.2.tar.gz |
|---|---|
| Size | 84.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
eaf097ffd5a35ce3e026fa7280a77c45a1a6eed6718aaee21c3a8cfc201f2ee9
|
|
BLAKE2b-256 checksum How to use checksums |
aa0309c8a223ad8753a1ea5f05bfa9be8733f08cbe4c04d3979697fb25f550a7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.17
|
Release files / chilo_api-2.0.2-py3-none-any.whl
| Download URL | chilo_api-2.0.2-py3-none-any.whl |
|---|---|
| Size | 114.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
dc7dec4de04a86d1cc9edc3fcb33b7e98c221ded905b26d5c8b4284f4675dc86
|
|
BLAKE2b-256 checksum How to use checksums |
f3c064ea78938feecd1e95bc703d4b397ed6d4a75ee426f5766e951c7ba7793c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.17
|