Migrate from Black to Ruff (#1750)

* Migrate from Black to Ruff and reformat code with enabling additional linter checks

* Add changelog for migration to Ruff as formatter and linter

* Add type ignores for specific attributes and replace tuple with set for chat type check

* Remove file from another changes
This commit is contained in:
Alex Root Junior 2026-01-04 21:34:08 +02:00 committed by GitHub
parent a4a3f42c71
commit ec7da0f678
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
214 changed files with 886 additions and 964 deletions

View file

@ -72,7 +72,7 @@ jobs:
run: |
uv run ruff check --output-format=github aiogram examples
uv run mypy aiogram
uv run black --check --diff aiogram tests
uv run ruff format --check --diff aiogram tests scripts examples
- name: Setup redis
if: ${{ env.IS_WINDOWS == 'false' }}

View file

@ -13,13 +13,10 @@ repos:
- id: "check-toml"
- id: "check-json"
- repo: https://github.com/psf/black
rev: 25.9.0
hooks:
- id: black
files: &files '^(aiogram|tests|examples)'
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: 'v0.14.0'
hooks:
- id: ruff
files: &files '^(aiogram|tests|examples)'
- id: ruff-format
files: *files

24
CHANGES/1750.misc.rst Normal file
View file

@ -0,0 +1,24 @@
Migrated from Black and isort to Ruff for code formatting and linting, a modern, blazingly fast formatter and linter written in Rust.
Enabled additional ruff rule sets.
**For end users:**
No changes required. This is purely a development tooling change that doesn't affect the library API or behavior.
**For contributors:**
- Use ``make reformat`` or ``uv run ruff format`` to format code (replaces ``black`` and ``isort``)
- Use ``make lint`` to check code quality (now includes formatting, linting, and type checking)
- Pre-commit hooks automatically updated to use ``ruff`` and ``ruff-format``
- CI/CD pipelines updated to use ruff in GitHub Actions workflows
**Benefits:**
- 10-100x faster formatting and linting compared to Black + isort + flake8
- Single tool for formatting, import sorting, and linting
- More comprehensive code quality checks out of the box
- Auto-fixes for many common issues (33 issues auto-fixed during migration)
- Better integration with modern Python development workflows
This change improves the developer experience and code quality while maintaining the same code style standards.

View file

@ -37,15 +37,14 @@ install: clean
.PHONY: lint
lint:
uv run isort --check-only $(code_dir)
uv run black --check --diff $(code_dir)
uv run ruff format --check --diff $(code_dir)
uv run ruff check --show-fixes --preview $(package_dir) $(examples_dir)
uv run mypy $(package_dir)
.PHONY: reformat
reformat:
uv run black $(code_dir)
uv run isort $(code_dir)
uv run ruff format $(code_dir)
uv run ruff check --fix $(code_dir)
# =================================================================================================
# Tests

View file

@ -2,12 +2,11 @@ from __future__ import annotations
import io
import pathlib
from collections.abc import AsyncGenerator, AsyncIterator
from contextlib import asynccontextmanager
from types import TracebackType
from typing import (
Any,
AsyncGenerator,
AsyncIterator,
BinaryIO,
TypeVar,
cast,
@ -283,9 +282,9 @@ class Bot:
# Few arguments are completely removed in 3.7.0 version
# Temporary solution to raise an error if user passed these arguments
# with explanation how to fix it
parse_mode = kwargs.get("parse_mode", None)
link_preview_is_disabled = kwargs.get("disable_web_page_preview", None)
protect_content = kwargs.get("protect_content", None)
parse_mode = kwargs.get("parse_mode")
link_preview_is_disabled = kwargs.get("disable_web_page_preview")
protect_content = kwargs.get("protect_content")
if (
parse_mode is not None
or link_preview_is_disabled is not None
@ -310,7 +309,7 @@ class Bot:
self.__token = token
self._me: User | None = None
async def __aenter__(self) -> "Bot":
async def __aenter__(self) -> Bot:
return self
async def __aexit__(

View file

@ -58,7 +58,7 @@ def _prepare_connector(chain_or_plain: _ProxyType) -> tuple[type[TCPConnector],
# 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
if isinstance(chain_or_plain, str) or (
isinstance(chain_or_plain, tuple) and len(chain_or_plain) == 2
isinstance(chain_or_plain, tuple) and len(chain_or_plain) == 2 # noqa: PLR2004
):
chain_or_plain = cast(_ProxyBasic, chain_or_plain)
return ProxyConnector, _retrieve_basic(chain_or_plain)
@ -170,10 +170,10 @@ class AiohttpSession(BaseSession):
timeout=self.timeout if timeout is None else timeout,
) as resp:
raw_result = await resp.text()
except asyncio.TimeoutError:
raise TelegramNetworkError(method=method, message="Request timeout error")
except asyncio.TimeoutError as e:
raise TelegramNetworkError(method=method, message="Request timeout error") from e
except ClientError as e:
raise TelegramNetworkError(method=method, message=f"{type(e).__name__}: {e}")
raise TelegramNetworkError(method=method, message=f"{type(e).__name__}: {e}") from e
response = self.check_response(
bot=bot,
method=method,

View file

@ -90,14 +90,14 @@ class BaseSession(abc.ABC):
# in due to decoder can be customized and raise any exception
msg = "Failed to decode object"
raise ClientDecodeError(msg, e, content)
raise ClientDecodeError(msg, e, content) from e
try:
response_type = Response[method.__returning__] # type: ignore
response = response_type.model_validate(json_data, context={"bot": bot})
except ValidationError as e:
msg = "Failed to deserialize object"
raise ClientDecodeError(msg, e, json_data)
raise ClientDecodeError(msg, e, json_data) from e
if HTTPStatus.OK <= status_code <= HTTPStatus.IM_USED and response.ok:
return response

View file

@ -21,10 +21,10 @@ class FlagDecorator:
flag: Flag
@classmethod
def _with_flag(cls, flag: Flag) -> "FlagDecorator":
def _with_flag(cls, flag: Flag) -> FlagDecorator:
return cls(flag)
def _with_value(self, value: Any) -> "FlagDecorator":
def _with_value(self, value: Any) -> FlagDecorator:
new_flag = Flag(self.flag.name, value)
return self._with_flag(new_flag)
@ -33,11 +33,11 @@ class FlagDecorator:
pass
@overload
def __call__(self, value: Any, /) -> "FlagDecorator":
def __call__(self, value: Any, /) -> FlagDecorator:
pass
@overload
def __call__(self, **kwargs: Any) -> "FlagDecorator":
def __call__(self, **kwargs: Any) -> FlagDecorator:
pass
def __call__(

View file

@ -37,7 +37,7 @@ class _MemberStatusMarker:
def __or__(
self,
other: _MemberStatusMarker | _MemberStatusGroupMarker,
) -> "_MemberStatusGroupMarker":
) -> _MemberStatusGroupMarker:
if isinstance(other, _MemberStatusMarker):
return _MemberStatusGroupMarker(self, other)
if isinstance(other, _MemberStatusGroupMarker):
@ -53,7 +53,7 @@ class _MemberStatusMarker:
def __rshift__(
self,
other: _MemberStatusMarker | _MemberStatusGroupMarker,
) -> "_MemberStatusTransition":
) -> _MemberStatusTransition:
old = _MemberStatusGroupMarker(self)
if isinstance(other, _MemberStatusMarker):
return _MemberStatusTransition(old=old, new=_MemberStatusGroupMarker(other))
@ -68,7 +68,7 @@ class _MemberStatusMarker:
def __lshift__(
self,
other: _MemberStatusMarker | _MemberStatusGroupMarker,
) -> "_MemberStatusTransition":
) -> _MemberStatusTransition:
new = _MemberStatusGroupMarker(self)
if isinstance(other, _MemberStatusMarker):
return _MemberStatusTransition(old=_MemberStatusGroupMarker(other), new=new)
@ -118,7 +118,7 @@ class _MemberStatusGroupMarker:
def __rshift__(
self,
other: _MemberStatusMarker | _MemberStatusGroupMarker,
) -> "_MemberStatusTransition":
) -> _MemberStatusTransition:
if isinstance(other, _MemberStatusMarker):
return _MemberStatusTransition(old=self, new=_MemberStatusGroupMarker(other))
if isinstance(other, _MemberStatusGroupMarker):
@ -132,7 +132,7 @@ class _MemberStatusGroupMarker:
def __lshift__(
self,
other: _MemberStatusMarker | _MemberStatusGroupMarker,
) -> "_MemberStatusTransition":
) -> _MemberStatusTransition:
if isinstance(other, _MemberStatusMarker):
return _MemberStatusTransition(old=_MemberStatusGroupMarker(other), new=self)
if isinstance(other, _MemberStatusGroupMarker):

View file

@ -123,14 +123,15 @@ class Command(Filter):
result.update(command.magic_result)
return result
def extract_command(self, text: str) -> CommandObject:
@classmethod
def extract_command(cls, text: str) -> CommandObject:
# First step: separate command with arguments
# "/command@mention arg1 arg2" -> "/command@mention", ["arg1 arg2"]
try:
full_command, *args = text.split(maxsplit=1)
except ValueError:
except ValueError as e:
msg = "not enough values to unpack"
raise CommandException(msg)
raise CommandException(msg) from e
# Separate command into valuable parts
# "/command@mention" -> "/", ("command", "@", "mention")
@ -292,6 +293,6 @@ class CommandStart(Command):
args = decode_payload(args)
except UnicodeDecodeError as e:
msg = f"Failed to decode Base64: {e}"
raise CommandException(msg)
raise CommandException(msg) from e
return replace(command, args=args)
return command

View file

@ -120,7 +120,7 @@ class ObserverDecorator:
handlers = getattr(target, "__aiogram_handler__", None)
if not handlers:
handlers = []
setattr(target, "__aiogram_handler__", handlers)
target.__aiogram_handler__ = handlers # type: ignore[union-attr]
handlers.append(
HandlerContainer(
@ -137,7 +137,7 @@ class ObserverDecorator:
action = getattr(target, "__aiogram_action__", None)
if action is None:
action = defaultdict(dict)
setattr(target, "__aiogram_action__", action)
target.__aiogram_action__ = action # type: ignore[attr-defined]
action[self.action][self.name] = CallableObject(target)
def __call__(self, target: CallbackType) -> CallbackType:
@ -865,7 +865,7 @@ class SceneRegistry:
return self._scenes[scene]
except KeyError:
msg = f"Scene {scene!r} is not registered"
raise SceneException(msg)
raise SceneException(msg) from None
@dataclass

View file

@ -1,11 +1,11 @@
from __future__ import annotations
from abc import ABC, abstractmethod
from collections.abc import Generator
from typing import (
TYPE_CHECKING,
Any,
ClassVar,
Generator,
Generic,
TypeVar,
)

View file

@ -1,4 +1,4 @@
from typing import List, Literal, Optional, Union
from typing import Literal, Optional, Union
from .accepted_gift_types import AcceptedGiftTypes
from .affiliate_info import AffiliateInfo
@ -654,7 +654,7 @@ for _entity_name in __all__:
continue
_entity.model_rebuild(
_types_namespace={
"List": List,
"List": list,
"Optional": Optional,
"Union": Union,
"Literal": Literal,

View file

@ -1,8 +1,8 @@
import sys
from datetime import datetime, timezone
from typing import Annotated
from pydantic import PlainSerializer
from typing_extensions import Annotated
if sys.platform == "win32": # pragma: no cover

View file

@ -130,9 +130,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendMessage
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendMessage(
chat_id=self.chat.id,
@ -208,9 +208,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendMessage
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendMessage(
chat_id=self.chat.id,
@ -298,9 +298,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendAnimation
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendAnimation(
chat_id=self.chat.id,
@ -391,9 +391,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendAnimation
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendAnimation(
chat_id=self.chat.id,
@ -483,9 +483,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendAudio
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendAudio(
chat_id=self.chat.id,
@ -571,9 +571,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendAudio
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendAudio(
chat_id=self.chat.id,
@ -652,9 +652,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendContact
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendContact(
chat_id=self.chat.id,
@ -727,9 +727,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendContact
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendContact(
chat_id=self.chat.id,
@ -808,9 +808,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendDocument
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendDocument(
chat_id=self.chat.id,
@ -889,9 +889,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendDocument
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendDocument(
chat_id=self.chat.id,
@ -958,9 +958,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendGame
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendGame(
chat_id=self.chat.id,
@ -1018,9 +1018,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendGame
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendGame(
chat_id=self.chat.id,
@ -1122,9 +1122,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendInvoice
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendInvoice(
chat_id=self.chat.id,
@ -1245,9 +1245,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendInvoice
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendInvoice(
chat_id=self.chat.id,
@ -1342,9 +1342,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendLocation
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendLocation(
chat_id=self.chat.id,
@ -1423,9 +1423,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendLocation
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendLocation(
chat_id=self.chat.id,
@ -1492,9 +1492,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendMediaGroup
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendMediaGroup(
chat_id=self.chat.id,
@ -1552,9 +1552,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendMediaGroup
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendMediaGroup(
chat_id=self.chat.id,
@ -1628,9 +1628,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendPhoto
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendPhoto(
chat_id=self.chat.id,
@ -1709,9 +1709,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendPhoto
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendPhoto(
chat_id=self.chat.id,
@ -1804,9 +1804,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendPoll
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendPoll(
chat_id=self.chat.id,
@ -1903,9 +1903,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendPoll
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendPoll(
chat_id=self.chat.id,
@ -1982,9 +1982,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendDice
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendDice(
chat_id=self.chat.id,
@ -2048,9 +2048,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendDice
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendDice(
chat_id=self.chat.id,
@ -2118,9 +2118,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendSticker
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendSticker(
chat_id=self.chat.id,
@ -2187,9 +2187,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendSticker
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendSticker(
chat_id=self.chat.id,
@ -2270,9 +2270,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendVenue
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendVenue(
chat_id=self.chat.id,
@ -2357,9 +2357,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendVenue
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendVenue(
chat_id=self.chat.id,
@ -2456,9 +2456,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendVideo
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendVideo(
chat_id=self.chat.id,
@ -2558,9 +2558,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendVideo
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendVideo(
chat_id=self.chat.id,
@ -2644,9 +2644,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendVideoNote
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendVideoNote(
chat_id=self.chat.id,
@ -2719,9 +2719,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendVideoNote
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendVideoNote(
chat_id=self.chat.id,
@ -2798,9 +2798,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendVoice
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendVoice(
chat_id=self.chat.id,
@ -2876,9 +2876,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendVoice
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendVoice(
chat_id=self.chat.id,
@ -2954,9 +2954,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendPaidMedia
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendPaidMedia(
chat_id=self.chat.id,
@ -3031,9 +3031,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendPaidMedia
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendPaidMedia(
chat_id=self.chat.id,

View file

@ -3,8 +3,9 @@ from __future__ import annotations
import io
import os
from abc import ABC, abstractmethod
from collections.abc import AsyncGenerator
from pathlib import Path
from typing import TYPE_CHECKING, Any, AsyncGenerator
from typing import TYPE_CHECKING, Any
import aiofiles
@ -33,7 +34,7 @@ class InputFile(ABC):
self.chunk_size = chunk_size
@abstractmethod
async def read(self, bot: "Bot") -> AsyncGenerator[bytes, None]: # pragma: no cover
async def read(self, bot: Bot) -> AsyncGenerator[bytes, None]: # pragma: no cover
yield b""
@ -72,7 +73,7 @@ class BufferedInputFile(InputFile):
data = f.read()
return cls(data, filename=filename, chunk_size=chunk_size)
async def read(self, bot: "Bot") -> AsyncGenerator[bytes, None]:
async def read(self, bot: Bot) -> AsyncGenerator[bytes, None]:
buffer = io.BytesIO(self.data)
while chunk := buffer.read(self.chunk_size):
yield chunk
@ -99,7 +100,7 @@ class FSInputFile(InputFile):
self.path = path
async def read(self, bot: "Bot") -> AsyncGenerator[bytes, None]:
async def read(self, bot: Bot) -> AsyncGenerator[bytes, None]:
async with aiofiles.open(self.path, "rb") as f:
while chunk := await f.read(self.chunk_size):
yield chunk
@ -135,7 +136,7 @@ class URLInputFile(InputFile):
self.timeout = timeout
self.bot = bot
async def read(self, bot: "Bot") -> AsyncGenerator[bytes, None]:
async def read(self, bot: Bot) -> AsyncGenerator[bytes, None]:
bot = self.bot or bot
stream = bot.session.stream_content(
url=self.url,

View file

@ -851,9 +851,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendAnimation
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendAnimation(
chat_id=self.chat.id,
@ -944,9 +944,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendAnimation
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendAnimation(
chat_id=self.chat.id,
@ -1032,9 +1032,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendAudio
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendAudio(
chat_id=self.chat.id,
@ -1120,9 +1120,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendAudio
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendAudio(
chat_id=self.chat.id,
@ -1197,9 +1197,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendContact
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendContact(
chat_id=self.chat.id,
@ -1272,9 +1272,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendContact
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendContact(
chat_id=self.chat.id,
@ -1349,9 +1349,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendDocument
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendDocument(
chat_id=self.chat.id,
@ -1430,9 +1430,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendDocument
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendDocument(
chat_id=self.chat.id,
@ -1495,9 +1495,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendGame
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendGame(
chat_id=self.chat.id,
@ -1555,9 +1555,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendGame
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendGame(
chat_id=self.chat.id,
@ -1657,9 +1657,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendInvoice
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendInvoice(
chat_id=self.chat.id,
@ -1783,9 +1783,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendInvoice
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendInvoice(
chat_id=self.chat.id,
@ -1877,9 +1877,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendLocation
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendLocation(
chat_id=self.chat.id,
@ -1958,9 +1958,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendLocation
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendLocation(
chat_id=self.chat.id,
@ -2023,9 +2023,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendMediaGroup
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendMediaGroup(
chat_id=self.chat.id,
@ -2083,9 +2083,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendMediaGroup
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendMediaGroup(
chat_id=self.chat.id,
@ -2153,9 +2153,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendMessage
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendMessage(
chat_id=self.chat.id,
@ -2231,9 +2231,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendMessage
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendMessage(
chat_id=self.chat.id,
@ -2309,9 +2309,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendPhoto
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendPhoto(
chat_id=self.chat.id,
@ -2390,9 +2390,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendPhoto
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendPhoto(
chat_id=self.chat.id,
@ -2481,9 +2481,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendPoll
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendPoll(
chat_id=self.chat.id,
@ -2580,9 +2580,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendPoll
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendPoll(
chat_id=self.chat.id,
@ -2655,9 +2655,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendDice
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendDice(
chat_id=self.chat.id,
@ -2721,9 +2721,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendDice
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendDice(
chat_id=self.chat.id,
@ -2787,9 +2787,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendSticker
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendSticker(
chat_id=self.chat.id,
@ -2856,9 +2856,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendSticker
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendSticker(
chat_id=self.chat.id,
@ -2935,9 +2935,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendVenue
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendVenue(
chat_id=self.chat.id,
@ -3022,9 +3022,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendVenue
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendVenue(
chat_id=self.chat.id,
@ -3117,9 +3117,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendVideo
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendVideo(
chat_id=self.chat.id,
@ -3219,9 +3219,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendVideo
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendVideo(
chat_id=self.chat.id,
@ -3301,9 +3301,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendVideoNote
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendVideoNote(
chat_id=self.chat.id,
@ -3376,9 +3376,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendVideoNote
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendVideoNote(
chat_id=self.chat.id,
@ -3451,9 +3451,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendVoice
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendVoice(
chat_id=self.chat.id,
@ -3529,9 +3529,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendVoice
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendVoice(
chat_id=self.chat.id,
@ -3808,9 +3808,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import CopyMessage
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return CopyMessage(
from_chat_id=self.chat.id,
@ -3872,9 +3872,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import EditMessageText
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return EditMessageText(
chat_id=self.chat.id,
@ -3928,9 +3928,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import ForwardMessage
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return ForwardMessage(
from_chat_id=self.chat.id,
@ -3975,9 +3975,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import EditMessageMedia
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return EditMessageMedia(
chat_id=self.chat.id,
@ -4016,9 +4016,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import EditMessageReplyMarkup
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return EditMessageReplyMarkup(
chat_id=self.chat.id,
@ -4055,9 +4055,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import EditMessageReplyMarkup
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return EditMessageReplyMarkup(
chat_id=self.chat.id,
@ -4107,9 +4107,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import EditMessageLiveLocation
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return EditMessageLiveLocation(
chat_id=self.chat.id,
@ -4153,9 +4153,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import StopMessageLiveLocation
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return StopMessageLiveLocation(
chat_id=self.chat.id,
@ -4201,9 +4201,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import EditMessageCaption
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return EditMessageCaption(
chat_id=self.chat.id,
@ -4261,9 +4261,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import DeleteMessage
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return DeleteMessage(
chat_id=self.chat.id,
@ -4297,9 +4297,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import PinChatMessage
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return PinChatMessage(
chat_id=self.chat.id,
@ -4332,9 +4332,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import UnpinChatMessage
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return UnpinChatMessage(
chat_id=self.chat.id,
@ -4353,7 +4353,7 @@ class Message(MaybeInaccessibleMessage):
:param include_thread_id: if set, adds chat thread id to URL and returns like https://t.me/username/thread_id/message_id
:return: string with full message URL
"""
if self.chat.type in ("private", "group"):
if self.chat.type in {"private", "group"}:
return None
chat_value = (
@ -4397,9 +4397,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SetMessageReaction
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SetMessageReaction(
chat_id=self.chat.id,
@ -4461,9 +4461,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendPaidMedia
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendPaidMedia(
chat_id=self.chat.id,
@ -4536,9 +4536,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendPaidMedia
assert (
self.chat is not None
), "This method can be used only if chat is present in the message."
assert self.chat is not None, (
"This method can be used only if chat is present in the message."
)
return SendPaidMedia(
chat_id=self.chat.id,

View file

@ -22,6 +22,7 @@ if TYPE_CHECKING:
from aiogram import Bot
BAD_PATTERN = re.compile(r"[^a-zA-Z0-9-_]")
DEEPLINK_PAYLOAD_LENGTH = 64
async def create_start_link(
@ -145,8 +146,8 @@ def create_deep_link(
)
raise ValueError(msg)
if len(payload) > 64:
msg = "Payload must be up to 64 characters long."
if len(payload) > DEEPLINK_PAYLOAD_LENGTH:
msg = f"Payload must be up to {DEEPLINK_PAYLOAD_LENGTH} characters long."
raise ValueError(msg)
if not app_name:

View file

@ -15,7 +15,7 @@ class DataMixin:
data: dict[str, Any] | None = getattr(self, "_data", None)
if data is None:
data = {}
setattr(self, "_data", data)
self._data = data
return data
def __getitem__(self, key: str) -> Any:

View file

@ -148,8 +148,8 @@ When using :code:`uv`, prefix commands with :code:`uv run` to execute them in th
.. code-block:: bash
# Format code
uv run black aiogram tests examples
uv run isort aiogram tests examples
uv run ruff format aiogram tests scripts examples
uv run ruff check --fix aiogram tests scripts examples
# Run tests
uv run pytest tests
@ -180,22 +180,22 @@ implementing new features or experimenting.
Format the code (code-style)
----------------------------
Note that this project is Black-formatted, so you should follow that code-style,
too be sure You're correctly doing this let's reformat the code automatically:
Note that this project uses Ruff for formatting and linting, so you should follow that code-style.
To be sure you're correctly doing this, let's reformat the code automatically:
Using traditional approach:
.. code-block:: bash
black aiogram tests examples
isort aiogram tests examples
ruff format aiogram tests scripts examples
ruff check --fix aiogram tests scripts examples
Or with uv:
.. code-block:: bash
uv run black aiogram tests examples
uv run isort aiogram tests examples
uv run ruff format aiogram tests scripts examples
uv run ruff check --fix aiogram tests scripts examples
Or simply use Makefile:

View file

@ -2,12 +2,11 @@ import asyncio
import logging
from os import getenv
from handlers.echo import echo_router
from handlers.start import start_router
from aiogram import Bot, Dispatcher
from aiogram.client.default import DefaultBotProperties
from aiogram.enums import ParseMode
from handlers.echo import echo_router
from handlers.start import start_router
# Bot token can be obtained via https://t.me/BotFather
TOKEN = getenv("BOT_TOKEN")

View file

@ -4,7 +4,6 @@ from os import getenv
from typing import Any
from aiohttp import web
from finite_state_machine import form_router
from aiogram import Bot, Dispatcher, F, Router
from aiogram.client.session.aiohttp import AiohttpSession
@ -19,6 +18,7 @@ from aiogram.webhook.aiohttp_server import (
TokenBasedRequestHandler,
setup_application,
)
from finite_state_machine import form_router
main_router = Router()

View file

@ -4,14 +4,14 @@ from os import getenv
from aiohttp.web import run_app
from aiohttp.web_app import Application
from handlers import my_router
from routes import check_data_handler, demo_handler, send_message_handler
from aiogram import Bot, Dispatcher
from aiogram.client.default import DefaultBotProperties
from aiogram.enums.parse_mode import ParseMode
from aiogram.types import MenuButtonWebApp, WebAppInfo
from aiogram.webhook.aiohttp_server import SimpleRequestHandler, setup_application
from handlers import my_router
from routes import check_data_handler, demo_handler, send_message_handler
TOKEN = getenv("BOT_TOKEN")

View file

@ -94,8 +94,6 @@ docs = [
[dependency-groups]
dev = [
"black~=25.9",
"isort~=7.0",
"ruff~=0.14",
"mypy==1.10.1",
"toml~=0.10.2",
@ -139,16 +137,38 @@ exclude = [
[tool.ruff.lint]
select = [
# "C", # TODO: mccabe - code complecity
"C4",
"E",
"F",
"Q",
"RET",
"T10",
"T20",
"A", # flake8-annotations
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"DTZ", # flake8-datetimez
"E", # pycodestyle errors
"F", # pyflakes
"I", # isort
"PERF", # perflint
"PL", # pylint
"Q", # flake8-quotes
"RET", # flake8-return
"SIM", # flake8-simplify
"T10", # flake8-debugger
"T20", # flake8-print
"UP", # pyupgrade
]
ignore = [
"F401",
"F401", # unused-import (handled by other tools)
"A002", # builtin-argument-shadowing (common in API: type, id, format, etc.)
"A005", # stdlib-module-shadowing (Module `types` shadows a Python standard-library module)
"B008", # function-call-in-default-argument (Pydantic Field() defaults)
"PLC0415", # import-outside-top-level (needed for circular imports)
"PLC1901", # compare-to-empty-string (sometimes more explicit is better)
"PLR0904", # too-many-public-methods (Bot class has many API methods)
"PLR0911", # too-many-return-statements
"PLR0912", # too-many-branches
"PLR0913", # too-many-arguments (Telegram API methods have many params)
"PLR0915", # too-many-statements
"PLR0917", # too-many-positional-arguments (Telegram API design)
"PLR6301", # no-self-use (sometimes methods are intentionally not static)
"PLW2901", # redefined-loop-name (intentional pattern in this codebase)
"PLW3201", # bad-dunder-method-name (custom dunders for framework design)
]
[tool.ruff.lint.isort]
@ -164,6 +184,20 @@ known-first-party = [
"aiogram/types/*" = ["E501"]
"aiogram/methods/*" = ["E501"]
"aiogram/enums/*" = ["E501"]
"tests/**" = [
"PLR0124",
"PLR2004",
"DTZ005",
"DTZ006",
"A001",
"A004",
"B018",
"B020",
"B904",
"E501",
"F821",
"UP",
]
[tool.pytest.ini_options]
@ -174,7 +208,6 @@ testpaths = [
filterwarnings = [
"error",
"ignore::pytest.PytestUnraisableExceptionWarning",
# Remove when uvloop fixes the issue
# https://github.com/MagicStack/uvloop/issues/703
# https://github.com/MagicStack/uvloop/pull/705
@ -235,23 +268,10 @@ module = [
ignore_missing_imports = true
disallow_untyped_defs = true
[tool.black]
line-length = 99
target-version = ['py310', 'py311', 'py312', 'py313']
exclude = '''
(
\.eggs
| \.git
| \.tox
| build
| dist
| venv
| docs
)
'''
[tool.isort]
profile = "black"
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
line-ending = "auto"
[tool.towncrier]
package = "aiogram"

View file

@ -1,5 +1,6 @@
#!/usr/bin/env python3
"""Version bumping script for aiogram (replaces hatch version)."""
import re
import sys
from pathlib import Path

View file

@ -144,8 +144,7 @@ async def memory_storage():
@pytest.fixture()
async def redis_isolation(redis_storage):
isolation = redis_storage.create_isolation()
return isolation
return redis_storage.create_isolation()
@pytest.fixture()

View file

@ -1,5 +1,4 @@
from contextlib import contextmanager
from typing import Type
import pytest
from packaging import version
@ -10,8 +9,8 @@ import aiogram
@contextmanager
def check_deprecated(
max_version: str,
exception: Type[Exception],
warning: Type[Warning] = DeprecationWarning,
exception: type[Exception],
warning: type[Warning] = DeprecationWarning,
) -> None:
"""
Should be used for modules that are being deprecated or already removed from aiogram

View file

@ -1,5 +1,6 @@
from collections import deque
from typing import TYPE_CHECKING, Any, AsyncGenerator, Deque, Dict, Optional, Type
from collections.abc import AsyncGenerator
from typing import TYPE_CHECKING, Any
from aiogram import Bot
from aiogram.client.session.base import BaseSession
@ -10,9 +11,9 @@ from aiogram.types import UNSET_PARSE_MODE, ResponseParameters, User
class MockedSession(BaseSession):
def __init__(self):
super(MockedSession, self).__init__()
self.responses: Deque[Response[TelegramType]] = deque()
self.requests: Deque[TelegramMethod[TelegramType]] = deque()
super().__init__()
self.responses: deque[Response[TelegramType]] = deque()
self.requests: deque[TelegramMethod[TelegramType]] = deque()
self.closed = True
def add_result(self, response: Response[TelegramType]) -> Response[TelegramType]:
@ -29,7 +30,7 @@ class MockedSession(BaseSession):
self,
bot: Bot,
method: TelegramMethod[TelegramType],
timeout: Optional[int] = UNSET_PARSE_MODE,
timeout: int | None = UNSET_PARSE_MODE,
) -> TelegramType:
self.closed = False
self.requests.append(method)
@ -45,7 +46,7 @@ class MockedSession(BaseSession):
async def stream_content(
self,
url: str,
headers: Optional[Dict[str, Any]] = None,
headers: dict[str, Any] | None = None,
timeout: int = 30,
chunk_size: int = 65536,
raise_for_status: bool = True,
@ -58,9 +59,7 @@ class MockedBot(Bot):
session: MockedSession
def __init__(self, **kwargs):
super(MockedBot, self).__init__(
kwargs.pop("token", "42:TEST"), session=MockedSession(), **kwargs
)
super().__init__(kwargs.pop("token", "42:TEST"), session=MockedSession(), **kwargs)
self._me = User(
id=self.id,
is_bot=True,
@ -72,13 +71,13 @@ class MockedBot(Bot):
def add_result_for(
self,
method: Type[TelegramMethod[TelegramType]],
method: type[TelegramMethod[TelegramType]],
ok: bool,
result: TelegramType = None,
description: Optional[str] = None,
description: str | None = None,
error_code: int = 200,
migrate_to_chat_id: Optional[int] = None,
retry_after: Optional[int] = None,
migrate_to_chat_id: int | None = None,
retry_after: int | None = None,
) -> Response[TelegramType]:
response = Response[method.__returning__]( # type: ignore
ok=ok,

View file

@ -1,12 +1,8 @@
import asyncio
from collections.abc import AsyncGenerator, AsyncIterable
from typing import (
Any,
AsyncContextManager,
AsyncGenerator,
AsyncIterable,
Dict,
List,
Union,
)
from unittest.mock import AsyncMock, patch
@ -115,10 +111,10 @@ class TestAiohttpSession:
str_: str
int_: int
bool_: bool
unset_: Union[str, Default] = Default("parse_mode")
unset_: str | Default = Default("parse_mode")
null_: None
list_: List[str]
dict_: Dict[str, Any]
list_: list[str]
dict_: dict[str, Any]
session = AiohttpSession()
form = session.build_form_data(

View file

@ -1,6 +1,7 @@
import datetime
import json
from typing import Any, AsyncContextManager, AsyncGenerator, Dict, Optional
from collections.abc import AsyncGenerator
from typing import Any, AsyncContextManager
from unittest.mock import AsyncMock, patch
import pytest
@ -39,7 +40,7 @@ class CustomSession(BaseSession):
self,
token: str,
method: TelegramMethod[TelegramType],
timeout: Optional[int] = UNSET_PARSE_MODE,
timeout: int | None = UNSET_PARSE_MODE,
) -> None: # type: ignore
assert isinstance(token, str)
assert isinstance(method, TelegramMethod)
@ -47,7 +48,7 @@ class CustomSession(BaseSession):
async def stream_content(
self,
url: str,
headers: Optional[Dict[str, Any]] = None,
headers: dict[str, Any] | None = None,
timeout: int = 30,
chunk_size: int = 65536,
raise_for_status: bool = True,

View file

@ -15,5 +15,5 @@ class TestAddStickerToSet:
sticker="file id", format=StickerFormat.STATIC, emoji_list=[":)"]
),
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -7,5 +7,5 @@ class TestAnswerCallbackQuery:
prepare_result = bot.add_result_for(AnswerCallbackQuery, ok=True, result=True)
response: bool = await bot.answer_callback_query(callback_query_id="cq id", text="OK")
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -17,5 +17,5 @@ class TestAnswerInlineQuery:
)
],
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -9,5 +9,5 @@ class TestAnswerPreCheckoutQuery:
response: bool = await bot.answer_pre_checkout_query(
pre_checkout_query_id="query id", ok=True
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -7,5 +7,5 @@ class TestAnswerShippingQuery:
prepare_result = bot.add_result_for(AnswerShippingQuery, ok=True, result=True)
response: bool = await bot.answer_shipping_query(shipping_query_id="query id", ok=True)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -15,5 +15,5 @@ class TestAnswerWebAppQuery:
thumbnail_url="test",
),
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -10,5 +10,5 @@ class TestApproveChatJoinRequest:
chat_id=-42,
user_id=42,
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -10,5 +10,5 @@ class TestApproveSuggestedPost:
chat_id=-42,
message_id=42,
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -7,5 +7,5 @@ class TestKickChatMember:
prepare_result = bot.add_result_for(BanChatMember, ok=True, result=True)
response: bool = await bot.ban_chat_member(chat_id=-42, user_id=42)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -10,5 +10,5 @@ class TestBanChatSenderChat:
chat_id=-42,
sender_chat_id=-1337,
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -7,5 +7,5 @@ class TestClose:
prepare_result = bot.add_result_for(Close, ok=True, result=True)
response: bool = await bot.close()
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -10,5 +10,5 @@ class TestCloseForumTopic:
chat_id=42,
message_thread_id=42,
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -7,5 +7,5 @@ class TestCloseGeneralForumTopic:
prepare_result = bot.add_result_for(CloseGeneralForumTopic, ok=True, result=True)
response: bool = await bot.close_general_forum_topic(chat_id=42)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -9,5 +9,5 @@ class TestConvertGiftToStars:
response: bool = await bot.convert_gift_to_stars(
business_connection_id="test_connection_id", owned_gift_id="test_gift_id"
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -12,5 +12,5 @@ class TestCopyMessage:
from_chat_id=42,
message_id=42,
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -20,5 +20,5 @@ class TestCreateChatInviteLink:
response: ChatInviteLink = await bot.create_chat_invite_link(
chat_id=-42,
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -24,5 +24,5 @@ class TestCreateChatSubscriptionInviteLink:
subscription_period=timedelta(days=30),
subscription_price=42,
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -15,5 +15,5 @@ class TestCreateForumTopic:
chat_id=42,
name="test",
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -17,5 +17,5 @@ class TestCreateInvoiceLink:
currency="BTC",
prices=[LabeledPrice(label="Test", amount=1)],
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -20,5 +20,5 @@ class TestCreateNewStickerSet:
],
sticker_format=StickerFormat.STATIC,
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -10,5 +10,5 @@ class TestDeclineChatJoinRequest:
chat_id=-42,
user_id=42,
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -10,5 +10,5 @@ class TestDeclineSuggestedPost:
chat_id=-42,
message_id=42,
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -9,5 +9,5 @@ class TestDeleteBusinessMessages:
response: bool = await bot.delete_business_messages(
business_connection_id="test_connection_id", message_ids=[1, 2, 3]
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -7,5 +7,5 @@ class TestDeleteChatPhoto:
prepare_result = bot.add_result_for(DeleteChatPhoto, ok=True, result=True)
response: bool = await bot.delete_chat_photo(chat_id=42)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -7,5 +7,5 @@ class TestDeleteChatStickerSet:
prepare_result = bot.add_result_for(DeleteChatStickerSet, ok=True, result=True)
response: bool = await bot.delete_chat_sticker_set(chat_id=42)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -10,5 +10,5 @@ class TestDeleteForumTopic:
chat_id=42,
message_thread_id=42,
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -7,5 +7,5 @@ class TestDeleteMessage:
prepare_result = bot.add_result_for(DeleteMessage, ok=True, result=True)
response: bool = await bot.delete_message(chat_id=42, message_id=42)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -7,5 +7,5 @@ class TestKickChatMember:
prepare_result = bot.add_result_for(DeleteMyCommands, ok=True, result=True)
response: bool = await bot.delete_my_commands()
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -7,5 +7,5 @@ class TestDeleteStickerFromSet:
prepare_result = bot.add_result_for(DeleteStickerFromSet, ok=True, result=True)
response: bool = await bot.delete_sticker_from_set(sticker="sticker id")
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -7,5 +7,5 @@ class TestDeleteStickerSet:
prepare_result = bot.add_result_for(DeleteStickerSet, ok=True, result=True)
response: bool = await bot.delete_sticker_set(name="test")
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -9,5 +9,5 @@ class TestDeleteStory:
response: bool = await bot.delete_story(
business_connection_id="test_connection_id", story_id=42
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -7,5 +7,5 @@ class TestDeleteWebhook:
prepare_result = bot.add_result_for(DeleteWebhook, ok=True, result=True)
response: bool = await bot.delete_webhook()
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -20,5 +20,5 @@ class TestEditChatInviteLink:
response: ChatInviteLink = await bot.edit_chat_invite_link(
chat_id=-42, invite_link="https://t.me/username", member_limit=1
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -22,5 +22,5 @@ class TestEditChatSubscriptionInviteLink:
invite_link="https://t.me/username/test",
name="test",
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -12,5 +12,5 @@ class TestEditForumTopic:
name="test",
icon_custom_emoji_id="0",
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -7,5 +7,5 @@ class TestCloseGeneralForumTopic:
prepare_result = bot.add_result_for(EditGeneralForumTopic, ok=True, result=True)
response: bool = await bot.edit_general_forum_topic(chat_id=42, name="Test")
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -1,5 +1,4 @@
import datetime
from typing import Union
from aiogram.methods import EditMessageCaption
from aiogram.types import Chat, Message
@ -19,6 +18,6 @@ class TestEditMessageCaption:
),
)
response: Union[Message, bool] = await bot.edit_message_caption()
request = bot.get_request()
response: Message | bool = await bot.edit_message_caption()
bot.get_request()
assert response == prepare_result.result

View file

@ -31,5 +31,5 @@ class TestEditMessageChecklist:
message_id=42,
checklist=checklist,
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -1,5 +1,3 @@
from typing import Union
from aiogram.methods import EditMessageLiveLocation
from aiogram.types import Message
from tests.mocked_bot import MockedBot
@ -9,8 +7,8 @@ class TestEditMessageLiveLocation:
async def test_bot_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(EditMessageLiveLocation, ok=True, result=True)
response: Union[Message, bool] = await bot.edit_message_live_location(
response: Message | bool = await bot.edit_message_live_location(
latitude=3.141592, longitude=3.141592
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -1,5 +1,3 @@
from typing import Union
from aiogram.methods import EditMessageMedia
from aiogram.types import BufferedInputFile, InputMediaPhoto, Message
from tests.mocked_bot import MockedBot
@ -9,8 +7,8 @@ class TestEditMessageMedia:
async def test_bot_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(EditMessageMedia, ok=True, result=True)
response: Union[Message, bool] = await bot.edit_message_media(
response: Message | bool = await bot.edit_message_media(
media=InputMediaPhoto(media=BufferedInputFile(b"", "photo.png"))
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -1,5 +1,3 @@
from typing import Union
from aiogram.methods import EditMessageReplyMarkup
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message
from tests.mocked_bot import MockedBot
@ -9,7 +7,7 @@ class TestEditMessageReplyMarkup:
async def test_bot_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(EditMessageReplyMarkup, ok=True, result=True)
response: Union[Message, bool] = await bot.edit_message_reply_markup(
response: Message | bool = await bot.edit_message_reply_markup(
chat_id=42,
inline_message_id="inline message id",
reply_markup=InlineKeyboardMarkup(
@ -18,5 +16,5 @@ class TestEditMessageReplyMarkup:
]
),
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -1,5 +1,3 @@
from typing import Union
from aiogram.methods import EditMessageText
from aiogram.types import Message
from tests.mocked_bot import MockedBot
@ -9,8 +7,8 @@ class TestEditMessageText:
async def test_bot_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(EditMessageText, ok=True, result=True)
response: Union[Message, bool] = await bot.edit_message_text(
response: Message | bool = await bot.edit_message_text(
chat_id=42, inline_message_id="inline message id", text="text"
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -1,5 +1,3 @@
import datetime
from aiogram.methods import EditStory
from aiogram.types import Chat, InputStoryContentPhoto, Story
from tests.mocked_bot import MockedBot
@ -22,5 +20,5 @@ class TestEditStory:
content=InputStoryContentPhoto(type="photo", photo="test_photo"),
caption="Test caption",
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -11,5 +11,5 @@ class TestEditUserStarSubscription:
telegram_payment_charge_id="telegram_payment_charge_id",
is_canceled=False,
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -9,5 +9,5 @@ class TestExportChatInviteLink:
)
response: str = await bot.export_chat_invite_link(chat_id=42)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -28,5 +28,5 @@ class TestGetAvailableGifts:
)
response: Gifts = await bot.get_available_gifts()
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -37,5 +37,5 @@ class TestGetBusinessAccountGifts:
business_connection_id="test_connection_id",
limit=10,
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -17,5 +17,5 @@ class TestGetBusinessAccountStarBalance:
response: StarAmount = await bot.get_business_account_star_balance(
business_connection_id="test_connection_id",
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -20,5 +20,5 @@ class TestGetBusinessConnection:
response: BusinessConnection = await bot.get_business_connection(
business_connection_id="test"
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -25,5 +25,5 @@ class TestGetChat:
)
response: ChatFullInfo = await bot.get_chat(chat_id=-42)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -1,5 +1,3 @@
from typing import List
from aiogram.methods import GetChatAdministrators
from aiogram.types import ChatMember, ChatMemberOwner, User
from tests.mocked_bot import MockedBot
@ -16,6 +14,6 @@ class TestGetChatAdministrators:
)
],
)
response: List[ChatMember] = await bot.get_chat_administrators(chat_id=-42)
request = bot.get_request()
response: list[ChatMember] = await bot.get_chat_administrators(chat_id=-42)
bot.get_request()
assert response == prepare_result.result

View file

@ -37,5 +37,5 @@ class TestGetChatGifts:
chat_id=42,
limit=10,
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -13,5 +13,5 @@ class TestGetChatMember:
),
)
response = await bot.get_chat_member(chat_id=-42, user_id=42)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -7,5 +7,5 @@ class TestGetChatMembersCount:
prepare_result = bot.add_result_for(GetChatMemberCount, ok=True, result=42)
response: int = await bot.get_chat_member_count(chat_id=-42)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -8,5 +8,5 @@ class TestGetChatMenuButton:
prepare_result = bot.add_result_for(GetChatMenuButton, ok=True, result=MenuButtonDefault())
response: MenuButton = await bot.get_chat_menu_button()
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -1,5 +1,3 @@
from typing import List
from aiogram.methods import GetCustomEmojiStickers
from aiogram.types import Sticker
from tests.mocked_bot import MockedBot
@ -24,8 +22,8 @@ class TestGetCustomEmojiStickers:
],
)
response: List[Sticker] = await bot.get_custom_emoji_stickers(
response: list[Sticker] = await bot.get_custom_emoji_stickers(
custom_emoji_ids=["1", "2"],
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -10,5 +10,5 @@ class TestGetFile:
)
response: File = await bot.get_file(file_id="file id")
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -1,5 +1,3 @@
from typing import List
from aiogram.methods import GetForumTopicIconStickers
from aiogram.types import Sticker
from tests.mocked_bot import MockedBot
@ -9,6 +7,6 @@ class TestGetForumTopicIconStickers:
async def test_bot_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(GetForumTopicIconStickers, ok=True, result=[])
response: List[Sticker] = await bot.get_forum_topic_icon_stickers()
request = bot.get_request()
response: list[Sticker] = await bot.get_forum_topic_icon_stickers()
bot.get_request()
assert response == prepare_result.result

View file

@ -1,5 +1,3 @@
from typing import List
from aiogram.methods import GetGameHighScores
from aiogram.types import GameHighScore, User
from tests.mocked_bot import MockedBot
@ -17,6 +15,6 @@ class TestGetGameHighScores:
],
)
response: List[GameHighScore] = await bot.get_game_high_scores(user_id=42)
request = bot.get_request()
response: list[GameHighScore] = await bot.get_game_high_scores(user_id=42)
bot.get_request()
assert response == prepare_result.result

View file

@ -9,7 +9,7 @@ class TestGetMe:
GetMe, ok=True, result=User(id=42, is_bot=False, first_name="User")
)
response: User = await bot.get_me()
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result
async def test_me_property(self, bot: MockedBot):

View file

@ -1,5 +1,3 @@
from typing import List
from aiogram.methods import GetMyCommands
from aiogram.types import BotCommand
from tests.mocked_bot import MockedBot
@ -9,6 +7,6 @@ class TestGetMyCommands:
async def test_bot_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(GetMyCommands, ok=True, result=None)
response: List[BotCommand] = await bot.get_my_commands()
request = bot.get_request()
response: list[BotCommand] = await bot.get_my_commands()
bot.get_request()
assert response == prepare_result.result

View file

@ -24,5 +24,5 @@ class TestGetMyDefaultAdministratorRights:
)
response: ChatAdministratorRights = await bot.get_my_default_administrator_rights()
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -10,5 +10,5 @@ class TestGetMyDescription:
)
response: BotDescription = await bot.get_my_description()
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -10,5 +10,5 @@ class TestGetMyShortDescription:
)
response: BotShortDescription = await bot.get_my_short_description()
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -14,5 +14,5 @@ class TestGetMyStarBalance:
)
response: StarAmount = await bot.get_my_star_balance()
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -3,7 +3,6 @@ from datetime import datetime
from aiogram.enums import TransactionPartnerUserTransactionTypeEnum
from aiogram.methods import GetStarTransactions
from aiogram.types import (
File,
StarTransaction,
StarTransactions,
TransactionPartnerUser,
@ -45,5 +44,5 @@ class TestGetStarTransactions:
)
response: StarTransactions = await bot.get_star_transactions(limit=10, offset=0)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -29,5 +29,5 @@ class TestGetStickerSet:
)
response: StickerSet = await bot.get_sticker_set(name="test")
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

View file

@ -1,5 +1,3 @@
from typing import List
from aiogram.methods import GetUpdates
from aiogram.types import Update
from tests.mocked_bot import MockedBot
@ -9,6 +7,6 @@ class TestGetUpdates:
async def test_bot_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(GetUpdates, ok=True, result=[Update(update_id=42)])
response: List[Update] = await bot.get_updates()
request = bot.get_request()
response: list[Update] = await bot.get_updates()
bot.get_request()
assert response == prepare_result.result

View file

@ -1,5 +1,4 @@
import datetime
from typing import Optional
import pytest
@ -31,9 +30,9 @@ class TestGetMessageUrl:
bot: MockedBot,
chat_type: str,
chat_id: int,
chat_username: Optional[str],
chat_username: str | None,
force_private: bool,
expected_result: Optional[str],
expected_result: str | None,
):
fake_chat = Chat(id=chat_id, username=chat_username, type=chat_type)
fake_message_id = 10
@ -80,11 +79,11 @@ class TestGetMessageUrl:
def test_get_url_if_topic_message(
self,
bot: MockedBot,
chat_username: Optional[str],
chat_username: str | None,
force_private: bool,
include_thread_id: bool,
fake_thread_id_topic: Optional[int],
expected_result: Optional[str],
fake_thread_id_topic: int | None,
expected_result: str | None,
):
fake_message_id = 10
fake_chat_id = -1001234567890

View file

@ -37,5 +37,5 @@ class TestGetUserGifts:
user_id=42,
limit=10,
)
request = bot.get_request()
bot.get_request()
assert response == prepare_result.result

Some files were not shown because too many files have changed in this diff Show more