Skip to content

dual/chilo

Repository files navigation

🐍 Chilo

Chilo

Chilo is a lightweight, form-meets-function, opinionated (yet highly configurable) API framework.

CircleCI Quality Gate Status Coverage Bugs PyPI Python Inline docs Contributions welcome

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_shutdown let you register callables that fire once when the router boots and when it stops.
  • πŸ” REST + gRPC – Switch between HTTP and gRPC by flipping api_type and pointing at protobufs.
  • πŸ“œ Spec Generation – Inspect handlers + requirements to emit openapi.yml/openapi.json with 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.

About

A minimal python web-server optimized for APIs

Resources

License

Security policy

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •