Cullinan — A pluggable IoC/DI web framework
Project description
_____ _ _ _
/ ____| | | (_)
| | _ _| | |_ _ __ __ _ _ __
| | | | | | | | | '_ \ / _` | '_ \
| |___| |_| | | | | | | | (_| | | | |
\_____\__,_|_|_|_|_| |_|\__,_|_| |_|
Cullinan
A business-first Python web framework for building applications through decorators, module boundaries, and built-in IoC/DI.
Cullinan is a Python Web Framework for developers who want to organize applications around
business services, controllers, and methods instead of manually wiring an app object.
Its default path is decorator-first discovery plus from cullinan import configure, module, run,
with @module used as the application boundary when ownership, runtime structure, and stability
matter. The framework-facing API stays engine-neutral:
Cullinan internally bridges your business application into Tornado or ASGI runtimes instead of
making a concrete server framework the primary developer mental model.
What is Cullinan?
Cullinan is designed to make the framework work around your business architecture, not the other way around.
- Business-first application model - write services, controllers, middleware, and methods first
- Decorator-first discovery - let Python imports and decorator metadata assemble the runtime
- Structured module boundaries - use
@moduleto express ownership and runtime boundaries - Built-in IoC/DI and lifecycle - keep application wiring inside the framework model
- Engine-neutral Web facade - build against Cullinan semantics while the framework adapts to Tornado or ASGI internally
- Public semantic path - build, run, test, and extend applications through stable public APIs
Cullinan is not centered on treating an app object as a manual registration hub. The recommended development flow is to declare business components, define a root module when structure matters, and let the framework assemble the application runtime.
Why Cullinan?
1. Framework semantics before manual wiring
Cullinan gives you a clear application model: declare components with decorators, let the framework discover them, and keep explicit runtime internals as an advanced path rather than the default onboarding path.
2. Application boundaries that scale past toy examples
@module is more than a naming convention. It is the structured boundary for owned packages,
runtime composition, and higher-level lifecycle behavior when your application grows.
3. Web, DI, lifecycle, and testing in one model
Routing, parameter binding, dependency injection, lifecycle hooks, middleware, and test flow all sit inside one framework vocabulary instead of forcing developers to stitch together multiple unrelated patterns.
4. A Pythonic path for business applications
Cullinan is designed to keep developers focused on business architecture and business methods. You can stay on the public path for most application work and only move into internals when you intentionally need advanced extension behavior.
5. A clearer semantic package surface
Cullinan now exposes a clearer framework-shaped package surface:
cullinan- recommended public startup and declaration API for application codecullinan.web- controllers, route decorators, request/response, parameters, middlewarecullinan.core- IoC/DI, lifecycle, context, and semantic rulescullinan.application- advanced application semantics for maintainers and framework-aware integrationscullinan.testing- test-facing helperscullinan.runtime/cullinan.transport- advanced discovery and adapter boundariescullinan.support- constrained support utilities, not a second public app path
Framework capabilities
| Capability layer | What Cullinan provides |
|---|---|
| Application composition | top-level configure(...) / run(), decorator-driven discovery, structured runtime assembly |
| Web API model | cullinan.web facade for @controller, RESTful decorators, WebResponse, parameters, middleware |
| Parameter system | Typed Path, Query, Body, validation, conversion, and controller-method binding |
| IoC/DI and lifecycle | Inject(), InjectByName(), request scope, startup/shutdown hooks, component lifecycle |
| Runtime boundaries | @module ownership, clearer package structure, better runtime organization |
| Testing and delivery | get_asgi_app(), resettable registries, packaging-friendly runtime, cross-platform support |
Cullinan's goal is not to expose every internal knob on the README homepage. The goal is to provide a coherent framework model that remains readable from first contact through production use.
Install
Cullinan is published on PyPI and currently supports Python 3.7+.
pip install -U pip
pip install cullinan
After installation, start from the minimal example in examples/minimal_app/
or the repository guide in docs/examples.md.
Quick Start
The recommended starting point is: define business components, declare the root boundary, configure the app, then run it.
from cullinan import configure, module, run
from cullinan.core import Inject, service
from cullinan.web import controller, get_api
@service
class GreetingService:
def greet(self) -> str:
return "Hello from Cullinan!"
@controller(url="/hello")
class HelloController:
greeting_service: "GreetingService" = Inject()
@get_api(url="")
def hello(self):
return {"message": self.greeting_service.greet()}
@module
class RootModule:
"""Declare the application boundary when structure matters."""
if __name__ == "__main__":
configure(root_module=RootModule)
run()
Run it:
python minimal_app.py
Then open:
http://localhost:4080/hello
This example shows the default Cullinan path:
- business service first
- controller methods as the public web surface
Inject()for framework-managed wiring@moduleas the runtime boundary- top-level
configure(root_module=...)plusrun()as the startup flow - runtime backend selection delegated to Cullinan instead of driving application structure
Learning path
If you are new to Cullinan, use this order:
examples/minimal_app/- shortest public entrypointexamples/controller_service_inject/- service/controller layering withInject()examples/middleware_and_module/-@moduleboundaries and middleware semanticsexamples/parameter_handling/- method-levelPath,Query, andBodyexamples/testing_flow/- public-API testing withget_asgi_app()
Then move into the documentation knowledge base:
docs/README.md- English knowledge base homedocs/zh/README.md- 中文知识库首页docs/getting_started.md- recommended application entrydocs/framework_semantics.md- framework concepts and semanticsdocs/examples.md- example navigation page
Documentation
English
- Documentation home
- Getting Started
- Framework Semantics
- Examples
- Dependency Injection Guide
- Web Runtime Guide
- Parameter System Guide
- Testing & Verification
中文
Current series
v0.93a8 makes the public application path more explicit around:
- top-level
configure(root_module=...)andrun() - decorator-first discovery through Python imports
@moduleas the structured boundary for runtime ownership- built-in IoC/DI, lifecycle, and semantic diagnostics
- a semantic package surface centered on the top-level
cullinanAPI, with advanced semantic namespaces undercullinan.application,cullinan.web, andcullinan.core - engine-neutral runtime selection over Tornado / ASGI backends
Version-specific details and migration notes live in the documentation knowledge base rather than in the homepage narrative.
Project links
- 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.
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.93a9.tar.gz.
File metadata
- Download URL: cullinan-0.93a9.tar.gz
- Upload date:
- Size: 199.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
44dbcaa2873fbe9bf43dea0dc2b99edec9974f78bb77f57af57b45eaee9cc337
|
|
| MD5 |
4aca2c3b0cc092ff8be51fc50d249295
|
|
| BLAKE2b-256 |
ab3983921be76dc7e5c7b945209d8a47ad7bdb967f5cfd48f73b6eb66aded85c
|
File details
Details for the file cullinan-0.93a9-py3-none-any.whl.
File metadata
- Download URL: cullinan-0.93a9-py3-none-any.whl
- Upload date:
- Size: 247.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e12ebfb77c9e070762535a29873fb2221e0f3be6097e72582f6ded14e3744a3f
|
|
| MD5 |
1ed588a44ef9ba1b7075575648ba96f8
|
|
| BLAKE2b-256 |
b0433385296f8dc9d4e4b86fe02b6410ed66338806e7cbdab6ac35d44bdffcec
|