An Installable is a small object that wires one application subsystem into Sillo. It is the right boundary for a component that needs application state, request middleware, startup or shutdown work, or a combination of them.
from sillo import SilloApp
app = SilloApp()metrics = app.install(Metrics("https://metrics.example"))Use app.install() for subsystems. Keep app.use() for HTTP middleware whose
order matters, and app.on_startup() / app.on_shutdown() for one-off work
owned by the application itself.
The contract
Section titled “The contract”An installable has a stable, non-empty name and an install(app) method.
The method returns the service the application will use.
from sillo.application import SilloApp
class Metrics: name = "metrics"
def __init__(self, endpoint: str) -> None: self.endpoint = endpoint
def install(self, app: SilloApp) -> MetricsClient: client = MetricsClient(self.endpoint) app.state[self.name] = client app.on_startup(client.connect) app.on_shutdown(client.close) return clientInstall it during bootstrap:
app = SilloApp()metrics = app.install(Metrics(settings.metrics_endpoint))Sillo records the returned object in app.installations under the installable
name. The view is read-only and is intended for diagnostics and tooling; use
the subsystem’s normal API in handlers rather than treating it as a general
service locator.
Idempotency and names
Section titled “Idempotency and names”app.install() is idempotent by name. A second install with the same name
returns the service from the first call and does not add duplicate middleware
or lifecycle handlers.
first = app.install(Metrics("https://metrics.example"))second = app.install(Metrics("https://other.example"))
assert first is secondChoose a package-qualified name for reusable third-party integrations, such as
"acme.metrics", to avoid colliding with another installable. Treat the first
configuration as authoritative; if configuration must change, create a new app
for the new configuration rather than relying on a repeated installation.
Request middleware
Section titled “Request middleware”An installable may add its own middleware when that middleware is intrinsic to the subsystem:
class TenantStore: name = "tenant_store"
def install(self, app: SilloApp) -> Store: store = Store() app.state[self.name] = store app.use(store.bind_request) return storeapp.use() retains its normal ordering rules. Installables should document any
ordering requirement they introduce. Do not hide unrelated application
middleware inside an installable merely to shorten bootstrap code.
Built-in installables
Section titled “Built-in installables”Sillo provides Record, Mail, StorageInstallable, Work, and Scheduler.
The existing setup_record, setup_mail, setup_storage, setup_work, and
setup_scheduler helpers remain available and use the same installation path,
so applications can adopt the object form gradually. New integrations should
expose an Installable first; a setup_* helper is only a compatibility
wrapper for projects using the older bootstrap style.