Callable | Awaitable in Lifecycle Handlers
#5878
Closed
AlePiccin
started this conversation in
Ideas / Feature Requests
Replies: 2 comments
-
|
See PR #5879 🚀 |
Beta Was this translation helpful? Give feedback.
0 replies
-
|
I'm closing this feature request as completed in version 3.10.0. ✔️ |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Summary
Several lifecycle registration APIs currently accept
Callable | Awaitable, for example:app.on_startup(...)app.on_shutdown(...)app.on_connect(...)app.on_disconnect(...)app.on_delete(...)client.on_*hooksAfter reviewing the codebase, I found that we only pass a direct
Awaitablein two places, both in tests:tests/test_lifecycle.py#L91:app.on_startup(startup_async())tests/test_lifecycle.py#L94:app.on_shutdown(shutdown_async())Every other usage I found passes a callable:
So the
Awaitableside of the API appears to be barely used, and only in two easy-to-fix cases.Current API Shape
In the original code, the lifecycle registration methods accept
Callable | Awaitable:That broader type then leaks into execution paths like this:
and:
This means the implementation has to keep supporting both:
———
Where We Actually Pass an Awaitable
I found only these two direct uses:
app.on_startup(startup_async())app.on_shutdown(shutdown_async())Source:
The equivalent callable versions already exist right next to them:
app.on_startup(startup_async)app.on_shutdown(shutdown_async)So the current Awaitable support is not only rarely used, but also unnecessary in the codebase as it stands.
———
Why This Feels Wrong
1. Lifecycle hooks are conceptually handlers, not already-created awaitables
A lifecycle registration API reads like "register something to be invoked later". That strongly suggests a callable contract.
Passing an already-created awaitable means:
That is especially awkward for hooks like on_connect, on_disconnect, and on_delete, which can run many times.
2. Awaitable makes the API broader than the real use case
For repeated lifecycle hooks, accepting Awaitable is arguably the wrong abstraction:
So for connect / disconnect / delete, the type is not just unnecessary, but misleading.
3. It complicates the implementation
Because the API accepts both shapes, execution code has to
branch:
This adds complexity in:
invocation
If these APIs were callable-only, the control flow would become simpler and easier to reason about.
4. It weakens the mental model
For users and maintainers, this is easier to explain:
That is much clearer than:
———
Why Changing This Looks Safe
From the current codebase review:
The fixes are straightforward:
app.on_startup(startup_async)
app.on_shutdown(shutdown_async)
instead of:
app.on_startup(startup_async())
app.on_shutdown(shutdown_async())
So the internal migration cost looks very low.
The benefits are:
In short: the current type is broader than the actual need, barely exercised in practice, and easy to remove from internal usages.
———
Questions for Maintainers
I think these are the right questions to ask before changing the API:
———
Recommendation
I would recommend:
API.
Given the current repository usage, this looks like a small change with a good clarity payoff.
I'd be happy to submit an PR.
Beta Was this translation helpful? Give feedback.
All reactions