Внедрение зависимостей¶
Внедрение зависимостей — это техника программирования, которая делает классы независимыми от их зависимостей. Оно достигается путем отделения использования объекта от его создания. Это помогает соблюдать принципы инверсии зависимостей и единой ответственности по SOLID.
Как это работает в litegram¶
Для каждого обновления litegram.dispatcher.dispatcher.Dispatcher передает данные контекста обработки. Фильтры и промежуточное ПО также могут вносить изменения в контекст.
Чтобы получить доступ к контекстным данным, вы должны указать соответствующий ключевой параметр в обработчике или фильтре. Например, чтобы получить litegram.fsm.context.FSMContext, мы делаем это так:
@router.message(ProfileCompletion.add_photo, F.photo)
async def add_photo(
message: types.Message, bot: Bot, state: FSMContext
) -> Any:
... # do something with photo
Внедрение собственных зависимостей¶
Litegram предоставляет несколько способов дополнения/модификации контекстных данных.
Первый и самый простой способ — это просто указать именованные аргументы во время инициализации litegram.dispatcher.dispatcher.Dispatcher, запуска методов опроса или инициализации litegram.webhook.litestar_server.SimpleRequestHandler, если вы используете вебхуки.
async def main() -> None:
dp = Dispatcher(..., foo=42)
return await dp.start_polling(
bot, bar="Bazz"
)
Аналогия для вебхука:
async def main() -> None:
dp = Dispatcher(..., foo=42)
handler = SimpleRequestHandler(dispatcher=dp, bot=bot, bar="Bazz")
... # starting webhook
Данные рабочего процесса litegram.dispatcher.dispatcher.Dispatcher также могут дополняться путем установки значений, как в словаре:
dp = Dispatcher(...)
dp["eggs"] = Spam()
Промежуточное ПО достаточно часто обновляет контекст. Вы можете прочитать об этом больше на этой странице:
Последний способ — вернуть словарь из фильтра:
from __future__ import annotations
from typing import TYPE_CHECKING, Any
from litegram import Router
from litegram.filters import Filter
if TYPE_CHECKING:
from litegram.types import Message, User
router = Router(name=__name__)
class HelloFilter(Filter):
def __init__(self, name: str | None = None) -> None:
self.name = name
async def __call__(
self,
message: Message,
event_from_user: User,
# Filters also can accept keyword parameters like in handlers
) -> bool | dict[str, Any]:
if message.text.casefold() == "hello":
# Returning a dictionary that will update the context data
return {"name": event_from_user.mention_html(name=self.name)}
return False
@router.message(HelloFilter())
async def my_handler(
message: Message,
name: str, # Now we can accept "name" as named parameter
) -> Any:
return message.answer(f"Hello, {name}!")
…или используя MagicFilter с методом .as_(...).
Использование аннотаций типов¶
Примечание
Использование аннотаций типов для данных промежуточного ПО необязательно и не требуется для корректной работы диспетчера. Однако рекомендуется использовать их для улучшения читаемости кода.
Вы можете использовать аннотации типов для определения типа контекстных данных в промежуточном ПО, фильтрах и обработчиках.
Словарь типов данных по умолчанию для промежуточного ПО можно найти в litegram.dispatcher.middlewares.data.MiddlewareData.
В случае если вы расширили данные контекста, вы можете использовать litegram.dispatcher.middlewares.data.MiddlewareData как базовый класс и указать аннотации типов для новых полей.
Предупреждение
Если вы используете инструменты проверки типов, такие как mypy, вы можете получить предупреждение о том, что эти аннотации типов нарушают принцип подстановки Лисков из-за того, что более строгий тип не является подклассом dict[str, Any]. Это известная проблема, и она не является ошибкой. Вы можете проигнорировать это предупреждение или использовать комментарий # type: ignore.
Пример использования аннотаций типов:
from litegram.dispatcher.middlewares.data import MiddlewareData
class MyMiddlewareData(MiddlewareData, total=False):
my_custom_value: int
class MyMessageMiddleware(BaseMiddleware):
async def __call__(
self,
handler: Callable[[Message, MyMiddlewareData], Awaitable[Any]],
event: Message,
data: MyMiddlewareData,
) -> Any:
bot = data["bot"] # <-- IDE will show you that data has `bot` key and its type is `Bot`
data["my_custom_value"] = bot.id * 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)
Доступные помощники для типов контекстных данных¶
- class litegram.dispatcher.middlewares.data.MiddlewareData[исходный код]¶
Данные, передаваемые обработчику промежуточным ПО.
Вы можете добавить собственные данные, расширив этот класс.
- dispatcher: Dispatcher¶
- bot: Bot¶
- handler: NotRequired[HandlerObject]¶
- event_context: EventContext¶
- fsm_storage: BaseStorage¶
- state: NotRequired[FSMContext]¶
- class litegram.dispatcher.middlewares.data.I18nData[исходный код]¶
Данные, связанные с I18n.
По умолчанию не включено, вам нужно добавить это в собственный класс данных, если это необходимо.
- i18n: I18n¶
Объект I18n.
- i18n_middleware: I18nMiddleware¶
Промежуточное ПО для I18n.