mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2024-12-22 06:25:12 +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
|
||||
.exrc
|
||||
|
||||
# virtual env
|
||||
venv*
|
||||
|
|
|
@ -110,22 +110,6 @@ if TYPE_CHECKING:
|
|||
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(
|
||||
func: Callable[..., RT], *args: Any, **kwargs: Any # pylint: disable=W0613
|
||||
) -> Callable[..., RT]:
|
||||
|
@ -144,6 +128,10 @@ def log(
|
|||
class Bot(TelegramObject):
|
||||
"""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:
|
||||
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
|
||||
|
@ -217,7 +205,7 @@ class Bot(TelegramObject):
|
|||
|
||||
self.base_url = str(base_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._request = request or Request()
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
@ -302,68 +290,69 @@ class Bot(TelegramObject):
|
|||
|
||||
return token
|
||||
|
||||
@property # type: ignore
|
||||
@info
|
||||
@property
|
||||
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:
|
||||
""":obj:`int`: Unique identifier for this bot."""
|
||||
|
||||
return self.bot.id # type: ignore
|
||||
return self.bot.id
|
||||
|
||||
@property # type: ignore
|
||||
@info
|
||||
@property
|
||||
def first_name(self) -> str:
|
||||
""":obj:`str`: Bot's first name."""
|
||||
|
||||
return self.bot.first_name # type: ignore
|
||||
return self.bot.first_name
|
||||
|
||||
@property # type: ignore
|
||||
@info
|
||||
@property
|
||||
def last_name(self) -> str:
|
||||
""":obj:`str`: Optional. Bot's last name."""
|
||||
|
||||
return self.bot.last_name # type: ignore
|
||||
|
||||
@property # type: ignore
|
||||
@info
|
||||
@property
|
||||
def username(self) -> str:
|
||||
""":obj:`str`: Bot's username."""
|
||||
|
||||
return self.bot.username # type: ignore
|
||||
|
||||
@property # type: ignore
|
||||
@info
|
||||
@property
|
||||
def link(self) -> str:
|
||||
""":obj:`str`: Convenience property. Returns the t.me link of the bot."""
|
||||
|
||||
return f"https://t.me/{self.username}"
|
||||
|
||||
@property # type: ignore
|
||||
@info
|
||||
@property
|
||||
def can_join_groups(self) -> bool:
|
||||
""":obj:`bool`: Bot's can_join_groups attribute."""
|
||||
|
||||
return self.bot.can_join_groups # type: ignore
|
||||
|
||||
@property # type: ignore
|
||||
@info
|
||||
@property
|
||||
def can_read_all_group_messages(self) -> bool:
|
||||
""":obj:`bool`: Bot's can_read_all_group_messages attribute."""
|
||||
|
||||
return self.bot.can_read_all_group_messages # type: ignore
|
||||
|
||||
@property # type: ignore
|
||||
@info
|
||||
@property
|
||||
def supports_inline_queries(self) -> bool:
|
||||
""":obj:`bool`: Bot's supports_inline_queries attribute."""
|
||||
|
||||
return self.bot.supports_inline_queries # type: ignore
|
||||
|
||||
@property # type: ignore
|
||||
@info
|
||||
@property
|
||||
def commands(self) -> List[BotCommand]:
|
||||
"""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
|
||||
def name(self) -> str:
|
||||
|
@ -392,9 +381,9 @@ class Bot(TelegramObject):
|
|||
"""
|
||||
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
|
||||
def send_message(
|
||||
|
@ -4814,6 +4803,12 @@ class Bot(TelegramObject):
|
|||
|
||||
return data
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
return self.bot == other
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.bot)
|
||||
|
||||
# camelCase aliases
|
||||
getMe = 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.utils.helpers import from_timestamp, escape_markdown, to_timestamp
|
||||
from tests.conftest import expect_bad_request
|
||||
from tests.bots import FALLBACKS
|
||||
|
||||
|
||||
BASE_TIME = time.time()
|
||||
HIGHSCORE_DELTA = 1450000000
|
||||
|
@ -144,6 +146,24 @@ class TestBot:
|
|||
assert get_me_bot.supports_inline_queries == bot.supports_inline_queries
|
||||
assert f'https://t.me/{get_me_bot.username}' == bot.link
|
||||
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)
|
||||
@pytest.mark.timeout(10)
|
||||
|
|
|
@ -208,7 +208,7 @@ class TestUpdater:
|
|||
monkeypatch.setattr(updater.bot, 'set_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
|
||||
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', [])
|
||||
|
||||
ip = '127.0.0.1'
|
||||
|
|
Loading…
Reference in a new issue