mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2024-11-21 22:56:38 +01:00
Rich Comparison for Bot (#2320)
* Make telegram.Bot comparable Signed-off-by: starry69 <starry369126@outlook.com> * Address review Signed-off-by: starry69 <starry369126@outlook.com> * Enhance tests & add docstring about comparison Signed-off-by: starry69 <starry369126@outlook.com> * Minor doc fix * Extend tests Co-authored-by: Hinrich Mahler <hinrich.mahler@freenet.de>
This commit is contained in:
parent
7a3fd83570
commit
b43a599e53
4 changed files with 59 additions and 41 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -84,3 +84,6 @@ telegram.jpg
|
||||||
|
|
||||||
# Exclude .exrc file for Vim
|
# Exclude .exrc file for Vim
|
||||||
.exrc
|
.exrc
|
||||||
|
|
||||||
|
# virtual env
|
||||||
|
venv*
|
||||||
|
|
|
@ -110,22 +110,6 @@ if TYPE_CHECKING:
|
||||||
RT = TypeVar('RT')
|
RT = TypeVar('RT')
|
||||||
|
|
||||||
|
|
||||||
def info(func: Callable[..., RT]) -> Callable[..., RT]:
|
|
||||||
# pylint: disable=W0212
|
|
||||||
@functools.wraps(func)
|
|
||||||
def decorator(self: 'Bot', *args: Any, **kwargs: Any) -> RT:
|
|
||||||
if not self.bot:
|
|
||||||
self.get_me()
|
|
||||||
|
|
||||||
if self._commands is None:
|
|
||||||
self.get_my_commands()
|
|
||||||
|
|
||||||
result = func(self, *args, **kwargs)
|
|
||||||
return result
|
|
||||||
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
|
|
||||||
def log(
|
def log(
|
||||||
func: Callable[..., RT], *args: Any, **kwargs: Any # pylint: disable=W0613
|
func: Callable[..., RT], *args: Any, **kwargs: Any # pylint: disable=W0613
|
||||||
) -> Callable[..., RT]:
|
) -> Callable[..., RT]:
|
||||||
|
@ -144,6 +128,10 @@ def log(
|
||||||
class Bot(TelegramObject):
|
class Bot(TelegramObject):
|
||||||
"""This object represents a Telegram Bot.
|
"""This object represents a Telegram Bot.
|
||||||
|
|
||||||
|
.. versionadded:: 13.2
|
||||||
|
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||||
|
considered equal, if their :attr:`bot` is equal.
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
Most bot methods have the argument ``api_kwargs`` which allows to pass arbitrary keywords
|
Most bot methods have the argument ``api_kwargs`` which allows to pass arbitrary keywords
|
||||||
to the Telegram API. This can be used to access new features of the API before they were
|
to the Telegram API. This can be used to access new features of the API before they were
|
||||||
|
@ -217,7 +205,7 @@ class Bot(TelegramObject):
|
||||||
|
|
||||||
self.base_url = str(base_url) + str(self.token)
|
self.base_url = str(base_url) + str(self.token)
|
||||||
self.base_file_url = str(base_file_url) + str(self.token)
|
self.base_file_url = str(base_file_url) + str(self.token)
|
||||||
self.bot: Optional[User] = None
|
self._bot: Optional[User] = None
|
||||||
self._commands: Optional[List[BotCommand]] = None
|
self._commands: Optional[List[BotCommand]] = None
|
||||||
self._request = request or Request()
|
self._request = request or Request()
|
||||||
self.logger = logging.getLogger(__name__)
|
self.logger = logging.getLogger(__name__)
|
||||||
|
@ -302,68 +290,69 @@ class Bot(TelegramObject):
|
||||||
|
|
||||||
return token
|
return token
|
||||||
|
|
||||||
@property # type: ignore
|
@property
|
||||||
@info
|
def bot(self) -> User:
|
||||||
|
""":class:`telegram.User`: User instance for the bot as returned by :meth:`get_me`."""
|
||||||
|
|
||||||
|
if self._bot is None:
|
||||||
|
self._bot = self.get_me()
|
||||||
|
return self._bot
|
||||||
|
|
||||||
|
@property
|
||||||
def id(self) -> int:
|
def id(self) -> int:
|
||||||
""":obj:`int`: Unique identifier for this bot."""
|
""":obj:`int`: Unique identifier for this bot."""
|
||||||
|
|
||||||
return self.bot.id # type: ignore
|
return self.bot.id
|
||||||
|
|
||||||
@property # type: ignore
|
@property
|
||||||
@info
|
|
||||||
def first_name(self) -> str:
|
def first_name(self) -> str:
|
||||||
""":obj:`str`: Bot's first name."""
|
""":obj:`str`: Bot's first name."""
|
||||||
|
|
||||||
return self.bot.first_name # type: ignore
|
return self.bot.first_name
|
||||||
|
|
||||||
@property # type: ignore
|
@property
|
||||||
@info
|
|
||||||
def last_name(self) -> str:
|
def last_name(self) -> str:
|
||||||
""":obj:`str`: Optional. Bot's last name."""
|
""":obj:`str`: Optional. Bot's last name."""
|
||||||
|
|
||||||
return self.bot.last_name # type: ignore
|
return self.bot.last_name # type: ignore
|
||||||
|
|
||||||
@property # type: ignore
|
@property
|
||||||
@info
|
|
||||||
def username(self) -> str:
|
def username(self) -> str:
|
||||||
""":obj:`str`: Bot's username."""
|
""":obj:`str`: Bot's username."""
|
||||||
|
|
||||||
return self.bot.username # type: ignore
|
return self.bot.username # type: ignore
|
||||||
|
|
||||||
@property # type: ignore
|
@property
|
||||||
@info
|
|
||||||
def link(self) -> str:
|
def link(self) -> str:
|
||||||
""":obj:`str`: Convenience property. Returns the t.me link of the bot."""
|
""":obj:`str`: Convenience property. Returns the t.me link of the bot."""
|
||||||
|
|
||||||
return f"https://t.me/{self.username}"
|
return f"https://t.me/{self.username}"
|
||||||
|
|
||||||
@property # type: ignore
|
@property
|
||||||
@info
|
|
||||||
def can_join_groups(self) -> bool:
|
def can_join_groups(self) -> bool:
|
||||||
""":obj:`bool`: Bot's can_join_groups attribute."""
|
""":obj:`bool`: Bot's can_join_groups attribute."""
|
||||||
|
|
||||||
return self.bot.can_join_groups # type: ignore
|
return self.bot.can_join_groups # type: ignore
|
||||||
|
|
||||||
@property # type: ignore
|
@property
|
||||||
@info
|
|
||||||
def can_read_all_group_messages(self) -> bool:
|
def can_read_all_group_messages(self) -> bool:
|
||||||
""":obj:`bool`: Bot's can_read_all_group_messages attribute."""
|
""":obj:`bool`: Bot's can_read_all_group_messages attribute."""
|
||||||
|
|
||||||
return self.bot.can_read_all_group_messages # type: ignore
|
return self.bot.can_read_all_group_messages # type: ignore
|
||||||
|
|
||||||
@property # type: ignore
|
@property
|
||||||
@info
|
|
||||||
def supports_inline_queries(self) -> bool:
|
def supports_inline_queries(self) -> bool:
|
||||||
""":obj:`bool`: Bot's supports_inline_queries attribute."""
|
""":obj:`bool`: Bot's supports_inline_queries attribute."""
|
||||||
|
|
||||||
return self.bot.supports_inline_queries # type: ignore
|
return self.bot.supports_inline_queries # type: ignore
|
||||||
|
|
||||||
@property # type: ignore
|
@property
|
||||||
@info
|
|
||||||
def commands(self) -> List[BotCommand]:
|
def commands(self) -> List[BotCommand]:
|
||||||
"""List[:class:`BotCommand`]: Bot's commands."""
|
"""List[:class:`BotCommand`]: Bot's commands."""
|
||||||
|
|
||||||
return self._commands or []
|
if self._commands is None:
|
||||||
|
self._commands = self.get_my_commands()
|
||||||
|
return self._commands
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
|
@ -392,9 +381,9 @@ class Bot(TelegramObject):
|
||||||
"""
|
"""
|
||||||
result = self._post('getMe', timeout=timeout, api_kwargs=api_kwargs)
|
result = self._post('getMe', timeout=timeout, api_kwargs=api_kwargs)
|
||||||
|
|
||||||
self.bot = User.de_json(result, self) # type: ignore
|
self._bot = User.de_json(result, self) # type: ignore
|
||||||
|
|
||||||
return self.bot # type: ignore[return-value]
|
return self._bot # type: ignore[return-value]
|
||||||
|
|
||||||
@log
|
@log
|
||||||
def send_message(
|
def send_message(
|
||||||
|
@ -4814,6 +4803,12 @@ class Bot(TelegramObject):
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def __eq__(self, other: object) -> bool:
|
||||||
|
return self.bot == other
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
return hash(self.bot)
|
||||||
|
|
||||||
# camelCase aliases
|
# camelCase aliases
|
||||||
getMe = get_me
|
getMe = get_me
|
||||||
"""Alias for :attr:`get_me`"""
|
"""Alias for :attr:`get_me`"""
|
||||||
|
|
|
@ -48,6 +48,8 @@ from telegram.constants import MAX_INLINE_QUERY_RESULTS
|
||||||
from telegram.error import BadRequest, InvalidToken, NetworkError, RetryAfter
|
from telegram.error import BadRequest, InvalidToken, NetworkError, RetryAfter
|
||||||
from telegram.utils.helpers import from_timestamp, escape_markdown, to_timestamp
|
from telegram.utils.helpers import from_timestamp, escape_markdown, to_timestamp
|
||||||
from tests.conftest import expect_bad_request
|
from tests.conftest import expect_bad_request
|
||||||
|
from tests.bots import FALLBACKS
|
||||||
|
|
||||||
|
|
||||||
BASE_TIME = time.time()
|
BASE_TIME = time.time()
|
||||||
HIGHSCORE_DELTA = 1450000000
|
HIGHSCORE_DELTA = 1450000000
|
||||||
|
@ -144,6 +146,24 @@ class TestBot:
|
||||||
assert get_me_bot.supports_inline_queries == bot.supports_inline_queries
|
assert get_me_bot.supports_inline_queries == bot.supports_inline_queries
|
||||||
assert f'https://t.me/{get_me_bot.username}' == bot.link
|
assert f'https://t.me/{get_me_bot.username}' == bot.link
|
||||||
assert commands == bot.commands
|
assert commands == bot.commands
|
||||||
|
bot._commands = None
|
||||||
|
assert commands == bot.commands
|
||||||
|
|
||||||
|
def test_equality(self):
|
||||||
|
a = Bot(FALLBACKS[0]["token"])
|
||||||
|
b = Bot(FALLBACKS[0]["token"])
|
||||||
|
c = Bot(FALLBACKS[1]["token"])
|
||||||
|
d = Update(123456789)
|
||||||
|
|
||||||
|
assert a == b
|
||||||
|
assert hash(a) == hash(b)
|
||||||
|
assert a is not b
|
||||||
|
|
||||||
|
assert a != c
|
||||||
|
assert hash(a) != hash(c)
|
||||||
|
|
||||||
|
assert a != d
|
||||||
|
assert hash(a) != hash(d)
|
||||||
|
|
||||||
@flaky(3, 1)
|
@flaky(3, 1)
|
||||||
@pytest.mark.timeout(10)
|
@pytest.mark.timeout(10)
|
||||||
|
|
|
@ -208,7 +208,7 @@ class TestUpdater:
|
||||||
monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True)
|
monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True)
|
||||||
monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True)
|
monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True)
|
||||||
# prevent api calls from @info decorator when updater.bot.id is used in thread names
|
# prevent api calls from @info decorator when updater.bot.id is used in thread names
|
||||||
monkeypatch.setattr(updater.bot, 'bot', User(id=123, first_name='bot', is_bot=True))
|
monkeypatch.setattr(updater.bot, '_bot', User(id=123, first_name='bot', is_bot=True))
|
||||||
monkeypatch.setattr(updater.bot, '_commands', [])
|
monkeypatch.setattr(updater.bot, '_commands', [])
|
||||||
|
|
||||||
ip = '127.0.0.1'
|
ip = '127.0.0.1'
|
||||||
|
|
Loading…
Reference in a new issue