Add TypedDict support for middleware context data

Introduced `MiddlewareData` and associated TypedDicts to type-hint middleware context data. Updated documentation to include usage examples and guidelines for extending the default middleware data. Also adjusted coverage configuration to exclude the new data module.
This commit is contained in:
JRoot Junior 2025-03-02 01:08:29 +02:00
parent 8b4976b3de
commit 3232f6067e
No known key found for this signature in database
GPG key ID: 738964250D5FF6E2
4 changed files with 138 additions and 1 deletions

4
CHANGES/1637.feature.rst Normal file
View file

@ -0,0 +1,4 @@
Add TypedDict definitions for middleware context data to the dispatcher dependency injection docs.
So, now you can use :class:`aiogram.dispatcher.middleware.data.MiddlewareData` directly or
extend it with your own data in the middlewares.

View file

@ -0,0 +1,74 @@
from __future__ import annotations
from typing import TYPE_CHECKING, TypedDict
from typing_extensions import NotRequired
if TYPE_CHECKING:
from aiogram import Bot, Dispatcher, Router
from aiogram.dispatcher.event.handler import HandlerObject
from aiogram.dispatcher.middlewares.user_context import EventContext
from aiogram.fsm.context import FSMContext
from aiogram.fsm.storage.base import BaseStorage
from aiogram.types import Chat, Update, User
from aiogram.utils.i18n import I18n, I18nMiddleware
class DispatcherData(TypedDict, total=False):
"""
Dispatcher and bot related data.
"""
dispatcher: Dispatcher
bot: Bot
bots: list[Bot]
event_update: Update
event_router: Router
handler: NotRequired[HandlerObject]
class UserContextData(TypedDict, total=False):
"""
Event context related data about user and chat.
"""
event_context: EventContext
event_from_user: NotRequired[User]
event_chat: NotRequired[Chat] # Deprecated
event_thread_id: NotRequired[int] # Deprecated
event_business_connection_id: NotRequired[str] # Deprecated
class FSMData(TypedDict, total=False):
"""
FSM related data.
"""
fsm_storage: BaseStorage
state: NotRequired[FSMContext]
raw_state: NotRequired[str | None]
class I18nData(TypedDict, total=False):
"""
I18n related data.
Is not included by default, you need to add it to your own Data class if you need it.
"""
i18n: I18n
i18n_middleware: I18nMiddleware
class MiddlewareData(
DispatcherData,
UserContextData,
FSMData,
# I18nData, # Disabled by default, add it if you need it to your own Data class.
total=False,
):
"""
Data passed to the handler by the middlewares.
You can add your own data by extending this class.
"""

View file

@ -70,3 +70,61 @@ The last way is to return a dictionary from the filter:
.. literalinclude:: ../../examples/context_addition_from_filter.py
...or using :ref:`MagicFilter <magic-filters>` with :code:`.as_(...)` method.
Using type hints
================
.. note::
Type-hinting middleware data is optional and is not required for the correct operation of the dispatcher.
However, it is recommended to use it to improve the readability of the code.
You can use type hints to specify the type of the context data in the middlewares, filters and handlers.
The default middleware data typed dict can be found in :class:`aiogram.dispatcher.middlewares.data.MiddlewareData`.
In case when you have extended the context data, you can use the :class:`aiogram.dispatcher.middlewares.data.MiddlewareData` as a base class and specify the type hints for the new fields.
.. warning::
If you using type checking tools like mypy, you can experience warnings about that this type hint against Liskov substitution principle in due stricter type is not a subclass of :code:`dict[str, Any]`.
This is a known issue and it is not a bug. You can ignore this warning or use :code:`# type: ignore` comment.
Example of using type hints:
.. code-block:: python
from aiogram.dispatcher.middlewares.data import MiddlewareData
class MyMiddlewareData(MiddlewareData, total=False):
my_custom_value: int
class MyMessageMiddleware(BaseMiddleware):
async def __call__(
self,
handler: Callable[[Message, MiddlewareData], Awaitable[Any]],
event: Message,
data: MiddlewareData,
) -> Any:
bot = data["bot"] # <-- IDE will show you that data has `bot` key and its type is `Bot`
data["my_custom_value"] = 42 # <-- IDE will show you that you can set `my_custom_value` key with int value and warn you if you try to set it with other type
return await handler(event, data))
Available context data type helpers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. autoclass:: aiogram.dispatcher.middlewares.data.MiddlewareData
:members:
:undoc-members:
:member-order: bysource
.. autoclass:: aiogram.dispatcher.middlewares.data.I18nData
:members:
:undoc-members:
:member-order: bysource

View file

@ -261,7 +261,8 @@ filterwarnings = [
branch = false
parallel = true
omit = [
"aiogram/__about__.py",
"aiogram/__meta__.py",
"aiogram/dispatcher/middlewares/data.py"
]
[tool.coverage.report]