mirror of
https://github.com/aiogram/aiogram.git
synced 2026-04-08 16:37:47 +00:00
Migrate to hatchling instead of poetry, ruff instead of flake8
This commit is contained in:
parent
04ccb390d5
commit
c62a450253
48 changed files with 452 additions and 2772 deletions
|
|
@ -1,6 +0,0 @@
|
|||
[report]
|
||||
exclude_lines =
|
||||
pragma: no cover
|
||||
if TYPE_CHECKING:
|
||||
@abstractmethod
|
||||
@overload
|
||||
12
.flake8
12
.flake8
|
|
@ -1,12 +0,0 @@
|
|||
[flake8]
|
||||
max-line-length = 99
|
||||
select = C,E,F,W,B,B950
|
||||
ignore = E501,W503,E203
|
||||
exclude =
|
||||
.git
|
||||
build
|
||||
dist
|
||||
venv
|
||||
docs
|
||||
*.egg-info
|
||||
experiment.py
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -13,7 +13,7 @@ dist/
|
|||
site/
|
||||
*.egg-info/
|
||||
*.egg
|
||||
aiogram/_meta.py
|
||||
.ruff_cache
|
||||
|
||||
.mypy_cache
|
||||
.pytest_cache
|
||||
|
|
|
|||
|
|
@ -19,34 +19,8 @@ repos:
|
|||
- id: black
|
||||
files: &files '^(aiogram|tests|examples)'
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-isort
|
||||
rev: v5.10.1
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: 'v0.0.215'
|
||||
hooks:
|
||||
- id: isort
|
||||
additional_dependencies: [ toml ]
|
||||
files: *files
|
||||
|
||||
- repo: https://gitlab.com/pycqa/flake8
|
||||
rev: 3.9.2
|
||||
hooks:
|
||||
- id: flake8
|
||||
args: [ '--config=.flake8' ]
|
||||
files: *files
|
||||
|
||||
- repo: https://github.com/floatingpurr/sync_with_poetry
|
||||
rev: 0.2.0
|
||||
hooks:
|
||||
- id: sync_with_poetry
|
||||
|
||||
- repo: https://github.com/python-poetry/poetry
|
||||
rev: '1.2.1'
|
||||
hooks:
|
||||
- id: poetry-check
|
||||
- id: poetry-lock
|
||||
args: [ "--no-update" ]
|
||||
- id: poetry-export
|
||||
args: [ "-f", "requirements.txt", "--without-hashes", "-o", "requirements/base.txt" ]
|
||||
- id: poetry-export
|
||||
args: [ "-f", "requirements.txt", "--without-hashes", "-o", "requirements/docs.txt",
|
||||
"-E", "fast", "-E", "redis", "-E", "proxy", "-E", "i18n",
|
||||
"--with", "docs" ]
|
||||
- id: ruff
|
||||
args: [ "--force-exclude" ]
|
||||
|
|
|
|||
13
Makefile
13
Makefile
|
|
@ -68,16 +68,15 @@ clean:
|
|||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
$(py) isort --check-only $(code_dir)
|
||||
$(py) black --check --diff $(code_dir)
|
||||
$(py) flake8 $(code_dir)
|
||||
$(py) mypy $(package_dir)
|
||||
# TODO: wemake-python-styleguide
|
||||
isort --check-only $(code_dir)
|
||||
black --check --diff $(code_dir)
|
||||
ruff $(code_dir)
|
||||
mypy $(package_dir)
|
||||
|
||||
.PHONY: reformat
|
||||
reformat:
|
||||
$(py) black $(code_dir)
|
||||
$(py) isort $(code_dir)
|
||||
black $(code_dir)
|
||||
isort $(code_dir)
|
||||
|
||||
# =================================================================================================
|
||||
# Tests
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
from contextlib import suppress
|
||||
|
||||
from aiogram.dispatcher.flags import FlagGenerator
|
||||
|
||||
from .client import session
|
||||
|
|
@ -9,12 +11,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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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"""
|
||||
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ class Dispatcher(Router):
|
|||
|
||||
:return:
|
||||
"""
|
||||
return None
|
||||
pass
|
||||
|
||||
@parent_router.setter
|
||||
def parent_router(self, value: Router) -> None:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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}"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ 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
|
||||
|
||||
from pydantic import BaseConfig, BaseModel, Extra, root_validator
|
||||
from pydantic.generics import GenericModel
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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}"
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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})"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
"""
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 user’s profile photo. The photo can be in .jpeg or .svg formats. Only returned for Web Apps launched from the attachment menu."""
|
||||
"""URL of the user’s 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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,12 @@ from aiogram import Bot, Dispatcher, F, Router, html
|
|||
from aiogram.filters import Command
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.fsm.state import State, StatesGroup
|
||||
from aiogram.types import KeyboardButton, Message, ReplyKeyboardMarkup, ReplyKeyboardRemove
|
||||
from aiogram.types import (
|
||||
KeyboardButton,
|
||||
Message,
|
||||
ReplyKeyboardMarkup,
|
||||
ReplyKeyboardRemove,
|
||||
)
|
||||
|
||||
form_router = Router()
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ from os import getenv
|
|||
from typing import Any, Dict, Union
|
||||
|
||||
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
|
||||
|
|
@ -16,6 +15,7 @@ from aiogram.webhook.aiohttp_server import (
|
|||
TokenBasedRequestHandler,
|
||||
setup_application,
|
||||
)
|
||||
from finite_state_machine import form_router
|
||||
|
||||
main_router = Router()
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ async def command_add_bot(message: Message, command: CommandObject, bot: Bot) ->
|
|||
return message.answer("Invalid token")
|
||||
await new_bot.delete_webhook(drop_pending_updates=True)
|
||||
await new_bot.set_webhook(OTHER_BOTS_URL.format(bot_token=command.args))
|
||||
await message.answer(f"Bot @{bot_user.username} successful added")
|
||||
return await message.answer(f"Bot @{bot_user.username} successful added")
|
||||
|
||||
|
||||
async def on_startup(dispatcher: Dispatcher, bot: Bot):
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ async def send_message_handler(request: Request):
|
|||
except ValueError:
|
||||
return json_response({"ok": False, "err": "Unauthorized"}, status=401)
|
||||
|
||||
print(data)
|
||||
reply_markup = None
|
||||
if data["with_webview"] == "1":
|
||||
reply_markup = InlineKeyboardMarkup(
|
||||
|
|
|
|||
37
mypy.ini
37
mypy.ini
|
|
@ -1,37 +0,0 @@
|
|||
[mypy]
|
||||
plugins = pydantic.mypy
|
||||
python_version = 3.8
|
||||
show_error_codes = True
|
||||
show_error_context = True
|
||||
pretty = True
|
||||
ignore_missing_imports = False
|
||||
warn_unused_configs = True
|
||||
disallow_subclassing_any = True
|
||||
disallow_any_generics = True
|
||||
disallow_untyped_calls = True
|
||||
disallow_untyped_defs = True
|
||||
disallow_incomplete_defs = True
|
||||
check_untyped_defs = True
|
||||
disallow_untyped_decorators = True
|
||||
no_implicit_optional = True
|
||||
warn_redundant_casts = True
|
||||
warn_unused_ignores = True
|
||||
warn_return_any = True
|
||||
follow_imports_for_stubs = True
|
||||
namespace_packages = True
|
||||
show_absolute_path = True
|
||||
|
||||
[mypy-aiofiles]
|
||||
ignore_missing_imports = True
|
||||
|
||||
[mypy-async_lru]
|
||||
ignore_missing_imports = True
|
||||
|
||||
[mypy-uvloop]
|
||||
ignore_missing_imports = True
|
||||
|
||||
[mypy-redis.*]
|
||||
ignore_missing_imports = True
|
||||
|
||||
[mypy-babel.*]
|
||||
ignore_missing_imports = True
|
||||
2486
poetry.lock
generated
2486
poetry.lock
generated
File diff suppressed because it is too large
Load diff
308
pyproject.toml
308
pyproject.toml
|
|
@ -1,18 +1,19 @@
|
|||
[tool.poetry]
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "aiogram"
|
||||
version = "3.0.0-beta.7"
|
||||
description = "Modern and fully asynchronous framework for Telegram Bot API"
|
||||
description = 'Modern and fully asynchronous framework for Telegram Bot API'
|
||||
readme = "README.rst"
|
||||
requires-python = ">=3.8"
|
||||
license = "MIT"
|
||||
authors = [
|
||||
"Alex Root Junior <jroot.junior@gmail.com>",
|
||||
{ name = "Alex Root Junior", email = "jroot.junior@gmail.com" },
|
||||
]
|
||||
maintainers = [
|
||||
"Alex Root Junior <jroot.junior@gmail.com>",
|
||||
{ name = "Alex Root Junior", email = "jroot.junior@gmail.com" },
|
||||
]
|
||||
license = "MIT"
|
||||
readme = "README.rst"
|
||||
homepage = "https://aiogram.dev/"
|
||||
documentation = "https://docs.aiogram.dev/"
|
||||
repository = "https://github.com/aiogram/aiogram/"
|
||||
keywords = [
|
||||
"telegram",
|
||||
"bot",
|
||||
|
|
@ -38,77 +39,230 @@ classifiers = [
|
|||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
"Topic :: Communications :: Chat",
|
||||
]
|
||||
packages = [
|
||||
{ include = "aiogram" }
|
||||
dependencies = [
|
||||
"magic-filter~=1.0.9",
|
||||
"aiohttp~=3.8.3",
|
||||
"pydantic~=1.10.4",
|
||||
"aiofiles~=22.1.0",
|
||||
"certifi>=2022.9.24",
|
||||
]
|
||||
dynamic = ["version"]
|
||||
|
||||
[tool.hatch.version]
|
||||
path = "aiogram/__init__.py"
|
||||
|
||||
[project.optional-dependencies]
|
||||
fast = [
|
||||
"uvloop>=0.17.0; sys_platform == 'darwin' or sys_platform == 'linux'",
|
||||
]
|
||||
redis = [
|
||||
"redis~=4.3.4",
|
||||
]
|
||||
proxy = [
|
||||
"aiohttp-socks~=0.7.1",
|
||||
]
|
||||
i18n = [
|
||||
"Babel~=2.9.1",
|
||||
]
|
||||
test = [
|
||||
"pytest~=7.1.3",
|
||||
"pytest-html~=3.1.1",
|
||||
"pytest-asyncio~=0.19.0",
|
||||
"pytest-lazy-fixture~=0.6.3",
|
||||
"pytest-mock~=3.9.0",
|
||||
"pytest-mypy~=0.10.0",
|
||||
"pytest-cov~=4.0.0",
|
||||
"pytest-aiohttp~=1.0.4",
|
||||
"aresponses~=2.1.6",
|
||||
]
|
||||
docs = [
|
||||
"Sphinx~=5.2.3",
|
||||
"sphinx-intl~=2.0.1",
|
||||
"sphinx-autobuild~=2021.3.14",
|
||||
"sphinx-copybutton~=0.5.0",
|
||||
"furo~=2022.9.29",
|
||||
"sphinx-prompt~=1.5.0",
|
||||
"Sphinx-Substitution-Extensions~=2022.2.16",
|
||||
"towncrier~=22.8.0",
|
||||
"pygments~=2.4",
|
||||
"pymdown-extensions~=9.6",
|
||||
"markdown-include~=0.7.0",
|
||||
"Pygments~=2.13.0",
|
||||
"sphinxcontrib-towncrier~=0.3.1a3",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://aiogram.dev/"
|
||||
Documentation = "https://docs.aiogram.dev/"
|
||||
Repository = "https://github.com/aiogram/aiogram/"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.8"
|
||||
magic-filter = "^1.0.9"
|
||||
aiohttp = "^3.8.3"
|
||||
pydantic = "^1.10.2"
|
||||
aiofiles = "^22.1.0"
|
||||
# Fast
|
||||
uvloop = { version = "^0.17.0", markers = "sys_platform == 'darwin' or sys_platform == 'linux'", optional = true }
|
||||
# i18n
|
||||
Babel = { version = "^2.9.1", optional = true }
|
||||
# Proxy
|
||||
aiohttp-socks = { version = "^0.7.1", optional = true }
|
||||
# Redis
|
||||
redis = { version = "^4.3.4", optional = true }
|
||||
certifi = "^2022.9.24"
|
||||
[tool.hatch.envs.default]
|
||||
features = [
|
||||
"fast",
|
||||
"redis",
|
||||
"proxy",
|
||||
"i18n",
|
||||
]
|
||||
dependencies = [
|
||||
"black~=22.8",
|
||||
"isort~=5.11",
|
||||
"ruff~=0.0.215",
|
||||
"mypy~=0.981",
|
||||
"toml~=0.10.2",
|
||||
"pre-commit~=2.20.0",
|
||||
"packaging~=21.3",
|
||||
"typing-extensions~=4.3.0",
|
||||
]
|
||||
post-install-commands = [
|
||||
"pre-commit install",
|
||||
]
|
||||
|
||||
[tool.hatch.envs.default.scripts]
|
||||
reformat = [
|
||||
"black aiogram tests",
|
||||
"isort aiogram tests",
|
||||
]
|
||||
lint = "ruff aiogram"
|
||||
|
||||
[tool.hatch.envs.docs]
|
||||
features = [
|
||||
"fast",
|
||||
"redis",
|
||||
"proxy",
|
||||
"i18n",
|
||||
"docs",
|
||||
]
|
||||
[tool.hatch.envs.docs.scripts]
|
||||
serve = "sphinx-autobuild --watch aiogram/ --watch CHANGELOG.rst --watch README.rst docs/ docs/_build/ {args}"
|
||||
|
||||
[tool.hatch.envs.dev]
|
||||
python = "3.10"
|
||||
features = [
|
||||
"fast",
|
||||
"redis",
|
||||
"proxy",
|
||||
"i18n",
|
||||
"test",
|
||||
]
|
||||
extra-dependencies = [
|
||||
"butcher @ git+https://github.com/aiogram/butcher.git@v0.1.9"
|
||||
]
|
||||
|
||||
[tool.hatch.envs.test]
|
||||
features = [
|
||||
"fast",
|
||||
"redis",
|
||||
"proxy",
|
||||
"i18n",
|
||||
"test",
|
||||
]
|
||||
|
||||
[tool.hatch.envs.test.scripts]
|
||||
cov = [
|
||||
"pytest --cov-config pyproject.toml --cov=aiogram --html=reports/py{matrix:python}/tests/index.html {args}",
|
||||
"coverage html -d reports/py{matrix:python}/coverage",
|
||||
]
|
||||
cov-redis = [
|
||||
"pytest --cov-config pyproject.toml --cov=aiogram --html=reports/py{matrix:python}/tests/index.html --redis {env:REDIS_DNS:'redis://localhost:6379'} {args}",
|
||||
"coverage html -d reports/py{matrix:python}/coverage",
|
||||
]
|
||||
view-cov = "google-chrome-stable reports/py{matrix:python}/coverage/index.html"
|
||||
|
||||
|
||||
[tool.poetry.group.docs.dependencies]
|
||||
Sphinx = "^5.2.3"
|
||||
sphinx-intl = "^2.0.1"
|
||||
sphinx-autobuild = "^2021.3.14"
|
||||
sphinx-copybutton = "^0.5.0"
|
||||
furo = "^2022.9.29"
|
||||
sphinx-prompt = "^1.5.0"
|
||||
Sphinx-Substitution-Extensions = "^2022.2.16"
|
||||
towncrier = "^22.8.0"
|
||||
pygments = "^2.4"
|
||||
pymdown-extensions = "^9.6"
|
||||
markdown-include = "^0.7.0"
|
||||
Pygments = "^2.13.0"
|
||||
sphinxcontrib-towncrier = "^0.3.1a3"
|
||||
[[tool.hatch.envs.test.matrix]]
|
||||
python = ["38", "39", "310", "311"]
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 99
|
||||
select = [
|
||||
# "C", # TODO: mccabe - code complecity
|
||||
"C4",
|
||||
"E",
|
||||
"F",
|
||||
"T10",
|
||||
"T20",
|
||||
"Q",
|
||||
"RET",
|
||||
"I"
|
||||
]
|
||||
src = ["aiogram", "tests"]
|
||||
exclude = [
|
||||
".git",
|
||||
"build",
|
||||
"dist",
|
||||
"venv",
|
||||
".venv",
|
||||
"docs",
|
||||
"tests",
|
||||
"dev",
|
||||
"scripts",
|
||||
"*.egg-info",
|
||||
]
|
||||
target-version = "py310"
|
||||
|
||||
[tool.ruff.isort]
|
||||
known-first-party = [
|
||||
"aiogram",
|
||||
"finite_state_machine",
|
||||
"handlers",
|
||||
"routes",
|
||||
]
|
||||
|
||||
[tool.ruff.per-file-ignores]
|
||||
"aiogram/client/bot.py" = ["E501"]
|
||||
"aiogram/types/*" = ["E501"]
|
||||
"aiogram/methods/*" = ["E501"]
|
||||
"aiogram/enums/*" = ["E501"]
|
||||
|
||||
|
||||
[tool.poetry.group.test.dependencies]
|
||||
pytest = "^7.1.3"
|
||||
pytest-html = "^3.1.1"
|
||||
pytest-asyncio = "^0.19.0"
|
||||
pytest-lazy-fixture = "^0.6.3"
|
||||
pytest-mock = "^3.9.0"
|
||||
pytest-mypy = "^0.10.0"
|
||||
pytest-cov = "^4.0.0"
|
||||
pytest-aiohttp = "^1.0.4"
|
||||
aresponses = "^2.1.6"
|
||||
[tool.pytest.ini_options]
|
||||
asyncio_mode = "auto"
|
||||
testpaths = [
|
||||
"tests",
|
||||
]
|
||||
|
||||
[tool.coverage.run]
|
||||
branch = true
|
||||
parallel = true
|
||||
omit = [
|
||||
"aiogram/__about__.py",
|
||||
]
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
black = "^22.8.0"
|
||||
isort = "^5.10.1"
|
||||
flake8 = "^5.0.4"
|
||||
mypy = "^0.981"
|
||||
toml = "^0.10.2"
|
||||
pre-commit = "^2.20.0"
|
||||
packaging = "^21.3"
|
||||
typing-extensions = "^4.3.0"
|
||||
butcher = { git = "https://github.com/aiogram/butcher.git", rev = "v0.1.8", python = "3.10" }
|
||||
[tool.coverage.report]
|
||||
exclude_lines = [
|
||||
"if __name__ == .__main__.:",
|
||||
"pragma: no cover",
|
||||
"if TYPE_CHECKING:",
|
||||
"@abstractmethod",
|
||||
"@overload",
|
||||
]
|
||||
|
||||
[tool.mypy]
|
||||
strict = true
|
||||
|
||||
[tool.poetry.extras]
|
||||
fast = ["uvloop"]
|
||||
redis = ["redis"]
|
||||
proxy = ["aiohttp-socks"]
|
||||
i18n = ["Babel"]
|
||||
[[tool.mypy.overrides]]
|
||||
module = "mypy-aiofiles"
|
||||
ignore_missing_imports = true
|
||||
|
||||
[[tool.mypy.overrides]]
|
||||
module = "mypy-async_lru"
|
||||
ignore_missing_imports = true
|
||||
|
||||
[[tool.mypy.overrides]]
|
||||
module = "mypy-uvloop"
|
||||
ignore_missing_imports = true
|
||||
|
||||
[[tool.mypy.overrides]]
|
||||
module = "mypy-redis.*"
|
||||
ignore_missing_imports = true
|
||||
|
||||
[[tool.mypy.overrides]]
|
||||
module = "mypy-babel.*"
|
||||
ignore_missing_imports = true
|
||||
|
||||
[tool.black]
|
||||
line-length = 99
|
||||
target-version = ['py38', 'py39', 'py310']
|
||||
target-version = ['py38', 'py39', 'py310', 'py311']
|
||||
exclude = '''
|
||||
(
|
||||
\.eggs
|
||||
|
|
@ -122,21 +276,7 @@ exclude = '''
|
|||
'''
|
||||
|
||||
[tool.isort]
|
||||
multi_line_output = 3
|
||||
include_trailing_comma = true
|
||||
force_grid_wrap = 0
|
||||
use_parentheses = true
|
||||
line_length = 99
|
||||
known_third_party = [
|
||||
"aiofiles",
|
||||
"aiohttp",
|
||||
"aiohttp_socks",
|
||||
"aresponses",
|
||||
"packaging",
|
||||
"pkg_resources",
|
||||
"pydantic",
|
||||
"pytest"
|
||||
]
|
||||
profile = "black"
|
||||
|
||||
[tool.towncrier]
|
||||
package = "aiogram"
|
||||
|
|
@ -172,7 +312,3 @@ showcontent = true
|
|||
directory = "misc"
|
||||
name = "Misc"
|
||||
showcontent = true
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
[pytest]
|
||||
asyncio_mode = auto
|
||||
testpaths =
|
||||
tests
|
||||
|
|
@ -5,7 +5,11 @@ from _pytest.config import UsageError
|
|||
from redis.asyncio.connection import parse_url as parse_redis_url
|
||||
|
||||
from aiogram import Bot, Dispatcher
|
||||
from aiogram.fsm.storage.memory import DisabledEventIsolation, MemoryStorage, SimpleEventIsolation
|
||||
from aiogram.fsm.storage.memory import (
|
||||
DisabledEventIsolation,
|
||||
MemoryStorage,
|
||||
SimpleEventIsolation,
|
||||
)
|
||||
from aiogram.fsm.storage.redis import RedisEventIsolation, RedisStorage
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
from aiogram import Bot
|
||||
from aiogram.methods import AnswerInlineQuery, Request
|
||||
from aiogram.types import InlineQueryResult, InlineQueryResultPhoto, InputTextMessageContent
|
||||
from aiogram.types import (
|
||||
InlineQueryResult,
|
||||
InlineQueryResultPhoto,
|
||||
InputTextMessageContent,
|
||||
)
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
from aiogram.methods import AnswerShippingQuery
|
||||
from aiogram.types import LabeledPrice, ShippingAddress, ShippingOption, ShippingQuery, User
|
||||
from aiogram.types import (
|
||||
LabeledPrice,
|
||||
ShippingAddress,
|
||||
ShippingOption,
|
||||
ShippingQuery,
|
||||
User,
|
||||
)
|
||||
|
||||
|
||||
class TestInlineQuery:
|
||||
|
|
|
|||
|
|
@ -5,7 +5,15 @@ from typing import Sequence, Type
|
|||
import pytest
|
||||
|
||||
from aiogram.filters import Text
|
||||
from aiogram.types import CallbackQuery, Chat, InlineQuery, Message, Poll, PollOption, User
|
||||
from aiogram.types import (
|
||||
CallbackQuery,
|
||||
Chat,
|
||||
InlineQuery,
|
||||
Message,
|
||||
Poll,
|
||||
PollOption,
|
||||
User,
|
||||
)
|
||||
|
||||
|
||||
class TestText:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
import pytest
|
||||
|
||||
from aiogram.fsm.storage.base import DEFAULT_DESTINY, StorageKey
|
||||
from aiogram.fsm.storage.redis import DefaultKeyBuilder, RedisEventIsolation, RedisStorage
|
||||
from aiogram.fsm.storage.redis import (
|
||||
DefaultKeyBuilder,
|
||||
RedisEventIsolation,
|
||||
RedisStorage,
|
||||
)
|
||||
|
||||
PREFIX = "test"
|
||||
BOT_ID = 42
|
||||
|
|
|
|||
|
|
@ -7,7 +7,12 @@ from aiogram.fsm.context import FSMContext
|
|||
from aiogram.fsm.storage.base import StorageKey
|
||||
from aiogram.fsm.storage.memory import MemoryStorage
|
||||
from aiogram.types import Update, User
|
||||
from aiogram.utils.i18n import ConstI18nMiddleware, FSMI18nMiddleware, I18n, SimpleI18nMiddleware
|
||||
from aiogram.utils.i18n import (
|
||||
ConstI18nMiddleware,
|
||||
FSMI18nMiddleware,
|
||||
I18n,
|
||||
SimpleI18nMiddleware,
|
||||
)
|
||||
from aiogram.utils.i18n.context import get_i18n, gettext, lazy_gettext
|
||||
from tests.conftest import DATA_DIR
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
|
|
|||
|
|
@ -7,7 +7,11 @@ from aiogram.types import (
|
|||
KeyboardButton,
|
||||
ReplyKeyboardMarkup,
|
||||
)
|
||||
from aiogram.utils.keyboard import InlineKeyboardBuilder, KeyboardBuilder, ReplyKeyboardBuilder
|
||||
from aiogram.utils.keyboard import (
|
||||
InlineKeyboardBuilder,
|
||||
KeyboardBuilder,
|
||||
ReplyKeyboardBuilder,
|
||||
)
|
||||
|
||||
|
||||
class MyCallback(CallbackData, prefix="test"):
|
||||
|
|
|
|||
|
|
@ -3,7 +3,11 @@ from typing import List, Optional
|
|||
import pytest
|
||||
|
||||
from aiogram.types import MessageEntity, User
|
||||
from aiogram.utils.text_decorations import TextDecoration, html_decoration, markdown_decoration
|
||||
from aiogram.utils.text_decorations import (
|
||||
TextDecoration,
|
||||
html_decoration,
|
||||
markdown_decoration,
|
||||
)
|
||||
|
||||
|
||||
class TestTextDecoration:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue