Cullinan is written based on tornado and Sqlalchemy to help the project quickly build web application
Project description
_____ _ _ _
/ ____| | | (_)
| | _ _| | |_ _ __ __ _ _ __
| | | | | | | | | '_ \ / _` | '_ \
| |___| |_| | | | | | | | (_| | | | |
\_____\__, _|_|_|_|_| |_|\__,_|_| |_|
Cullinan
A lightweight, modular Python web framework with built-in IoC/DI
Cullinan is built on Tornado (HTTP/WebSocket) and focuses on:
- A unified registry model for controllers, services, handlers
- Built-in IoC/DI with request scope and service lifecycle hooks
- Production-friendly startup/shutdown flow on a default port 4080
✨ Features
Core Framework
- Simple decorator-based routing (
@controller,@get_api,@post_api, ...) - Type-safe parameter system with
Path,Query,Body,Header,File(v0.90+) - Unified parameter syntax:
param: Type = Type(...)(v0.90a5+) - Pure type annotation as Query:
page: intautomatically becomes Query parameter (v0.90a5+) - as_required() shortcut:
File.as_required(),Body.as_required()(v0.90a5+) - DynamicBody for flexible request body access with safe accessors (v0.90a4+)
- RawBody for raw unparsed request body (bytes) (v0.90a5+)
- FileInfo/FileList for file upload with validation (v0.90a5+)
- @field_validator for dataclass field validation (v0.90a5+)
- ResponseSerializer for automatic response serialization (v0.90a5+)
- Pluggable model handlers for Pydantic and custom model integration (v0.90a5+)
- Auto type conversion and validation (ge, le, min_length, regex, etc.)
- Modular architecture with registry, DI, and lifecycle management
- Built-in IoC/DI with
InjectByNameandInjectsupport - Lifecycle hooks (e.g.
on_init,on_destroy) on services - Designed for tests: resettable registries and request-scoped dependencies
Services & WebSocket
- Service registry with dependency resolution
- WebSocket support via
@websocket_handlerand registry pattern - Request context / request scope for per-request objects
Deployment & Production
- Packaging-friendly (Nuitka, PyInstaller)
- Cross-platform: Windows, Linux, macOS
- Based on production-tested Tornado
📚 Documentation
Language / 语言
- English Documentation – complete English docs
- 中文文档 – 完整中文文档
- 快速开始
- 架构 · 生命周期
- IoC/DI 2.0 · 迁移指南
- RESTful 路由
Version Notes
-
Current series: v0.90a5+
- New type-safe parameter system with
Path,Query,Body,Header,File - DynamicBody for flexible request body access with safe accessors
- FileInfo/FileList for file upload handling with validation (v0.90a5+)
- @field_validator for dataclass field validation (v0.90a5+)
- ResponseSerializer for automatic response serialization (v0.90a5+)
- Pluggable model handlers for Pydantic and custom model integration (v0.90a5+)
- Auto type conversion and parameter validation
- New IoC/DI 2.0 architecture with
ApplicationContext - Single container entry point with freeze-after-startup
- Definition/Factory separation for dependency management
- Structured diagnostics with stable dependency chains
- Core module with registry, DI, lifecycle management
- Enhanced service layer with dependency injection
- WebSocket support with unified registry
- Request context management
- New type-safe parameter system with
-
Migration from v0.83:
- See Import Migration Guide for detailed migration steps
- Legacy APIs are deprecated but still functional in v0.90
- Will be removed in v1.0
🚀 Quick Start
Install
Use pip from PyPI:
pip install -U pip
pip install cullinan
Ensure you have a working Python 3.8+ environment (virtualenv/conda/system Python are all fine).
Minimal Application
# app.py
from cullinan import application
from cullinan.controller import controller, get_api, post_api
from cullinan.params import Query, Body, DynamicBody
@controller(url='/api')
class HelloController:
# Type-safe query parameters (new unified syntax)
@get_api(url='/hello')
def hello(self, name: str = Query(default='World')):
return self.response_factory(
status=200,
body={"message": f"Hello, {name}!"}
)
# Pure type annotation as Query (v0.90a5+)
@get_api(url='/users')
def list_users(self, page: int = 1, size: int = 10):
# page and size are automatically Query parameters
return {"page": page, "size": size}
# DynamicBody for flexible request body access
@post_api(url='/users')
def create_user(self, body: DynamicBody):
return self.response_factory(
status=200,
body={"name": body.name, "age": body.get('age', 0)}
)
if __name__ == '__main__':
# Framework-level entrypoint, no manual app instantiation required
application.run()
Run:
python app.py
# GET: http://localhost:4080/api/hello?name=Cullinan
# POST: http://localhost:4080/api/users {"name": "John", "age": 25}
For a more detailed onboarding, follow docs/getting_started.md (or docs/zh/getting_started.md).
💡 Dependency Injection Patterns
Cullinan ships with a core IoC/DI system. Recommended patterns:
1. InjectByName (recommended default)
from cullinan import service, Service
from cullinan.core import InjectByName
@service
class EmailService(Service):
def send_email(self, to, subject, body):
print(f"Sending email to {to}: {subject}")
return True
@service
class UserService(Service):
"""Service for user management with email dependency."""
# Name-based injection, no direct import needed
email_service = InjectByName('EmailService')
def create_user(self, name, email):
user = {'name': name, 'email': email}
self.email_service.send_email(email, "Welcome", f"Welcome {name}!")
return user
2. Inject + TYPE_CHECKING (IDE autocomplete)
from typing import TYPE_CHECKING
from cullinan import service, Service
from cullinan.core import Inject
if TYPE_CHECKING:
from services.email import EmailService
@service
class UserService(Service):
# Type-hinted injection for better IDE support
email_service: 'EmailService' = Inject()
def create_user(self, name, email):
self.email_service.send_email(email, "Welcome", f"Welcome {name}!")
return {"name": name, "email": email}
Controllers and RESTful decorators
from cullinan.controller import controller, get_api, post_api
from cullinan.core import InjectByName
from cullinan.params import Query, Body
@controller(url='/api')
class UserController:
# Inject the UserService by name
user_service = InjectByName('UserService')
# Type-safe query parameter (v0.90+)
@get_api(url='/users')
def get_user(self, id: Query(str)):
return self.response_factory(
status=200,
body={"message": "User fetched successfully", "user_id": id},
)
# Type-safe body parameters (v0.90+)
@post_api(url='/users')
def create_user(self, name: Body(str, required=True), email: Body(str, required=True)):
user = self.user_service.create_user(name, email)
return self.response_factory(
status=201,
body={"message": "User created successfully", "data": user},
)
Note: RESTful decorators are defined as
def get_api(**kwargs)etc. Only keyword arguments are supported. Use@get_api(url='/users'), not@get_api('/users').
For full parameter system documentation, see docs/parameter_system_guide.md.
More DI patterns and controller examples are documented in docs/wiki/injection.md and docs/wiki/restful_api.md (and their Chinese counterparts).
📖 More Examples
The examples/ folder contains additional runnable demos (HTTP, middleware, DI). Refer to:
examples/hello_http.py– minimal HTTP example using the handler registryexamples/controller_di_middleware.py– controller + DI + middleware integration
Each example is referenced from the docs so you can cross-check behavior with tests (tests/ directory).
📖 Additional Documentation
For advanced topics, see the docs:
- Configuration – environment and config options
- Packaging – building executables with Nuitka/PyInstaller
- Service Layer – service patterns and DI
- Registry Pattern – unified registry behavior
- Testing – running tests and using test registries
- Troubleshooting – common issues and diagnostics
🔗 Links
- Documentation: docs/README.md
- GitHub: https://github.com/plumeink/Cullinan
- PyPI: https://pypi.org/project/cullinan/
- Issues: https://github.com/plumeink/Cullinan/issues
- Discussions: https://github.com/plumeink/Cullinan/discussions
📄 License
MIT License – see LICENSE for details.
💻 Maintainer
Plumeink
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 cullinan-0.91.tar.gz.
File metadata
- Download URL: cullinan-0.91.tar.gz
- Upload date:
- Size: 320.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6e3a4f39613b6d831f86b8e8f7ede726aad0365a4d1151e33591bfe4616864e9
|
|
| MD5 |
712b48bc8b15e35e278301d5ac52faff
|
|
| BLAKE2b-256 |
924964e2d89ce8a30a554ecae03142901a00b93bde45e1e5b38a998e950d9959
|
File details
Details for the file cullinan-0.91-py3-none-any.whl.
File metadata
- Download URL: cullinan-0.91-py3-none-any.whl
- Upload date:
- Size: 219.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8b0437b668cd765e43d4aa6e8ec7a68a9f77b1ad0e6ae7a355aba5605e02f4b5
|
|
| MD5 |
018ef8b8bb8886ec927ac1126f4051c2
|
|
| BLAKE2b-256 |
f251f4ac29e72ad80131e119554a06041a0727b46e4059da6208b7895196aad8
|