You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Inject dependencies into CLI commands, background tasks, event handlers, or any standalone function that needs container access.
188
+
### 🔑 The `@injectable` API
189
+
190
+
Instead of separate registration APIs for services, factories, resources, and async resources, Wireup's `@injectable` API uses standard Python constructs to determine how a dependency behaves.
Wireup keeps the API small, but it is built for larger application graphs.
241
+
242
+
| Need | Wireup |
243
+
| --- | --- |
244
+
| One shared instance |[`@injectable`](https://maldoinc.github.io/wireup/latest/injectables/) / `lifetime="singleton"`|
245
+
| Per request, job, command, handler, or WebSocket |[`@injectable(lifetime="scoped")`](https://maldoinc.github.io/wireup/latest/lifetimes_and_scopes/)|
246
+
| Fresh instance each resolution |[`@injectable(lifetime="transient")`](https://maldoinc.github.io/wireup/latest/lifetimes_and_scopes/)|
247
+
| Setup and cleanup |[`yield` from a sync or async factory](https://maldoinc.github.io/wireup/latest/resources/)|
248
+
| Configuration | Both [`Inject(config=...)`](https://maldoinc.github.io/wireup/latest/configuration/) values and injectable settings objects |
249
+
| Register an already-created object |[`wireup.instance(...)`](https://maldoinc.github.io/wireup/latest/injectables/)|
250
+
| Dynamic function injection and injectable lookup | Inject the [root or active scoped container](https://maldoinc.github.io/wireup/latest/container/#injecting-the-container)|
251
+
| Interfaces and protocols |[`as_type=...`](https://maldoinc.github.io/wireup/latest/interfaces/) or factory return annotations |
| Modular or parametrized registration |[Functions that return injectables](https://maldoinc.github.io/wireup/latest/reusable_bundles/)|
258
+
| Optional or conditional dependencies |[Params with defaults are skipped when unregistered; factories can return `T \| None`](https://maldoinc.github.io/wireup/latest/injectables/#optional-dependencies-and-default-values)|
259
+
260
+
261
+
262
+
### 🎯 Function Injection
263
+
264
+
Inject dependencies into CLI commands, background tasks, event handlers, or any standalone function that needs container access.
Declare dependencies as `singleton`, `scoped`, or`transient` to control reuse explicitly.
308
+
Wireup has three lifetimes: `singleton`, `scoped`, and`transient`, plus explicit scope forking from root for unit-of-work patterns like batch jobs, fan-out tasks, and multi-tenant request processing.
248
309
249
310
```python
250
311
# Singleton: one instance per application (default)
251
312
@injectable
252
313
classSettings:
253
314
pass
254
315
255
-
# Async singleton with cleanup — no lru_cache, no app.state
See [Interfaces & Qualifiers](https://maldoinc.github.io/wireup/latest/interfaces/) for collection injection (`Sequence[T]`, `Mapping[K, T]`) and more.
382
+
383
+
### 🔀 Fan-out & Isolated Worker Scopes
384
+
385
+
Wireup uses **isolated scopes with explicit context sharing** rather than nested scope inheritance. Each worker scope gets exactly the context it needs without implicit leakage of the parent's full graph.
386
+
387
+
The container is injectable too, so you never need a global reference or `app.state` to create child scopes.
388
+
389
+
```python
390
+
@app.post("/batch")
391
+
asyncdefprocess_batch(
392
+
doc_ids: list[str],
393
+
container: Injected[wireup.AsyncContainer],
394
+
ctx: Injected[TenantContext],
395
+
) -> list[Result]:
396
+
# TenantContext is shared while everything else is isolated per worker.
397
+
asyncdefprocess_one(doc_id: str) -> Result:
398
+
asyncwith container.enter_scope({TenantContext: ctx}) as scope:
399
+
# DocumentService and all its dependencies (db connections, transactions, etc.)
returnawait asyncio.gather(*[process_one(doc_id) for doc_id in doc_ids])
405
+
```
406
+
407
+
Because child scopes don't silently inherit the parent graph, parallel workers can never accidentally share or corrupt each other's state.
408
+
409
+
See [Lifetimes & Scopes](https://maldoinc.github.io/wireup/latest/lifetimes_and_scopes/) for the full model.
410
+
273
411
### 🛡️ Startup Validation
274
412
275
413
Wireup validates the dependency graph when the container is created. See <ahref="https://maldoinc.github.io/wireup/latest/what_wireup_validates/">What Wireup Validates</a> for the full rules and limits.
@@ -299,10 +437,11 @@ with container.override.injectable(target=Database, new=in_memory_database):
299
437
response = client.get("/users")
300
438
```
301
439
440
+
302
441
## 📚 Documentation
303
442
304
443
See the docs for integrations, lifetimes, factories, testing, and more advanced patterns.
0 commit comments