Loading repository data…
Loading repository data…
cullinan-py / repository
A business-logic-first Python web framework built atop Tornado/ASGI, featuring decorator-driven development, modular boundaries, and built-in IoC/DI for scalable applications.
_____ _ _ _
/ ____| | | (_)
| | _ _| | |_ _ __ __ _ _ __
| | | | | | | | | '_ \ / _` | '_ \
| |___| |_| | | | | | | | (_| | | | |
\_____\__,_|_|_|_|_| |_|\__,_|_| |_|
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 @application + @configure(...),
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.
Cullinan is designed to make the framework work around your business architecture, not the other way around.
@module to express ownership and runtime boundariesCullinan 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.
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.
@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.
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.
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.
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| Capability layer | What Cullinan provides |
|---|---|
| Application composition | @application + @configure(...), 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.
Cullinan is published on PyPI and currently supports Python 3.9+.
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.
The recommended starting point is: define business components, decorate them,
and declare the application entry with @configure(...) + @application + main().
from cullinan import application, configure
from cullinan.core import 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 # constructor injection: one line is all you need
@get_api(url="")
def hello(self):
return {"message": self.greeting_service.greet()}
@configure(user_packages=["minimal_app"], server_port=4080)
@application
def main(): ...
Run it:
python -m minimal_app
Then open:
http://localhost:4080/hello
This example shows the default Cullinan path:
@application + @configure(...) + main() as the startup flow@module available when explicit runtime boundaries are neededIf you are new to Cullinan, use this order:
examples/minimal_app/ - shortest public entrypointexamples/controller_service_inject/ - service/controller layering with Inject()examples/middleware_and_module/ - @module boundaries and middleware semanticsexamples/parameter_handling/ - method-level Path, Query, and Bodyexamples/testing_flow/ - public-API testing with get_asgi_app()Then move into the documentation knowledge base:
docs/README.md - English knowledge base homedocs/zh/README.md - Chinese knowledge base homedocs/getting_started.md - recommended application entrydocs/framework_semantics.md - framework concepts and semanticsdocs/examples.md - example navigation pagev0.94a1 is the Phase A convergence cut for the 1.0 roadmap. This release line locks down the public surface and packaging baseline around:
@application + @configure(...) + main() as the startup flow@module as the structured boundary for runtime ownershipcullinan API, with
advanced semantic namespaces under cullinan.application, cullinan.web,
and cullinan.corepyproject.toml + setuptools.build_meta packaging with setup.py kept as a compatibility shimcullinan/py.typedVersion-specific details and migration notes live in the documentation knowledge base rather than in the homepage narrative.
MIT License - see LICENSE for details.