diff --git a/aiogram/enums/bot_command_scope_type.py b/aiogram/enums/bot_command_scope_type.py index 387e4d05..f0994316 100644 --- a/aiogram/enums/bot_command_scope_type.py +++ b/aiogram/enums/bot_command_scope_type.py @@ -15,4 +15,3 @@ class BotCommandScopeType(str, Enum): CHAT = "chat" CHAT_ADMINISTRATORS = "chat_administrators" CHAT_MEMBER = "chat_member" - ALL = "all" diff --git a/aiogram/types/__init__.py b/aiogram/types/__init__.py index 8c262c74..b1174eb1 100644 --- a/aiogram/types/__init__.py +++ b/aiogram/types/__init__.py @@ -15,7 +15,6 @@ from .bot_command_scope_default import BotCommandScopeDefault from .callback_game import CallbackGame from .callback_query import CallbackQuery from .chat import Chat -from .chat_action import ChatAction from .chat_administrator_rights import ChatAdministratorRights from .chat_invite_link import ChatInviteLink from .chat_join_request import ChatJoinRequest @@ -286,7 +285,6 @@ __all__ = ( "Game", "CallbackGame", "GameHighScore", - "ChatAction", ) # Load typing forward refs for every TelegramObject diff --git a/aiogram/types/bot_command_scope_all_chat_administrators.py b/aiogram/types/bot_command_scope_all_chat_administrators.py index 25a35cbf..e9f6a969 100644 --- a/aiogram/types/bot_command_scope_all_chat_administrators.py +++ b/aiogram/types/bot_command_scope_all_chat_administrators.py @@ -2,6 +2,7 @@ from __future__ import annotations from pydantic import Field +from ..enums import BotCommandScopeType from .bot_command_scope import BotCommandScope @@ -12,5 +13,5 @@ class BotCommandScopeAllChatAdministrators(BotCommandScope): Source: https://core.telegram.org/bots/api#botcommandscopeallchatadministrators """ - type: str = Field("all_chat_administrators", const=True) + type: str = Field(BotCommandScopeType.ALL_CHAT_ADMINISTRATORS, const=True) """Scope type, must be *all_chat_administrators*""" diff --git a/aiogram/types/bot_command_scope_all_group_chats.py b/aiogram/types/bot_command_scope_all_group_chats.py index 00e2984e..f9675ad6 100644 --- a/aiogram/types/bot_command_scope_all_group_chats.py +++ b/aiogram/types/bot_command_scope_all_group_chats.py @@ -2,6 +2,7 @@ from __future__ import annotations from pydantic import Field +from ..enums import BotCommandScopeType from .bot_command_scope import BotCommandScope @@ -12,5 +13,5 @@ class BotCommandScopeAllGroupChats(BotCommandScope): Source: https://core.telegram.org/bots/api#botcommandscopeallgroupchats """ - type: str = Field("all_group_chats", const=True) + type: str = Field(BotCommandScopeType.ALL_GROUP_CHATS, const=True) """Scope type, must be *all_group_chats*""" diff --git a/aiogram/types/bot_command_scope_all_private_chats.py b/aiogram/types/bot_command_scope_all_private_chats.py index debc3baf..f13e2866 100644 --- a/aiogram/types/bot_command_scope_all_private_chats.py +++ b/aiogram/types/bot_command_scope_all_private_chats.py @@ -2,6 +2,7 @@ from __future__ import annotations from pydantic import Field +from ..enums import BotCommandScopeType from .bot_command_scope import BotCommandScope @@ -12,5 +13,5 @@ class BotCommandScopeAllPrivateChats(BotCommandScope): Source: https://core.telegram.org/bots/api#botcommandscopeallprivatechats """ - type: str = Field("all_private_chats", const=True) + type: str = Field(BotCommandScopeType.ALL_PRIVATE_CHATS, const=True) """Scope type, must be *all_private_chats*""" diff --git a/aiogram/types/bot_command_scope_chat.py b/aiogram/types/bot_command_scope_chat.py index 5d89c046..d96bc6f4 100644 --- a/aiogram/types/bot_command_scope_chat.py +++ b/aiogram/types/bot_command_scope_chat.py @@ -4,6 +4,7 @@ from typing import Union from pydantic import Field +from ..enums import BotCommandScopeType from .bot_command_scope import BotCommandScope @@ -14,7 +15,7 @@ class BotCommandScopeChat(BotCommandScope): Source: https://core.telegram.org/bots/api#botcommandscopechat """ - type: str = Field("chat", const=True) + type: str = Field(BotCommandScopeType.CHAT, const=True) """Scope type, must be *chat*""" chat_id: Union[int, str] """Unique identifier for the target chat or username of the target supergroup (in the format :code:`@supergroupusername`)""" diff --git a/aiogram/types/bot_command_scope_chat_administrators.py b/aiogram/types/bot_command_scope_chat_administrators.py index 152eab13..824dc5a1 100644 --- a/aiogram/types/bot_command_scope_chat_administrators.py +++ b/aiogram/types/bot_command_scope_chat_administrators.py @@ -4,6 +4,7 @@ from typing import Union from pydantic import Field +from ..enums import BotCommandScopeType from .bot_command_scope import BotCommandScope @@ -14,7 +15,7 @@ class BotCommandScopeChatAdministrators(BotCommandScope): Source: https://core.telegram.org/bots/api#botcommandscopechatadministrators """ - type: str = Field("chat_administrators", const=True) + type: str = Field(BotCommandScopeType.CHAT_ADMINISTRATORS, const=True) """Scope type, must be *chat_administrators*""" chat_id: Union[int, str] """Unique identifier for the target chat or username of the target supergroup (in the format :code:`@supergroupusername`)""" diff --git a/aiogram/types/bot_command_scope_chat_member.py b/aiogram/types/bot_command_scope_chat_member.py index e69ff642..e9fb0dda 100644 --- a/aiogram/types/bot_command_scope_chat_member.py +++ b/aiogram/types/bot_command_scope_chat_member.py @@ -4,6 +4,7 @@ from typing import Union from pydantic import Field +from ..enums import BotCommandScopeType from .bot_command_scope import BotCommandScope @@ -14,7 +15,7 @@ class BotCommandScopeChatMember(BotCommandScope): Source: https://core.telegram.org/bots/api#botcommandscopechatmember """ - type: str = Field("chat_member", const=True) + type: str = Field(BotCommandScopeType.CHAT_MEMBER, const=True) """Scope type, must be *chat_member*""" chat_id: Union[int, str] """Unique identifier for the target chat or username of the target supergroup (in the format :code:`@supergroupusername`)""" diff --git a/aiogram/types/bot_command_scope_default.py b/aiogram/types/bot_command_scope_default.py index 8cf1a1d5..79825631 100644 --- a/aiogram/types/bot_command_scope_default.py +++ b/aiogram/types/bot_command_scope_default.py @@ -2,6 +2,7 @@ from __future__ import annotations from pydantic import Field +from ..enums import BotCommandScopeType from .bot_command_scope import BotCommandScope @@ -12,5 +13,5 @@ class BotCommandScopeDefault(BotCommandScope): Source: https://core.telegram.org/bots/api#botcommandscopedefault """ - type: str = Field("default", const=True) + type: str = Field(BotCommandScopeType.DEFAULT, const=True) """Scope type, must be *default*""" diff --git a/aiogram/types/chat_action.py b/aiogram/types/chat_action.py deleted file mode 100644 index 619a0dc5..00000000 --- a/aiogram/types/chat_action.py +++ /dev/null @@ -1,33 +0,0 @@ -import enum - -from ..utils.enum import AutoName - - -class ChatAction(AutoName): - """ - This object represents bot actions. - - Choose one, depending on what the user is about to receive: - • typing for text messages, - • upload_photo for photos, - • record_video or upload_video for videos, - • record_voice or upload_voice for voice notes, - • upload_document for general files, - • choose_sticker for stickers, - • find_location for location data, - • record_video_note or upload_video_note for video notes. - - Source: https://core.telegram.org/bots/api#sendchataction - """ - - TYPING = enum.auto() - UPLOAD_PHOTO = enum.auto() - RECORD_VIDEO = enum.auto() - UPLOAD_VIDEO = enum.auto() - RECORD_VOICE = enum.auto() - UPLOAD_VOICE = enum.auto() - UPLOAD_DOCUMENT = enum.auto() - CHOOSE_STICKER = enum.auto() - FIND_LOCATION = enum.auto() - RECORD_VIDEO_NOTE = enum.auto() - UPLOAD_VIDEO_NOTE = enum.auto() diff --git a/aiogram/types/chat_member_administrator.py b/aiogram/types/chat_member_administrator.py index 1c9a8aae..4692981d 100644 --- a/aiogram/types/chat_member_administrator.py +++ b/aiogram/types/chat_member_administrator.py @@ -4,6 +4,7 @@ from typing import TYPE_CHECKING, Optional from pydantic import Field +from ..enums import ChatMemberStatus from .chat_member import ChatMember if TYPE_CHECKING: @@ -17,7 +18,7 @@ class ChatMemberAdministrator(ChatMember): Source: https://core.telegram.org/bots/api#chatmemberadministrator """ - status: str = Field("administrator", const=True) + status: str = Field(ChatMemberStatus.ADMINISTRATOR, const=True) """The member's status in the chat, always 'administrator'""" user: User """Information about the user""" diff --git a/aiogram/types/chat_member_banned.py b/aiogram/types/chat_member_banned.py index cbed5493..85c07f51 100644 --- a/aiogram/types/chat_member_banned.py +++ b/aiogram/types/chat_member_banned.py @@ -5,6 +5,7 @@ from typing import TYPE_CHECKING from pydantic import Field +from ..enums import ChatMemberStatus from .chat_member import ChatMember if TYPE_CHECKING: @@ -18,7 +19,7 @@ class ChatMemberBanned(ChatMember): Source: https://core.telegram.org/bots/api#chatmemberbanned """ - status: str = Field("kicked", const=True) + status: str = Field(ChatMemberStatus.KICKED, const=True) """The member's status in the chat, always 'kicked'""" user: User """Information about the user""" diff --git a/aiogram/types/chat_member_left.py b/aiogram/types/chat_member_left.py index 02df05de..6d7968c1 100644 --- a/aiogram/types/chat_member_left.py +++ b/aiogram/types/chat_member_left.py @@ -4,6 +4,7 @@ from typing import TYPE_CHECKING from pydantic import Field +from ..enums import ChatMemberStatus from .chat_member import ChatMember if TYPE_CHECKING: @@ -17,7 +18,7 @@ class ChatMemberLeft(ChatMember): Source: https://core.telegram.org/bots/api#chatmemberleft """ - status: str = Field("left", const=True) + status: str = Field(ChatMemberStatus.LEFT, const=True) """The member's status in the chat, always 'left'""" user: User """Information about the user""" diff --git a/aiogram/types/chat_member_member.py b/aiogram/types/chat_member_member.py index db5d5731..303a7d9d 100644 --- a/aiogram/types/chat_member_member.py +++ b/aiogram/types/chat_member_member.py @@ -4,6 +4,7 @@ from typing import TYPE_CHECKING from pydantic import Field +from ..enums import ChatMemberStatus from .chat_member import ChatMember if TYPE_CHECKING: @@ -17,7 +18,7 @@ class ChatMemberMember(ChatMember): Source: https://core.telegram.org/bots/api#chatmembermember """ - status: str = Field("member", const=True) + status: str = Field(ChatMemberStatus.MEMBER, const=True) """The member's status in the chat, always 'member'""" user: User """Information about the user""" diff --git a/aiogram/types/chat_member_owner.py b/aiogram/types/chat_member_owner.py index 30853996..e7c64fc1 100644 --- a/aiogram/types/chat_member_owner.py +++ b/aiogram/types/chat_member_owner.py @@ -4,6 +4,7 @@ from typing import TYPE_CHECKING, Optional from pydantic import Field +from ..enums import ChatMemberStatus from .chat_member import ChatMember if TYPE_CHECKING: @@ -17,7 +18,7 @@ class ChatMemberOwner(ChatMember): Source: https://core.telegram.org/bots/api#chatmemberowner """ - status: str = Field("creator", const=True) + status: str = Field(ChatMemberStatus.CREATOR, const=True) """The member's status in the chat, always 'creator'""" user: User """Information about the user""" diff --git a/aiogram/types/chat_member_restricted.py b/aiogram/types/chat_member_restricted.py index cc6fe679..916b343b 100644 --- a/aiogram/types/chat_member_restricted.py +++ b/aiogram/types/chat_member_restricted.py @@ -5,6 +5,7 @@ from typing import TYPE_CHECKING from pydantic import Field +from ..enums import ChatMemberStatus from .chat_member import ChatMember if TYPE_CHECKING: @@ -18,7 +19,7 @@ class ChatMemberRestricted(ChatMember): Source: https://core.telegram.org/bots/api#chatmemberrestricted """ - status: str = Field("restricted", const=True) + status: str = Field(ChatMemberStatus.RESTRICTED, const=True) """The member's status in the chat, always 'restricted'""" user: User """Information about the user""" diff --git a/aiogram/types/menu_button_commands.py b/aiogram/types/menu_button_commands.py index f115bdd6..62a9061c 100644 --- a/aiogram/types/menu_button_commands.py +++ b/aiogram/types/menu_button_commands.py @@ -2,6 +2,7 @@ from __future__ import annotations from pydantic import Field +from ..enums import MenuButtonType from .menu_button import MenuButton @@ -12,5 +13,5 @@ class MenuButtonCommands(MenuButton): Source: https://core.telegram.org/bots/api#menubuttoncommands """ - type: str = Field("commands", const=True) + type: str = Field(MenuButtonType.COMMANDS, const=True) """Type of the button, must be *commands*""" diff --git a/aiogram/types/menu_button_default.py b/aiogram/types/menu_button_default.py index 2ff09546..dc754ec0 100644 --- a/aiogram/types/menu_button_default.py +++ b/aiogram/types/menu_button_default.py @@ -2,6 +2,7 @@ from __future__ import annotations from pydantic import Field +from ..enums import MenuButtonType from .menu_button import MenuButton @@ -12,5 +13,5 @@ class MenuButtonDefault(MenuButton): Source: https://core.telegram.org/bots/api#menubuttondefault """ - type: str = Field("default", const=True) + type: str = Field(MenuButtonType.DEFAULT, const=True) """Type of the button, must be *default*""" diff --git a/aiogram/types/menu_button_web_app.py b/aiogram/types/menu_button_web_app.py index 982dfa06..f77ed2ea 100644 --- a/aiogram/types/menu_button_web_app.py +++ b/aiogram/types/menu_button_web_app.py @@ -4,6 +4,7 @@ from typing import TYPE_CHECKING from pydantic import Field +from ..enums import MenuButtonType from .menu_button import MenuButton if TYPE_CHECKING: @@ -17,7 +18,7 @@ class MenuButtonWebApp(MenuButton): Source: https://core.telegram.org/bots/api#menubuttonwebapp """ - type: str = Field("web_app", const=True) + type: str = Field(MenuButtonType.WEB_APP, const=True) """Type of the button, must be *web_app*""" text: str """Text on the button""" diff --git a/aiogram/utils/helper.py b/aiogram/utils/helper.py deleted file mode 100644 index 97b6f008..00000000 --- a/aiogram/utils/helper.py +++ /dev/null @@ -1,293 +0,0 @@ -""" -Example: - >>> from aiogram.utils.helper import Helper, ListItem, HelperMode, Item - >>> class MyHelper(Helper): - ... mode = HelperMode.lowerCamelCase - ... FOO_ITEM = ListItem() - ... BAR_ITEM = ListItem() - ... BAZ_ITEM = ListItem() - ... LOREM = Item() - ... - >>> print(MyHelper.FOO_ITEM & MyHelper.BAR_ITEM) - <<< ['fooItem', 'barItem'] - >>> print(MyHelper.all()) - <<< ['barItem', 'bazItem', 'fooItem', 'lorem'] -""" -import inspect -from typing import Any, Callable, Generic, Iterable, List, Optional, TypeVar, Union, cast -from weakref import WeakKeyDictionary - -T = TypeVar("T") - -PROPS_KEYS_ATTR_NAME = "_props_keys" - - -class Helper: - mode = "" - - @classmethod - def all(cls) -> List[Any]: - """ - Get all consts - :return: list - """ - result: List[Any] = [] - for name in dir(cls): - if not name.isupper(): - continue - value = getattr(cls, name) - if isinstance(value, ItemsList): - result.append(value[0]) - else: - result.append(value) - return result - - -class HelperMode(Helper): - mode = "original" - - SCREAMING_SNAKE_CASE = "SCREAMING_SNAKE_CASE" - lowerCamelCase = "lowerCamelCase" - CamelCase = "CamelCase" - snake_case = "snake_case" - lowercase = "lowercase" - - @classmethod - def all(cls) -> List[str]: - return [ - cls.SCREAMING_SNAKE_CASE, - cls.lowerCamelCase, - cls.CamelCase, - cls.snake_case, - cls.lowercase, - ] - - @classmethod - def _screaming_snake_case(cls, text: str) -> str: - """ - Transform text to SCREAMING_SNAKE_CASE - - :param text: - :return: - """ - if text.isupper(): - return text - result = "" - for pos, symbol in enumerate(text): - if symbol.isupper() and pos > 0: - result += "_" + symbol - else: - result += symbol.upper() - return result - - @classmethod - def _snake_case(cls, text: str) -> str: - """ - Transform text to snake case (Based on SCREAMING_SNAKE_CASE) - - :param text: - :return: - """ - if text.islower(): - return text - return cls._screaming_snake_case(text).lower() - - @classmethod - def _camel_case(cls, text: str, first_upper: bool = False) -> str: - """ - Transform text to camelCase or CamelCase - - :param text: - :param first_upper: first symbol must be upper? - :return: - """ - result = "" - need_upper = False - for pos, symbol in enumerate(text): - if symbol == "_" and pos > 0: - need_upper = True - else: - if need_upper: - result += symbol.upper() - else: - result += symbol.lower() - need_upper = False - if first_upper: - result = result[0].upper() + result[1:] - return result - - @classmethod - def apply(cls, text: str, mode: Union[str, Callable[[str], str]]) -> str: - """ - Apply mode for text - - :param text: - :param mode: - :return: - """ - if mode == cls.SCREAMING_SNAKE_CASE: - return cls._screaming_snake_case(text) - if mode == cls.snake_case: - return cls._snake_case(text) - if mode == cls.lowercase: - return cls._snake_case(text).replace("_", "") - if mode == cls.lowerCamelCase: - return cls._camel_case(text) - if mode == cls.CamelCase: - return cls._camel_case(text, True) - if callable(mode): - return mode(text) - return text - - -class _BaseItem: - def __init__(self, value: Optional[str] = None): - self._value = cast(str, value) - - def __set_name__(self, owner: Any, name: str) -> None: - if not name.isupper(): - raise NameError("Name for helper item must be in uppercase!") - if not self._value: - if not inspect.isclass(owner) or not issubclass(owner, Helper): - raise RuntimeError("Instances of Item can be used only as Helper attributes") - self._value = HelperMode.apply(name, owner.mode) - - -class Item(_BaseItem): - """ - Helper item - - If a value is not provided, - it will be automatically generated based on a variable's name - """ - - def __get__(self, instance: Any, owner: Any) -> str: - return self._value - - -class ListItem(_BaseItem): - """ - This item is always a list - - You can use &, | and + operators for that. - """ - - def add(self, other: "ListItem") -> "ListItem": # pragma: no cover - return self + other - - def __get__(self, instance: Any, owner: Any) -> "ItemsList": - return ItemsList(self._value) - - def __getitem__(self, item: Any) -> Any: # pragma: no cover - # Only for IDE. This method is never be called. - return self._value - - # Need only for IDE - __iadd__ = __add__ = __rand__ = __and__ = __ror__ = __or__ = add - - -class ItemsList(List[str]): - """ - Patch for default list - - This class provides +, &, |, +=, &=, |= operators for extending the list - """ - - def __init__(self, *seq: Any): - super(ItemsList, self).__init__(map(str, seq)) - - def add(self, other: Iterable[str]) -> "ItemsList": - self.extend(other) - return self - - __iadd__ = __add__ = __rand__ = __and__ = __ror__ = __or__ = add # type: ignore - - -class OrderedHelperMeta(type): - def __new__(mcs, name: Any, bases: Any, namespace: Any, **kwargs: Any) -> "OrderedHelperMeta": - cls = super().__new__(mcs, name, bases, namespace) - - props_keys = [] - - for prop_name in ( - name for name, prop in namespace.items() if isinstance(prop, (Item, ListItem)) - ): - props_keys.append(prop_name) - - setattr(cls, PROPS_KEYS_ATTR_NAME, props_keys) - - return cls - - -class OrderedHelper(Helper, metaclass=OrderedHelperMeta): - mode = "" - - @classmethod - def all(cls) -> List[str]: - """ - Get all Items values - """ - result = [] - for name in getattr(cls, PROPS_KEYS_ATTR_NAME, []): - value = getattr(cls, name) - if isinstance(value, ItemsList): - result.append(value[0]) - else: - result.append(value) - return result - - -class Default(Generic[T]): - """ - Descriptor that holds default value getter - - Example: - >>> class MyClass: - ... att = Default("dflt") - ... - >>> my_instance = MyClass() - >>> my_instance.att = "not dflt" - >>> my_instance.att - 'not dflt' - >>> MyClass.att - 'dflt' - >>> del my_instance.att - >>> my_instance.att - 'dflt' - >>> - - Intended to be used as a class attribute and only internally. - """ - - __slots__ = "fget", "_descriptor_instances" - - def __init__( - self, - default: Optional[T] = None, - *, - fget: Optional[Callable[[Any], T]] = None, - ) -> None: - self.fget = fget or (lambda _: cast(T, default)) - self._descriptor_instances = WeakKeyDictionary() # type: ignore - - def __get__(self, instance: Any, owner: Any) -> T: - if instance is None: - return self.fget(instance) - - return self._descriptor_instances.get(instance, self.fget(instance)) - - def __set__(self, instance: Any, value: T) -> None: - if instance is None or isinstance(instance, type): - raise AttributeError( - "Instance cannot be class or None. Setter must be called from a class." - ) - - self._descriptor_instances[instance] = value - - def __delete__(self, instance: Any) -> None: - if instance is None or isinstance(instance, type): - raise AttributeError( - "Instance cannot be class or None. Deleter must be called from a class." - ) - - self._descriptor_instances.pop(instance, None) diff --git a/aiogram/utils/text_decorations.py b/aiogram/utils/text_decorations.py index 36e41219..93dc7111 100644 --- a/aiogram/utils/text_decorations.py +++ b/aiogram/utils/text_decorations.py @@ -5,6 +5,8 @@ import re from abc import ABC, abstractmethod from typing import TYPE_CHECKING, Generator, List, Optional, Pattern, cast +from aiogram.enums import MessageEntityType + if TYPE_CHECKING: from aiogram.types import MessageEntity @@ -36,25 +38,37 @@ class TextDecoration(ABC): :param text: :return: """ - if entity.type in {"bot_command", "url", "mention", "phone_number"}: + if entity.type in { + MessageEntityType.BOT_COMMAND, + MessageEntityType.URL, + MessageEntityType.MENTION, + MessageEntityType.PHONE_NUMBER, + }: # This entities should not be changed return text - if entity.type in {"bold", "italic", "code", "underline", "strikethrough", "spoiler"}: + if entity.type in { + MessageEntityType.BOLD, + MessageEntityType.ITALIC, + MessageEntityType.CODE, + MessageEntityType.UNDERLINE, + MessageEntityType.STRIKETHROUGH, + MessageEntityType.SPOILER, + }: return cast(str, getattr(self, entity.type)(value=text)) - if entity.type == "pre": + if entity.type == MessageEntityType.PRE: return ( self.pre_language(value=text, language=entity.language) if entity.language else self.pre(value=text) ) - if entity.type == "text_mention": + if entity.type == MessageEntityType.TEXT_MENTION: from aiogram.types import User user = cast(User, entity.user) return self.link(value=text, link=f"tg://user?id={user.id}") - if entity.type == "text_link": + if entity.type == MessageEntityType.TEXT_LINK: return self.link(value=text, link=cast(str, entity.url)) - if entity.type == "custom_emoji": + if entity.type == MessageEntityType.CUSTOM_EMOJI: return self.custom_emoji(value=text, custom_emoji_id=cast(str, entity.custom_emoji_id)) return self.quote(text) diff --git a/tests/test_api/test_methods/test_send_chat_action.py b/tests/test_api/test_methods/test_send_chat_action.py index 98c9d016..41f9e3e4 100644 --- a/tests/test_api/test_methods/test_send_chat_action.py +++ b/tests/test_api/test_methods/test_send_chat_action.py @@ -1,7 +1,7 @@ import pytest +from aiogram.enums import ChatAction from aiogram.methods import Request, SendChatAction -from aiogram.types import ChatAction from tests.mocked_bot import MockedBot pytestmark = pytest.mark.asyncio diff --git a/tests/test_utils/test_helper.py b/tests/test_utils/test_helper.py deleted file mode 100644 index 466db4ef..00000000 --- a/tests/test_utils/test_helper.py +++ /dev/null @@ -1,181 +0,0 @@ -import pytest - -from aiogram.utils.helper import Default, Helper, HelperMode, Item, ListItem, OrderedHelper - - -class TestHelper: - def test_items_all(self): - class MyHelper(Helper): - A = Item() - B = Item() - C = Item() - D = Item() - - assert set(MyHelper.all()) == {"A", "B", "C", "D"} - - def test_listed_items_all(self): - class MyHelper(Helper): - A = ListItem() - B = ListItem() - C = ListItem() - D = ListItem() - - assert set(MyHelper.all()) == {"A", "B", "C", "D"} - - def test_listed_items_combinations(self): - class MyHelper(Helper): - A = ListItem() - B = ListItem() - C = ListItem() - D = ListItem() - - assert (MyHelper.A | MyHelper.B) == ["A", "B"] - assert (MyHelper.C & MyHelper.D) == ["C", "D"] - assert MyHelper.A.add(MyHelper.D) == ["A", "D"] - assert MyHelper.B + MyHelper.D == ["B", "D"] - - def test_wrong_name(self): - with pytest.raises(RuntimeError): - - class MyHelper(Helper): - kaboom = Item() - - def test_not_a_helper_subclass(self): - with pytest.raises(RuntimeError): - - class NotAHelperSubclass: - A = Item() - - -class TestHelperMode: - def test_helper_mode_all(self): - assert set(HelperMode.all()) == { - "SCREAMING_SNAKE_CASE", - "lowerCamelCase", - "CamelCase", - "snake_case", - "lowercase", - } - - def test_screaming_snake_case(self): - class MyHelper(Helper): - mode = HelperMode.SCREAMING_SNAKE_CASE - - FOO = Item() - BAR_BAZ = Item() - - assert MyHelper.FOO == "FOO" - assert MyHelper.BAR_BAZ == "BAR_BAZ" - - def test_lower_camel_case(self): - class MyHelper(Helper): - mode = HelperMode.lowerCamelCase - - FOO = Item() - BAR_BAZ = Item() - - assert MyHelper.FOO == "foo" - assert MyHelper.BAR_BAZ == "barBaz" - - def test_camel_case(self): - class MyHelper(Helper): - mode = HelperMode.CamelCase - - FOO = Item() - BAR_BAZ = Item() - - assert MyHelper.FOO == "Foo" - assert MyHelper.BAR_BAZ == "BarBaz" - - def test_snake_case(self): - class MyHelper(Helper): - mode = HelperMode.snake_case - - FOO = Item() - BAR_BAZ = Item() - - assert MyHelper.FOO == "foo" - assert MyHelper.BAR_BAZ == "bar_baz" - - def test_lowercase(self): - class MyHelper(Helper): - mode = HelperMode.lowercase - - FOO = Item() - BAR_BAZ = Item() - - assert MyHelper.FOO == "foo" - assert MyHelper.BAR_BAZ == "barbaz" - - def test_extended_converters(self): - assert HelperMode.apply("test_text", mode=HelperMode.SCREAMING_SNAKE_CASE) == "TEST_TEXT" - assert HelperMode.apply("TestText", mode=HelperMode.SCREAMING_SNAKE_CASE) == "TEST_TEXT" - assert HelperMode.apply("test_text", mode=HelperMode.snake_case) == "test_text" - assert HelperMode.apply("foo", mode=lambda m: m.upper()) == "FOO" - - -class TestOrderedHelper: - def test_items_are_ordered(self): - class MyOrderedHelper(OrderedHelper): - A = Item() - D = Item() - C = Item() - B = Item() - - assert MyOrderedHelper.all() == ["A", "D", "C", "B"] - - def test_list_items_are_ordered(self): - class MyOrderedHelper(OrderedHelper): - A = ListItem() - D = ListItem() - C = ListItem() - B = ListItem() - - assert MyOrderedHelper.all() == ["A", "D", "C", "B"] - - -class TestDefaultDescriptor: - def test_descriptor_fs(self): - obj = type("ClassA", (), {})() - default_x_val = "some_x" - x = Default(default_x_val) - - # we can omit owner, usually it's just obj.__class__ - assert x.__get__(instance=obj, owner=None) == default_x_val - assert x.__get__(instance=obj, owner=obj.__class__) == default_x_val - - new_x_val = "new_x" - assert x.__set__(instance=obj, value=new_x_val) is None - - with pytest.raises(AttributeError) as exc: - x.__set__(instance=obj.__class__, value="will never be set") - assert "Instance cannot be class or None" in str(exc.value) - - assert x.__get__(instance=obj, owner=obj.__class__) == new_x_val - - with pytest.raises(AttributeError) as exc: - x.__delete__(instance=obj.__class__) - assert "Instance cannot be class or None" in str(exc.value) - - x.__delete__(instance=obj) - assert x.__get__(instance=obj, owner=obj.__class__) == default_x_val - - def test_init(self): - class A: - x = Default(fget=lambda a_inst: "nothing") - - assert isinstance(A.__dict__["x"], Default) - - a = A() - assert a.x == "nothing" - - x = Default("x") - assert x.__get__(None, None) == "x" - assert x.fget(None) == x.__get__(None, None) - - def test_nullability(self): - class A: - x = Default(default=None, fget=None) - - assert A.x is None - assert A().x is None