Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce keyword-only arguments in Bot methods #3035

Merged
merged 7 commits into from
May 18, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
720 changes: 491 additions & 229 deletions telegram/_bot.py

Large diffs are not rendered by default.

250 changes: 150 additions & 100 deletions telegram/_chat.py

Large diffs are not rendered by default.

12 changes: 10 additions & 2 deletions telegram/_inline/inlinequery.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ class InlineQuery(TelegramObject):
Note:
In Python :keyword:`from` is a reserved word use :paramref:`from_user` instead.

.. versionchanged:: 20.0

* The following are now keyword-only arguments in Bot methods:
``{read, write, connect, pool}_timeout``, ``api_kwargs``, ``auto_pagination``.
Use a named argument for those,
and notice that some positional arguments changed position as a result.

Args:
id (:obj:`str`): Unique identifier for this query.
from_user (:class:`telegram.User`): Sender.
Expand Down Expand Up @@ -123,13 +130,14 @@ async def answer(
next_offset: str = None,
switch_pm_text: str = None,
switch_pm_parameter: str = None,
current_offset: str = None,
*,
auto_pagination: bool = False,
tal66 marked this conversation as resolved.
Show resolved Hide resolved
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
current_offset: str = None,
api_kwargs: JSONDict = None,
auto_pagination: bool = False,
) -> bool:
"""Shortcut for::

Expand Down
357 changes: 199 additions & 158 deletions telegram/_message.py

Large diffs are not rendered by default.

213 changes: 124 additions & 89 deletions telegram/_user.py

Large diffs are not rendered by default.

17 changes: 11 additions & 6 deletions telegram/ext/_extbot.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,12 +241,13 @@ async def _send_message(
disable_notification: ODVInput[bool] = DEFAULT_NONE,
reply_markup: ReplyMarkup = None,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
protect_content: ODVInput[bool] = DEFAULT_NONE,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
protect_content: ODVInput[bool] = DEFAULT_NONE,
) -> Union[bool, Message]:
# We override this method to call self._replace_keyboard and self._insert_callback_data.
# This covers most methods that have a reply_markup
Expand All @@ -257,12 +258,12 @@ async def _send_message(
disable_notification=disable_notification,
reply_markup=self._replace_keyboard(reply_markup),
allow_sending_without_reply=allow_sending_without_reply,
protect_content=protect_content,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
api_kwargs=api_kwargs,
protect_content=protect_content,
)
if isinstance(result, Message):
self._insert_callback_data(result)
Expand All @@ -273,22 +274,23 @@ async def get_updates(
offset: int = None,
limit: int = None,
timeout: float = None,
allowed_updates: List[str] = None,
*,
read_timeout: float = 2,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
allowed_updates: List[str] = None,
api_kwargs: JSONDict = None,
) -> List[Update]:
updates = await super().get_updates(
offset=offset,
limit=limit,
timeout=timeout,
allowed_updates=allowed_updates,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
allowed_updates=allowed_updates,
api_kwargs=api_kwargs,
)

Expand Down Expand Up @@ -360,6 +362,7 @@ async def stop_poll(
chat_id: Union[int, str],
message_id: int,
reply_markup: InlineKeyboardMarkup = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
Expand Down Expand Up @@ -390,12 +393,13 @@ async def copy_message(
reply_to_message_id: int = None,
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
reply_markup: ReplyMarkup = None,
protect_content: ODVInput[bool] = DEFAULT_NONE,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
protect_content: ODVInput[bool] = DEFAULT_NONE,
) -> MessageId:
# We override this method to call self._replace_keyboard
return await super().copy_message(
Expand All @@ -409,17 +413,18 @@ async def copy_message(
reply_to_message_id=reply_to_message_id,
allow_sending_without_reply=allow_sending_without_reply,
reply_markup=self._replace_keyboard(reply_markup),
protect_content=protect_content,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
api_kwargs=api_kwargs,
protect_content=protect_content,
)

async def get_chat(
self,
chat_id: Union[str, int],
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
Expand Down
2 changes: 1 addition & 1 deletion tests/test_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -2381,7 +2381,7 @@ async def test_get_set_chat_menu_button(self, bot, chat_id):

# Test setting our chat menu button to Webapp.
my_menu = MenuButtonWebApp("click me!", WebAppInfo("https://telegram.org/"))
await bot.set_chat_menu_button(chat_id, my_menu)
await bot.set_chat_menu_button(chat_id=chat_id, menu_button=my_menu)
menu_button = await bot.get_chat_menu_button(chat_id)
assert isinstance(menu_button, MenuButtonWebApp)
assert menu_button.type == MenuButtonType.WEB_APP
Expand Down
4 changes: 2 additions & 2 deletions tests/test_chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
def chat(bot):
return Chat(
TestChat.id_,
TestChat.title,
TestChat.type_,
title=TestChat.title,
type=TestChat.type_,
harshil21 marked this conversation as resolved.
Show resolved Hide resolved
username=TestChat.username,
all_members_are_administrators=TestChat.all_members_are_administrators,
bot=bot,
Expand Down
54 changes: 38 additions & 16 deletions tests/test_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@
@pytest.fixture(scope="class")
def message(bot):
return Message(
TestMessage.id_,
TestMessage.date,
TestMessage.chat,
message_id=TestMessage.id_,
date=TestMessage.date,
chat=TestMessage.chat,
from_user=TestMessage.from_user,
bot=bot,
)
Expand Down Expand Up @@ -344,7 +344,14 @@ async def test_parse_entity(self):
b"\\u200d\\U0001f467\\U0001f431http://google.com"
).decode("unicode-escape")
entity = MessageEntity(type=MessageEntity.URL, offset=13, length=17)
message = Message(1, self.from_user, self.date, self.chat, text=text, entities=[entity])
message = Message(
1,
from_user=self.from_user,
date=self.date,
chat=self.chat,
text=text,
entities=[entity],
)
assert message.parse_entity(entity) == "http://google.com"

with pytest.raises(RuntimeError, match="Message has no"):
Expand All @@ -357,7 +364,12 @@ async def test_parse_caption_entity(self):
).decode("unicode-escape")
entity = MessageEntity(type=MessageEntity.URL, offset=13, length=17)
message = Message(
1, self.from_user, self.date, self.chat, caption=caption, caption_entities=[entity]
1,
from_user=self.from_user,
date=self.date,
chat=self.chat,
caption=caption,
caption_entities=[entity],
)
assert message.parse_caption_entity(entity) == "http://google.com"

Expand All @@ -372,7 +384,12 @@ async def test_parse_entities(self):
entity = MessageEntity(type=MessageEntity.URL, offset=13, length=17)
entity_2 = MessageEntity(type=MessageEntity.BOLD, offset=13, length=1)
message = Message(
1, self.from_user, self.date, self.chat, text=text, entities=[entity_2, entity]
1,
from_user=self.from_user,
date=self.date,
chat=self.chat,
text=text,
entities=[entity_2, entity],
)
assert message.parse_entities(MessageEntity.URL) == {entity: "http://google.com"}
assert message.parse_entities() == {entity: "http://google.com", entity_2: "h"}
Expand All @@ -386,9 +403,9 @@ async def test_parse_caption_entities(self):
entity_2 = MessageEntity(type=MessageEntity.BOLD, offset=13, length=1)
message = Message(
1,
self.from_user,
self.date,
self.chat,
from_user=self.from_user,
date=self.date,
chat=self.chat,
caption=text,
caption_entities=[entity_2, entity],
)
Expand Down Expand Up @@ -507,7 +524,12 @@ def test_text_html_emoji(self):
expected = b"\\U0001f469\\u200d\\U0001f469\\u200d <b>ABC</b>".decode("unicode-escape")
bold_entity = MessageEntity(type=MessageEntity.BOLD, offset=7, length=3)
message = Message(
1, self.from_user, self.date, self.chat, text=text, entities=[bold_entity]
1,
from_user=self.from_user,
date=self.date,
chat=self.chat,
text=text,
entities=[bold_entity],
)
assert expected == message.text_html

Expand Down Expand Up @@ -607,9 +629,9 @@ def test_caption_html_emoji(self):
bold_entity = MessageEntity(type=MessageEntity.BOLD, offset=7, length=3)
message = Message(
1,
self.from_user,
self.date,
self.chat,
from_user=self.from_user,
date=self.date,
chat=self.chat,
caption=caption,
caption_entities=[bold_entity],
)
Expand All @@ -621,9 +643,9 @@ def test_caption_markdown_emoji(self):
bold_entity = MessageEntity(type=MessageEntity.BOLD, offset=7, length=3)
message = Message(
1,
self.from_user,
self.date,
self.chat,
from_user=self.from_user,
date=self.date,
chat=self.chat,
caption=caption,
caption_entities=[bold_entity],
)
Expand Down
18 changes: 18 additions & 0 deletions tests/test_official.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,24 @@ def check_method(h4):

assert (sig.parameters.keys() ^ checked) - ignored == set()

check_extra_params_are_keywordonly(method.__name__, checked, name)


def check_extra_params_are_keywordonly(method_name, api_params, name_in_api):
for c in [telegram.Bot, telegram.Chat, telegram.Message, telegram.User]:
harshil21 marked this conversation as resolved.
Show resolved Hide resolved
try:
method = getattr(c, method_name)
sig = inspect.signature(method)
kw_or_positional_args = [
p.name for p in sig.parameters.values() if p.kind != inspect.Parameter.KEYWORD_ONLY
]
assert set(kw_or_positional_args) - set(api_params) - {"self"} == set(), (
f"In {str(method).split()[1]}, extra args should be keyword only "
f"(compared to {name_in_api} in API)"
)
except (AttributeError, TypeError):
pass


def check_object(h4):
name = h4.text
Expand Down