Skip to main content

Cullinan — A pluggable IoC/DI web framework

Project description

Python version PyPI version PyPI downloads Ask DeepWiki GitHub stars License

   _____      _ _ _
  / ____|    | | (_)
 | |    _   _| | |_ _ __   __ _ _ __
 | |   | | | | | | | '_ \ / _` | '_ \
 | |___| |_| | | | | | | | (_| | | | |
  \_____\__,_|_|_|_|_| |_|\__,_|_| |_|

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 cullinan.application.configure(root_module=...) and cullinan.application.run(), with @module used as the application boundary when ownership, runtime structure, and stability matter. The root namespace is now a thin gateway to the final framework surface rather than a place where historical wrapper files continue to coexist.


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 @module to express ownership and runtime boundaries
  • Built-in IoC/DI and lifecycle - keep application wiring inside the framework model
  • 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.application - application definition, configuration, and startup
  • cullinan.web - controllers, route decorators, request/response, parameters, middleware
  • cullinan.core - IoC/DI, lifecycle, context, and semantic rules
  • cullinan.testing - test-facing helpers
  • cullinan.runtime / cullinan.transport - advanced discovery and adapter boundaries
  • cullinan.support - constrained support utilities, not a second public app path

Framework capabilities

Capability layer What Cullinan provides
Application composition cullinan.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.


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.application 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."""


def create_app():
    return configure(root_module=RootModule)


if __name__ == "__main__":
    create_app()
    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
  • @module as the runtime boundary
  • cullinan.application.configure(root_module=...) plus run() as the startup flow

Learning path

If you are new to Cullinan, use this order:

  1. examples/minimal_app/ - shortest public entrypoint
  2. examples/controller_service_inject/ - service/controller layering with Inject()
  3. examples/middleware_and_module/ - @module boundaries and middleware semantics
  4. examples/parameter_handling/ - method-level Path, Query, and Body
  5. examples/testing_flow/ - public-API testing with get_asgi_app()

Then move into the documentation knowledge base:


Documentation

English

中文


Current series

v0.93a6.post1 focuses the public application path around:

  • configure(root_module=...) and run()
  • decorator-first discovery through Python imports
  • @module as the structured boundary for runtime ownership
  • built-in IoC/DI, lifecycle, and semantic diagnostics
  • unified example and documentation guidance around the current public API
  • consistency fixes for high-level public runtime entrypoints

Version-specific details and migration notes live in the documentation knowledge base rather than in the homepage narrative.


Project links


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

cullinan-0.93a7.tar.gz (201.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cullinan-0.93a7-py3-none-any.whl (249.9 kB view details)

Uploaded Python 3

File details

Details for the file cullinan-0.93a7.tar.gz.

File metadata

  • Download URL: cullinan-0.93a7.tar.gz
  • Upload date:
  • Size: 201.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for cullinan-0.93a7.tar.gz
Algorithm Hash digest
SHA256 056dbdf4df897b385858e7b2d73e0fa967747f9c72528d65c26c3f6e23125249
MD5 7159623cbeb03b86e91ec7f9f53942d9
BLAKE2b-256 9f60e0ecb643b02ff9bf3c4413d4a6f810f07f89ad3dd38858ec52bc668d0826

See more details on using hashes here.

File details

Details for the file cullinan-0.93a7-py3-none-any.whl.

File metadata

  • Download URL: cullinan-0.93a7-py3-none-any.whl
  • Upload date:
  • Size: 249.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for cullinan-0.93a7-py3-none-any.whl
Algorithm Hash digest
SHA256 dc491b5e2c841ab3e184d2bb232b11ea4e222d21649d6cac9809b8221082037e
MD5 5f4c22c735f32d3d9fe82a12869ee786
BLAKE2b-256 5aeea24e23f377dc3f093317b8ae1538e1487a690a5d280017757333bf23f29a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page