Migrate to hatchling (#1095)

* Migrate to hatchling instead of poetry, ruff instead of flake8

* Migrate to hatchling instead of poetry, ruff instead of flake8

* Update tests suite

* venv?

* -m venv?

* Change dependencies

* Remove venv

* Change mypy config

* Added changelog

* Mark uvloop incompatible with pypy

* Update release script

* Use internal caching for dependencies

* Re-disable cov branches

* Added contributing guide
This commit is contained in:
Alex Root Junior 2023-01-12 02:49:58 +02:00 committed by GitHub
parent 04ccb390d5
commit f4ce4431f9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
58 changed files with 799 additions and 3001 deletions

View file

@ -1,5 +1,9 @@
from aiogram.dispatcher.flags import FlagGenerator
from contextlib import suppress
from aiogram.dispatcher.flags import FlagGenerator
from . import methods
from . import types
from . import enums
from .client import session
from .client.bot import Bot
from .dispatcher.dispatcher import Dispatcher
@ -9,12 +13,10 @@ from .utils.magic_filter import MagicFilter
from .utils.text_decorations import html_decoration as html
from .utils.text_decorations import markdown_decoration as md
try:
with suppress(ImportError):
import uvloop as _uvloop
_uvloop.install()
except ImportError: # pragma: no cover
pass
F = MagicFilter()
flags = FlagGenerator()
@ -24,6 +26,7 @@ __all__ = (
"__version__",
"types",
"methods",
"enums",
"Bot",
"session",
"Dispatcher",

View file

@ -310,10 +310,9 @@ class Bot(ContextInstanceMixin["Bot"]):
if isinstance(destination, (str, pathlib.Path)):
await self.__download_file(destination=destination, stream=stream)
return None
else:
return await self.__download_file_binary_io(
destination=destination, seek=seek, stream=stream
)
return await self.__download_file_binary_io(
destination=destination, seek=seek, stream=stream
)
finally:
if close_stream:
await stream.aclose()

View file

@ -48,18 +48,22 @@ def _retrieve_basic(basic: _ProxyBasic) -> Dict[str, Any]:
username = proxy_auth.login
password = proxy_auth.password
return dict(
proxy_type=proxy_type,
host=host,
port=port,
username=username,
password=password,
rdns=True,
)
return {
"proxy_type": proxy_type,
"host": host,
"port": port,
"username": username,
"password": password,
"rdns": True,
}
def _prepare_connector(chain_or_plain: _ProxyType) -> Tuple[Type["TCPConnector"], Dict[str, Any]]:
from aiohttp_socks import ChainProxyConnector, ProxyConnector, ProxyInfo # type: ignore
from aiohttp_socks import ( # type: ignore
ChainProxyConnector,
ProxyConnector,
ProxyInfo,
)
# since tuple is Iterable(compatible with _ProxyChain) object, we assume that
# user wants chained proxies if tuple is a pair of string(url) and BasicAuth
@ -74,7 +78,7 @@ def _prepare_connector(chain_or_plain: _ProxyType) -> Tuple[Type["TCPConnector"]
for basic in chain_or_plain:
infos.append(ProxyInfo(**_retrieve_basic(basic)))
return ChainProxyConnector, dict(proxy_infos=infos)
return ChainProxyConnector, {"proxy_infos": infos}
class AiohttpSession(BaseSession):

View file

@ -6,7 +6,17 @@ import json
from enum import Enum
from http import HTTPStatus
from types import TracebackType
from typing import TYPE_CHECKING, Any, AsyncGenerator, Callable, Final, Optional, Type, Union, cast
from typing import (
TYPE_CHECKING,
Any,
AsyncGenerator,
Callable,
Final,
Optional,
Type,
Union,
cast,
)
from pydantic import ValidationError
@ -165,8 +175,7 @@ class BaseSession(abc.ABC):
return str(round(value.timestamp()))
if isinstance(value, Enum):
return self.prepare_value(value.value)
else:
return str(value)
return str(value)
def clean_json(self, value: Any) -> Any:
"""
@ -174,7 +183,7 @@ class BaseSession(abc.ABC):
"""
if isinstance(value, list):
return [self.clean_json(v) for v in value if v is not None]
elif isinstance(value, dict):
if isinstance(value, dict):
return {k: self.clean_json(v) for k, v in value.items() if v is not None}
return value

View file

@ -52,7 +52,8 @@ class TelegramAPIServer:
file: str
"""Files URL"""
is_local: bool = False
"""Mark this server is in `local mode <https://core.telegram.org/bots/api#using-a-local-bot-api-server>`_."""
"""Mark this server is
in `local mode <https://core.telegram.org/bots/api#using-a-local-bot-api-server>`_."""
wrap_local_file: FilesPathWrapper = BareFilesPathWrapper()
"""Callback to wrap files path in local mode"""

View file

@ -107,13 +107,13 @@ class Dispatcher(Router):
return self.fsm.storage
@property
def parent_router(self) -> None:
def parent_router(self) -> Optional[Router]:
"""
Dispatcher has no parent router and can't be included to any other routers or dispatchers
:return:
"""
return None
return None # noqa: RET501
@parent_router.setter
def parent_router(self, value: Router) -> None:

View file

@ -9,7 +9,8 @@ class EventObserver:
"""
Simple events observer
Is used for managing events is not related with Telegram (For example startup/shutdown processes)
Is used for managing events is not related with Telegram
(For example startup/shutdown processes)
Handlers can be registered via decorator or method

View file

@ -94,7 +94,7 @@ def extract_flags(handler: Union["HandlerObject", Dict[str, Any]]) -> Dict[str,
handler = handler["handler"]
if not hasattr(handler, "flags"):
return {}
return handler.flags # type: ignore
return handler.flags
def get_flag(

View file

@ -1,7 +1,11 @@
import functools
from typing import Any, Callable, Dict, List, Optional, Sequence, Union, overload
from aiogram.dispatcher.event.bases import MiddlewareEventType, MiddlewareType, NextMiddlewareType
from aiogram.dispatcher.event.bases import (
MiddlewareEventType,
MiddlewareType,
NextMiddlewareType,
)
from aiogram.dispatcher.event.handler import CallbackType
from aiogram.types import TelegramObject

View file

@ -101,7 +101,7 @@ class Router:
if observer.handlers and update_name not in skip_events:
handlers_in_use.add(update_name)
return list(sorted(handlers_in_use))
return list(sorted(handlers_in_use)) # NOQA: C413
async def propagate_event(self, update_type: str, event: TelegramObject, **kwargs: Any) -> Any:
kwargs.update(event_router=self)

View file

@ -120,6 +120,7 @@ class ClientDecodeError(AiogramError):
original_type = type(self.original)
return (
f"{self.message}\n"
f"Caused from error: {original_type.__module__}.{original_type.__name__}: {self.original}\n"
f"Caused from error: "
f"{original_type.__module__}.{original_type.__name__}: {self.original}\n"
f"Content: {self.data}"
)

View file

@ -37,7 +37,8 @@ class Filter(ABC):
def update_handler_flags(self, flags: Dict[str, Any]) -> None:
"""
Also if you want to extend handler flags with using this filter you should implement this method
Also if you want to extend handler flags with using this filter
you should implement this method
:param flags: existing flags, can be updated directly
"""

View file

@ -3,7 +3,17 @@ from __future__ import annotations
from decimal import Decimal
from enum import Enum
from fractions import Fraction
from typing import TYPE_CHECKING, Any, ClassVar, Dict, Literal, Optional, Type, TypeVar, Union
from typing import (
TYPE_CHECKING,
Any,
ClassVar,
Dict,
Literal,
Optional,
Type,
TypeVar,
Union,
)
from uuid import UUID
from magic_filter import MagicFilter

View file

@ -22,7 +22,7 @@ class _MemberStatusMarker:
result = self.name.upper()
if self.is_member is not None:
result = ("+" if self.is_member else "-") + result
return result
return result # noqa: RET504
def __pos__(self: MarkerT) -> MarkerT:
return type(self)(name=self.name, is_member=True)
@ -38,7 +38,8 @@ class _MemberStatusMarker:
if isinstance(other, _MemberStatusGroupMarker):
return other | self
raise TypeError(
f"unsupported operand type(s) for |: {type(self).__name__!r} and {type(other).__name__!r}"
f"unsupported operand type(s) for |: "
f"{type(self).__name__!r} and {type(other).__name__!r}"
)
__ror__ = __or__
@ -52,7 +53,8 @@ class _MemberStatusMarker:
if isinstance(other, _MemberStatusGroupMarker):
return _MemberStatusTransition(old=old, new=other)
raise TypeError(
f"unsupported operand type(s) for >>: {type(self).__name__!r} and {type(other).__name__!r}"
f"unsupported operand type(s) for >>: "
f"{type(self).__name__!r} and {type(other).__name__!r}"
)
def __lshift__(
@ -64,7 +66,8 @@ class _MemberStatusMarker:
if isinstance(other, _MemberStatusGroupMarker):
return _MemberStatusTransition(old=other, new=new)
raise TypeError(
f"unsupported operand type(s) for <<: {type(self).__name__!r} and {type(other).__name__!r}"
f"unsupported operand type(s) for <<: "
f"{type(self).__name__!r} and {type(other).__name__!r}"
)
def __hash__(self) -> int:
@ -89,10 +92,11 @@ class _MemberStatusGroupMarker:
) -> MarkerGroupT:
if isinstance(other, _MemberStatusMarker):
return type(self)(*self.statuses, other)
elif isinstance(other, _MemberStatusGroupMarker):
if isinstance(other, _MemberStatusGroupMarker):
return type(self)(*self.statuses, *other.statuses)
raise TypeError(
f"unsupported operand type(s) for |: {type(self).__name__!r} and {type(other).__name__!r}"
f"unsupported operand type(s) for |: "
f"{type(self).__name__!r} and {type(other).__name__!r}"
)
def __rshift__(
@ -103,7 +107,8 @@ class _MemberStatusGroupMarker:
if isinstance(other, _MemberStatusGroupMarker):
return _MemberStatusTransition(old=self, new=other)
raise TypeError(
f"unsupported operand type(s) for >>: {type(self).__name__!r} and {type(other).__name__!r}"
f"unsupported operand type(s) for >>: "
f"{type(self).__name__!r} and {type(other).__name__!r}"
)
def __lshift__(
@ -114,7 +119,8 @@ class _MemberStatusGroupMarker:
if isinstance(other, _MemberStatusGroupMarker):
return _MemberStatusTransition(old=other, new=self)
raise TypeError(
f"unsupported operand type(s) for <<: {type(self).__name__!r} and {type(other).__name__!r}"
f"unsupported operand type(s) for <<: "
f"{type(self).__name__!r} and {type(other).__name__!r}"
)
def __str__(self) -> str:
@ -124,10 +130,7 @@ class _MemberStatusGroupMarker:
return result
def check(self, *, member: ChatMember) -> bool:
for status in self.statuses:
if status.check(member=member):
return True
return False
return any(status.check(member=member) for status in self.statuses)
class _MemberStatusTransition:

View file

@ -181,7 +181,7 @@ class Command(Filter):
await self.validate_mention(bot=bot, command=command)
command = self.validate_command(command)
command = self.do_magic(command=command)
return command
return command # noqa: RET504
def do_magic(self, command: CommandObject) -> Any:
if not self.magic:

View file

@ -84,7 +84,7 @@ class Text(Filter):
@classmethod
def _validate_constraints(cls, **values: Any) -> None:
# Validate that only one text filter type is presented
used_args = set(key for key, value in values.items() if value is not None)
used_args = {key for key, value in values.items() if value is not None}
if len(used_args) < 1:
raise ValueError(f"Filter should contain one of arguments: {set(values.keys())}")
if len(used_args) > 1:
@ -133,5 +133,4 @@ class Text(Filter):
def prepare_text(self, text: str) -> str:
if self.ignore_case:
return str(text).lower()
else:
return str(text)
return str(text)

View file

@ -3,7 +3,12 @@ from typing import Any, Awaitable, Callable, Dict, Optional, cast
from aiogram import Bot
from aiogram.dispatcher.middlewares.base import BaseMiddleware
from aiogram.fsm.context import FSMContext
from aiogram.fsm.storage.base import DEFAULT_DESTINY, BaseEventIsolation, BaseStorage, StorageKey
from aiogram.fsm.storage.base import (
DEFAULT_DESTINY,
BaseEventIsolation,
BaseStorage,
StorageKey,
)
from aiogram.fsm.strategy import FSMStrategy, apply_strategy
from aiogram.types import TelegramObject

View file

@ -6,7 +6,12 @@ from typing import Any, AsyncGenerator, DefaultDict, Dict, Hashable, Optional
from aiogram import Bot
from aiogram.fsm.state import State
from aiogram.fsm.storage.base import BaseEventIsolation, BaseStorage, StateType, StorageKey
from aiogram.fsm.storage.base import (
BaseEventIsolation,
BaseStorage,
StateType,
StorageKey,
)
@dataclass

View file

@ -2,7 +2,16 @@ from __future__ import annotations
import abc
import secrets
from typing import TYPE_CHECKING, Any, Dict, Generator, Generic, Optional, TypeVar, Union
from typing import (
TYPE_CHECKING,
Any,
Dict,
Generator,
Generic,
Optional,
TypeVar,
Union,
)
from pydantic import BaseConfig, BaseModel, Extra, root_validator
from pydantic.generics import GenericModel

View file

@ -5,7 +5,9 @@ from .audio import Audio
from .base import UNSET, TelegramObject
from .bot_command import BotCommand
from .bot_command_scope import BotCommandScope
from .bot_command_scope_all_chat_administrators import BotCommandScopeAllChatAdministrators
from .bot_command_scope_all_chat_administrators import (
BotCommandScopeAllChatAdministrators,
)
from .bot_command_scope_all_group_chats import BotCommandScopeAllGroupChats
from .bot_command_scope_all_private_chats import BotCommandScopeAllPrivateChats
from .bot_command_scope_chat import BotCommandScopeChat
@ -110,7 +112,9 @@ from .passport_element_error_front_side import PassportElementErrorFrontSide
from .passport_element_error_reverse_side import PassportElementErrorReverseSide
from .passport_element_error_selfie import PassportElementErrorSelfie
from .passport_element_error_translation_file import PassportElementErrorTranslationFile
from .passport_element_error_translation_files import PassportElementErrorTranslationFiles
from .passport_element_error_translation_files import (
PassportElementErrorTranslationFiles,
)
from .passport_element_error_unspecified import PassportElementErrorUnspecified
from .passport_file import PassportFile
from .photo_size import PhotoSize

View file

@ -5,7 +5,11 @@ from typing import TYPE_CHECKING, Any, List, Optional, Union
from pydantic import Field
from aiogram.utils.text_decorations import TextDecoration, html_decoration, markdown_decoration
from aiogram.utils.text_decorations import (
TextDecoration,
html_decoration,
markdown_decoration,
)
from ..enums import ContentType
from .base import UNSET, TelegramObject
@ -2465,7 +2469,7 @@ class Message(TelegramObject):
**kwargs,
)
def send_copy(
def send_copy( # noqa: C901
self: Message,
chat_id: Union[str, int],
disable_notification: Optional[bool] = None,
@ -2538,7 +2542,7 @@ class Message(TelegramObject):
if self.text:
return SendMessage(text=text, entities=entities, **kwargs)
elif self.audio:
if self.audio:
return SendAudio(
audio=self.audio.file_id,
caption=text,
@ -2548,29 +2552,29 @@ class Message(TelegramObject):
caption_entities=entities,
**kwargs,
)
elif self.animation:
if self.animation:
return SendAnimation(
animation=self.animation.file_id, caption=text, caption_entities=entities, **kwargs
)
elif self.document:
if self.document:
return SendDocument(
document=self.document.file_id, caption=text, caption_entities=entities, **kwargs
)
elif self.photo:
if self.photo:
return SendPhoto(
photo=self.photo[-1].file_id, caption=text, caption_entities=entities, **kwargs
)
elif self.sticker:
if self.sticker:
return SendSticker(sticker=self.sticker.file_id, **kwargs)
elif self.video:
if self.video:
return SendVideo(
video=self.video.file_id, caption=text, caption_entities=entities, **kwargs
)
elif self.video_note:
if self.video_note:
return SendVideoNote(video_note=self.video_note.file_id, **kwargs)
elif self.voice:
if self.voice:
return SendVoice(voice=self.voice.file_id, **kwargs)
elif self.contact:
if self.contact:
return SendContact(
phone_number=self.contact.phone_number,
first_name=self.contact.first_name,
@ -2578,7 +2582,7 @@ class Message(TelegramObject):
vcard=self.contact.vcard,
**kwargs,
)
elif self.venue:
if self.venue:
return SendVenue(
latitude=self.venue.location.latitude,
longitude=self.venue.location.longitude,
@ -2588,20 +2592,20 @@ class Message(TelegramObject):
foursquare_type=self.venue.foursquare_type,
**kwargs,
)
elif self.location:
if self.location:
return SendLocation(
latitude=self.location.latitude, longitude=self.location.longitude, **kwargs
)
elif self.poll:
if self.poll:
return SendPoll(
question=self.poll.question,
options=[option.text for option in self.poll.options],
**kwargs,
)
elif self.dice: # Dice value can't be controlled
if self.dice: # Dice value can't be controlled
return SendDice(**kwargs)
else:
raise TypeError("This type of message can't be copied.")
raise TypeError("This type of message can't be copied.")
def copy_to(
self,
@ -3066,9 +3070,10 @@ class Message(TelegramObject):
if self.chat.type in ("private", "group"):
return None
if not self.chat.username or force_private:
chat_value = f"c/{self.chat.shifted_id}"
else:
chat_value = self.chat.username
chat_value = (
f"c/{self.chat.shifted_id}"
if not self.chat.username or force_private
else self.chat.username
)
return f"https://t.me/{chat_value}/{self.message_id}"

View file

@ -15,7 +15,7 @@ def check_signature(token: str, hash: str, **kwargs: Any) -> bool:
:return:
"""
secret = hashlib.sha256(token.encode("utf-8"))
check_string = "\n".join(map(lambda k: f"{k}={kwargs[k]}", sorted(kwargs)))
check_string = "\n".join(f"{k}={kwargs[k]}" for k in sorted(kwargs))
hmac_string = hmac.new(
secret.digest(), check_string.encode("utf-8"), digestmod=hashlib.sha256
).hexdigest()

View file

@ -77,4 +77,7 @@ class Backoff:
self._next_delay = self.min_delay
def __str__(self) -> str:
return f"Backoff(tryings={self._counter}, current_delay={self._current_delay}, next_delay={self._next_delay})"
return (
f"Backoff(tryings={self._counter}, current_delay={self._current_delay}, "
f"next_delay={self._next_delay})"
)

View file

@ -23,7 +23,8 @@ class ChatActionSender:
Provides simply to use context manager.
Technically sender start background task with infinity loop which works
until action will be finished and sends the `chat action <https://core.telegram.org/bots/api#sendchataction>`_
until action will be finished and sends the
`chat action <https://core.telegram.org/bots/api#sendchataction>`_
every 5 seconds.
"""
@ -110,7 +111,7 @@ class ChatActionSender:
async with self._lock:
if not self.running:
return
if not self._close_event.is_set():
if not self._close_event.is_set(): # pragma: no branches
self._close_event.set()
await self._closed_event.wait()
self._task = None

View file

@ -96,7 +96,8 @@ class KeyboardBuilder(Generic[ButtonType]):
"""
if not isinstance(row, list):
raise ValueError(
f"Row {row!r} should be type 'List[{self._button_type.__name__}]' not type {type(row).__name__}"
f"Row {row!r} should be type 'List[{self._button_type.__name__}]' "
f"not type {type(row).__name__}"
)
if len(row) > MAX_WIDTH:
raise ValueError(f"Row {row!r} is too long (MAX_WIDTH={MAX_WIDTH})")
@ -114,7 +115,8 @@ class KeyboardBuilder(Generic[ButtonType]):
count = 0
if not isinstance(markup, list):
raise ValueError(
f"Markup should be type 'List[List[{self._button_type.__name__}]]' not type {type(markup).__name__!r}"
f"Markup should be type 'List[List[{self._button_type.__name__}]]' "
f"not type {type(markup).__name__!r}"
)
for row in markup:
self._validate_row(row)
@ -206,7 +208,8 @@ class KeyboardBuilder(Generic[ButtonType]):
By default, when the sum of passed sizes is lower than buttons count the last
one size will be used for tail of the markup.
If repeat=True is passed - all sizes will be cycled when available more buttons count than all sizes
If repeat=True is passed - all sizes will be cycled when available more buttons
count than all sizes
:param sizes:
:param repeat:

View file

@ -12,8 +12,9 @@ class AsFilterResultOperation(BaseOperation):
self.name = name
def resolve(self, value: Any, initial_value: Any) -> Any:
if value:
return {self.name: value}
if not value:
return None
return {self.name: value}
class MagicFilter(_MagicFilter):

View file

@ -81,13 +81,12 @@ class TextDecoration(ABC):
:param entities: Array of MessageEntities
:return:
"""
result = "".join(
return "".join(
self._unparse_entities(
add_surrogates(text),
sorted(entities, key=lambda item: item.offset) if entities else [],
)
)
return result
def _unparse_entities(
self,

View file

@ -17,7 +17,10 @@ class WebAppUser(TelegramObject):
"""
id: int
"""A unique identifier for the user or bot. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. It has at most 52 significant bits, so a 64-bit integer or a double-precision float type is safe for storing this identifier."""
"""A unique identifier for the user or bot. This number may have more than 32 significant bits
and some programming languages may have difficulty/silent defects in interpreting it.
It has at most 52 significant bits, so a 64-bit integer or a double-precision float type
is safe for storing this identifier."""
is_bot: Optional[bool] = None
"""True, if this user is a bot. Returns in the receiver field only."""
first_name: str
@ -29,24 +32,32 @@ class WebAppUser(TelegramObject):
language_code: Optional[str] = None
"""IETF language tag of the user's language. Returns in user field only."""
photo_url: Optional[str] = None
"""URL of the users profile photo. The photo can be in .jpeg or .svg formats. Only returned for Web Apps launched from the attachment menu."""
"""URL of the users profile photo. The photo can be in .jpeg or .svg formats.
Only returned for Web Apps launched from the attachment menu."""
class WebAppInitData(TelegramObject):
"""
This object contains data that is transferred to the Web App when it is opened. It is empty if the Web App was launched from a keyboard button.
This object contains data that is transferred to the Web App when it is opened.
It is empty if the Web App was launched from a keyboard button.
Source: https://core.telegram.org/bots/webapps#webappinitdata
"""
query_id: Optional[str] = None
"""A unique identifier for the Web App session, required for sending messages via the answerWebAppQuery method."""
"""A unique identifier for the Web App session, required for sending messages
via the answerWebAppQuery method."""
user: Optional[WebAppUser] = None
"""An object containing data about the current user."""
receiver: Optional[WebAppUser] = None
"""An object containing data about the chat partner of the current user in the chat where the bot was launched via the attachment menu. Returned only for Web Apps launched via the attachment menu."""
"""An object containing data about the chat partner of the current user in the chat where
the bot was launched via the attachment menu.
Returned only for Web Apps launched via the attachment menu."""
start_param: Optional[str] = None
"""The value of the startattach parameter, passed via link. Only returned for Web Apps when launched from the attachment menu via link. The value of the start_param parameter will also be passed in the GET-parameter tgWebAppStartParam, so the Web App can load the correct interface right away."""
"""The value of the startattach parameter, passed via link.
Only returned for Web Apps when launched from the attachment menu via link.
The value of the start_param parameter will also be passed in the GET-parameter
tgWebAppStartParam, so the Web App can load the correct interface right away."""
auth_date: datetime
"""Unix time when the form was opened."""
hash: str

View file

@ -88,7 +88,8 @@ class BaseRequestHandler(ABC):
) -> None:
"""
:param dispatcher: instance of :class:`aiogram.dispatcher.dispatcher.Dispatcher`
:param handle_in_background: immediately respond to the Telegram instead of waiting end of handler process
:param handle_in_background: immediately respond to the Telegram instead of
waiting end of handler process
"""
self.dispatcher = dispatcher
self.handle_in_background = handle_in_background
@ -166,7 +167,8 @@ class SimpleRequestHandler(BaseRequestHandler):
) -> None:
"""
:param dispatcher: instance of :class:`aiogram.dispatcher.dispatcher.Dispatcher`
:param handle_in_background: immediately respond to the Telegram instead of waiting end of handler process
:param handle_in_background: immediately respond to the Telegram instead of
waiting end of handler process
:param bot: instance of :class:`aiogram.client.bot.Bot`
"""
super().__init__(dispatcher=dispatcher, handle_in_background=handle_in_background, **data)
@ -184,7 +186,8 @@ class SimpleRequestHandler(BaseRequestHandler):
class TokenBasedRequestHandler(BaseRequestHandler):
"""
Handler that supports multiple bots, the context will be resolved from path variable 'bot_token'
Handler that supports multiple bots, the context will be resolved
from path variable 'bot_token'
"""
def __init__(
@ -196,7 +199,8 @@ class TokenBasedRequestHandler(BaseRequestHandler):
) -> None:
"""
:param dispatcher: instance of :class:`aiogram.dispatcher.dispatcher.Dispatcher`
:param handle_in_background: immediately respond to the Telegram instead of waiting end of handler process
:param handle_in_background: immediately respond to the Telegram instead of
waiting end of handler process
:param bot_settings: kwargs that will be passed to new Bot instance
"""
super().__init__(dispatcher=dispatcher, handle_in_background=handle_in_background, **data)