Skip to content

Drop Backward Compatibility for user_id in send_gift #4692

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

Merged
merged 7 commits into from
Apr 11, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions changes/unreleased/4692.dVZs28GuwTFnNJdWkvPbNv.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
breaking = "Drop backward compatibility for `user_id` in `send_gift` by updating the order of parameters. Please adapt your code accordingly or use keyword arguments."
[[pull_requests]]
uid = "4692"
author_uid = "Bibo-Joshi"
closes_threads = []
16 changes: 7 additions & 9 deletions telegram/_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -9845,13 +9845,13 @@ async def get_available_gifts(

async def send_gift(
self,
user_id: Optional[int] = None,
gift_id: Union[str, Gift] = None, # type: ignore
gift_id: Union[str, Gift],
text: Optional[str] = None,
text_parse_mode: ODVInput[str] = DEFAULT_NONE,
text_entities: Optional[Sequence["MessageEntity"]] = None,
pay_for_upgrade: Optional[bool] = None,
chat_id: Optional[Union[str, int]] = None,
user_id: Optional[int] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
Expand All @@ -9863,15 +9863,18 @@ async def send_gift(
The gift can't be converted to Telegram Stars by the receiver.

.. versionadded:: 21.8
.. versionchanged:: NEXT.VERSION
Bot API 8.3 made :paramref:`user_id` optional. In version NEXT.VERSION, the methods
signature was changed accordingly.

Args:
gift_id (:obj:`str` | :class:`~telegram.Gift`): Identifier of the gift or a
:class:`~telegram.Gift` object
user_id (:obj:`int`, optional): Required if :paramref:`chat_id` is not specified.
Unique identifier of the target user that will receive the gift.

.. versionchanged:: 21.11
Now optional.
gift_id (:obj:`str` | :class:`~telegram.Gift`): Identifier of the gift or a
:class:`~telegram.Gift` object
chat_id (:obj:`int` | :obj:`str`, optional): Required if :paramref:`user_id`
is not specified. |chat_id_channel| It will receive the gift.

Expand Down Expand Up @@ -9902,11 +9905,6 @@ async def send_gift(
Raises:
:class:`telegram.error.TelegramError`
"""
# TODO: Remove when stability policy allows, tags: deprecated 21.11
# also we should raise a deprecation warnung if anything is passed by
# position since it will be moved, not sure how
if gift_id is None:
raise TypeError("Missing required argument `gift_id`.")
data: JSONDict = {
"user_id": user_id,
"gift_id": gift_id.id if isinstance(gift_id, Gift) else gift_id,
Expand Down
4 changes: 2 additions & 2 deletions telegram/ext/_extbot.py
Original file line number Diff line number Diff line change
Expand Up @@ -4476,13 +4476,13 @@ async def get_available_gifts(

async def send_gift(
self,
user_id: Optional[int] = None,
gift_id: Union[str, Gift] = None, # type: ignore
gift_id: Union[str, Gift],
text: Optional[str] = None,
text_parse_mode: ODVInput[str] = DEFAULT_NONE,
text_entities: Optional[Sequence["MessageEntity"]] = None,
pay_for_upgrade: Optional[bool] = None,
chat_id: Optional[Union[str, int]] = None,
user_id: Optional[int] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
Expand Down
3 changes: 0 additions & 3 deletions tests/auxil/bot_method_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,9 +351,6 @@ def build_kwargs(
allow_sending_without_reply=manually_passed_value,
quote_parse_mode=manually_passed_value,
)
# TODO remove when gift_id isnt marked as optional anymore, tags: deprecated 21.11
elif name == "gift_id":
kws[name] = "GIFT-ID"

return kws

Expand Down
10 changes: 1 addition & 9 deletions tests/test_chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -1330,15 +1330,7 @@ async def make_assertion_channel(*_, **kwargs):
and kwargs["text_entities"] == "text_entities"
)

# TODO discuss if better way exists
# tags: deprecated 21.11
with pytest.raises(
Exception,
match="Default for argument gift_id does not match the default of the Bot method.",
):
assert check_shortcut_signature(
Chat.send_gift, Bot.send_gift, ["user_id", "chat_id"], []
)
assert check_shortcut_signature(Chat.send_gift, Bot.send_gift, ["user_id", "chat_id"], [])
assert await check_shortcut_call(
chat.send_gift, chat.get_bot(), "send_gift", ["user_id", "chat_id"]
)
Expand Down
44 changes: 4 additions & 40 deletions tests/test_gifts.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,40 +135,8 @@ def test_equality(self, gift):
],
ids=["string", "Gift"],
)
async def test_send_gift(self, offline_bot, gift, monkeypatch):
# We can't send actual gifts, so we just check that the correct parameters are passed
text_entities = [
MessageEntity(MessageEntity.TEXT_LINK, 0, 4, "url"),
MessageEntity(MessageEntity.BOLD, 5, 9),
]

async def make_assertion(url, request_data: RequestData, *args, **kwargs):
user_id = request_data.parameters["user_id"] == "user_id"
gift_id = request_data.parameters["gift_id"] == "gift_id"
text = request_data.parameters["text"] == "text"
text_parse_mode = request_data.parameters["text_parse_mode"] == "text_parse_mode"
tes = request_data.parameters["text_entities"] == [
me.to_dict() for me in text_entities
]
pay_for_upgrade = request_data.parameters["pay_for_upgrade"] is True

return user_id and gift_id and text and text_parse_mode and tes and pay_for_upgrade

monkeypatch.setattr(offline_bot.request, "post", make_assertion)
assert await offline_bot.send_gift(
"user_id",
gift,
"text",
text_parse_mode="text_parse_mode",
text_entities=text_entities,
pay_for_upgrade=True,
)

@pytest.mark.parametrize("id_name", ["user_id", "chat_id"])
async def test_send_gift_user_chat_id(self, offline_bot, gift, monkeypatch, id_name):
# Only here because we have to temporarily mark gift_id as optional.
# tags: deprecated 21.11

async def test_send_gift(self, offline_bot, gift, monkeypatch, id_name):
# We can't send actual gifts, so we just check that the correct parameters are passed
text_entities = [
MessageEntity(MessageEntity.TEXT_LINK, 0, 4, "url"),
Expand All @@ -177,7 +145,7 @@ async def test_send_gift_user_chat_id(self, offline_bot, gift, monkeypatch, id_n

async def make_assertion(url, request_data: RequestData, *args, **kwargs):
received_id = request_data.parameters[id_name] == id_name
gift_id = request_data.parameters["gift_id"] == "some_id"
gift_id = request_data.parameters["gift_id"] == "gift_id"
text = request_data.parameters["text"] == "text"
text_parse_mode = request_data.parameters["text_parse_mode"] == "text_parse_mode"
tes = request_data.parameters["text_entities"] == [
Expand All @@ -189,18 +157,14 @@ async def make_assertion(url, request_data: RequestData, *args, **kwargs):

monkeypatch.setattr(offline_bot.request, "post", make_assertion)
assert await offline_bot.send_gift(
gift_id=gift,
text="text",
gift,
"text",
text_parse_mode="text_parse_mode",
text_entities=text_entities,
pay_for_upgrade=True,
**{id_name: id_name},
)

async def test_send_gift_without_gift_id(self, offline_bot):
with pytest.raises(TypeError, match="Missing required argument `gift_id`."):
await offline_bot.send_gift()

@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
@pytest.mark.parametrize(
("passed_value", "expected_value"),
Expand Down
10 changes: 2 additions & 8 deletions tests/test_official/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"""This module contains exceptions to our API compared to the official API."""
import datetime as dtm

from telegram import Animation, Audio, Document, PhotoSize, Sticker, Video, VideoNote, Voice
from telegram import Animation, Audio, Document, Gift, PhotoSize, Sticker, Video, VideoNote, Voice
from tests.test_official.helpers import _get_params_base

IGNORED_OBJECTS = ("ResponseParameters",)
Expand Down Expand Up @@ -47,8 +47,7 @@ class ParamTypeCheckingExceptions:
"animation": Animation,
"voice": Voice,
"sticker": Sticker,
# TODO: Deprecated and will be corrected (and readded) in next major bot API release:
# "gift_id": Gift,
"gift_id": Gift,
},
"(delete|set)_sticker.*": {
"sticker$": Sticker,
Expand Down Expand Up @@ -101,9 +100,6 @@ class ParamTypeCheckingExceptions:
"EncryptedPassportElement": {
"data": str, # actual: Union[IdDocumentData, PersonalDetails, ResidentialAddress]
},
# TODO: Deprecated and will be corrected (and removed) in next major PTB
# version:
"send_gift": {"gift_id": str}, # actual: Non optional
}

# param names ignored in the param type checking in classes for the `tg.Defaults` case.
Expand Down Expand Up @@ -196,8 +192,6 @@ def ptb_ignored_params(object_name: str) -> set[str]:
"send_venue": {"latitude", "longitude", "title", "address"},
"send_contact": {"phone_number", "first_name"},
# ---->
# here for backwards compatibility. Todo: remove on next bot api release
"send_gift": {"gift_id"},
}


Expand Down
10 changes: 1 addition & 9 deletions tests/test_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -731,15 +731,7 @@ async def make_assertion(*_, **kwargs):
and kwargs["text_entities"] == "text_entities"
)

# TODO discuss if better way exists
# tags: deprecated 21.11
with pytest.raises(
Exception,
match="Default for argument gift_id does not match the default of the Bot method.",
):
assert check_shortcut_signature(
user.send_gift, Bot.send_gift, ["user_id", "chat_id"], []
)
assert check_shortcut_signature(user.send_gift, Bot.send_gift, ["user_id", "chat_id"], [])
assert await check_shortcut_call(
user.send_gift, user.get_bot(), "send_gift", ["chat_id", "user_id"]
)
Expand Down
Loading