From 1878a4b07ee1a60ab5ba7d3afdee96f405058bdb Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Thu, 31 Oct 2024 15:58:17 +0100 Subject: [PATCH 1/7] Improve Exception Handling in `File.download_*` --- telegram/_files/file.py | 14 +++++++++++++- tests/_files/test_file.py | 9 +++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/telegram/_files/file.py b/telegram/_files/file.py index c9b8d22d49a..9171713b089 100644 --- a/telegram/_files/file.py +++ b/telegram/_files/file.py @@ -176,6 +176,9 @@ async def download_to_drive( :class:`pathlib.Path`: Returns the Path object the file was downloaded to. """ + if not self.file_path: + raise RuntimeError("No `file_path` available for this file. Can not download.") + local_file = is_local_file(self.file_path) url = None if local_file else self._get_encoded_url() @@ -254,13 +257,19 @@ async def download_to_memory( pool_timeout (:obj:`float` | :obj:`None`, optional): Value to pass to :paramref:`telegram.request.BaseRequest.post.pool_timeout`. Defaults to :attr:`~telegram.request.BaseRequest.DEFAULT_NONE`. + + Raises: + RuntimeError: If :attr:`file_path` is not set. """ + if not self.file_path: + raise RuntimeError("No `file_path` available for this file. Can not download.") + local_file = is_local_file(self.file_path) url = None if local_file else self._get_encoded_url() path = Path(self.file_path) if local_file else None if local_file: buf = path.read_bytes() - else: + elif url: buf = await self.get_bot().request.retrieve( url, read_timeout=read_timeout, @@ -313,6 +322,9 @@ async def download_as_bytearray( newly allocated :obj:`bytearray`. """ + if not self.file_path: + raise RuntimeError("No `file_path` available for this file. Can not download.") + if buf is None: buf = bytearray() diff --git a/tests/_files/test_file.py b/tests/_files/test_file.py index 70874d5feb8..83032a4d152 100644 --- a/tests/_files/test_file.py +++ b/tests/_files/test_file.py @@ -17,6 +17,7 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. import os +from io import BytesIO from pathlib import Path from tempfile import TemporaryFile, mkstemp @@ -272,6 +273,14 @@ async def test(*args, **kwargs): assert buf2[len(buf) :] == buf assert buf2[: len(buf)] == buf + async def test_download_no_file_path(self): + with pytest.raises(RuntimeError, match="No `file_path` available"): + await File(self.file_id, self.file_unique_id).download_to_drive() + with pytest.raises(RuntimeError, match="No `file_path` available"): + await File(self.file_id, self.file_unique_id).download_to_memory(BytesIO()) + with pytest.raises(RuntimeError, match="No `file_path` available"): + await File(self.file_id, self.file_unique_id).download_as_bytearray() + class TestFileWithRequest(FileTestBase): async def test_error_get_empty_file_id(self, bot): From 77297da3317406b72b620a3c4f21c69bdeb9bdbb Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Thu, 31 Oct 2024 16:00:24 +0100 Subject: [PATCH 2/7] A bit more documentation --- telegram/_files/file.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/telegram/_files/file.py b/telegram/_files/file.py index 9171713b089..93b4ebe3ff1 100644 --- a/telegram/_files/file.py +++ b/telegram/_files/file.py @@ -152,6 +152,9 @@ async def download_to_drive( * This method was previously called ``download``. It was split into :meth:`download_to_drive` and :meth:`download_to_memory`. + .. versionchanged:: NEXT.VERSION + Raises :exc:`RuntimeError` if :attr:`file_path` is not set. + Args: custom_path (:class:`pathlib.Path` | :obj:`str` , optional): The path where the file will be saved to. If not specified, will be saved in the current working directory @@ -175,6 +178,9 @@ async def download_to_drive( Returns: :class:`pathlib.Path`: Returns the Path object the file was downloaded to. + Raises: + RuntimeError: If :attr:`file_path` is not set. + """ if not self.file_path: raise RuntimeError("No `file_path` available for this file. Can not download.") @@ -240,6 +246,9 @@ async def download_to_memory( .. versionadded:: 20.0 + .. versionchanged:: NEXT.VERSION + Raises :exc:`RuntimeError` if :attr:`file_path` is not set. + Args: out (:obj:`io.BufferedIOBase`): A file-like object. Must be opened for writing in binary mode. @@ -292,6 +301,9 @@ async def download_as_bytearray( ) -> bytearray: """Download this file and return it as a bytearray. + .. versionchanged:: NEXT.VERSION + Raises :exc:`RuntimeError` if :attr:`file_path` is not set. + Args: buf (:obj:`bytearray`, optional): Extend the given bytearray with the downloaded data. @@ -321,6 +333,9 @@ async def download_as_bytearray( :obj:`bytearray`: The same object as :paramref:`buf` if it was specified. Otherwise a newly allocated :obj:`bytearray`. + Raises: + RuntimeError: If :attr:`file_path` is not set. + """ if not self.file_path: raise RuntimeError("No `file_path` available for this file. Can not download.") From bf883a5cf62a1256b4e523278f9161d8fbacf1b0 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sat, 2 Nov 2024 18:53:24 +0100 Subject: [PATCH 3/7] Remove `file_id` case in `download_to_drive` and add `stable_get_file` to tests as workaround for tdlib/telegram-bot-api#658 --- telegram/_files/file.py | 21 ++++++++++++--------- tests/_files/test_animation.py | 4 ++-- tests/_files/test_audio.py | 4 ++-- tests/_files/test_chatphoto.py | 7 +++++-- tests/_files/test_document.py | 4 ++-- tests/_files/test_file.py | 15 --------------- tests/_files/test_inputfile.py | 6 +++--- tests/_files/test_sticker.py | 4 ++-- tests/_files/test_video.py | 4 ++-- tests/_files/test_videonote.py | 4 ++-- tests/_files/test_voice.py | 4 ++-- tests/auxil/files.py | 21 +++++++++++++++++++++ tests/test_bot.py | 6 +++--- 13 files changed, 58 insertions(+), 46 deletions(-) diff --git a/telegram/_files/file.py b/telegram/_files/file.py index 93b4ebe3ff1..667ae1d6c74 100644 --- a/telegram/_files/file.py +++ b/telegram/_files/file.py @@ -128,9 +128,8 @@ async def download_to_drive( ) -> Path: """ Download this file. By default, the file is saved in the current working directory with - :attr:`file_path` as file name. If the file has no filename, the file ID will be used as - filename. If :paramref:`custom_path` is supplied as a :obj:`str` or :obj:`pathlib.Path`, - it will be saved to that path. + :attr:`file_path` as file name. If :paramref:`custom_path` is supplied as a :obj:`str` or + :obj:`pathlib.Path`, it will be saved to that path. Note: If :paramref:`custom_path` isn't provided and :attr:`file_path` is the path of a @@ -153,7 +152,9 @@ async def download_to_drive( :meth:`download_to_drive` and :meth:`download_to_memory`. .. versionchanged:: NEXT.VERSION - Raises :exc:`RuntimeError` if :attr:`file_path` is not set. + Raises :exc:`RuntimeError` if :attr:`file_path` is not set. Note that files without + a :attr:`file_path` could never be downloaded, as this attribute is mandatory for that + operation. Args: custom_path (:class:`pathlib.Path` | :obj:`str` , optional): The path where the file @@ -207,10 +208,8 @@ async def download_to_drive( filename = Path(custom_path) elif local_file: return Path(self.file_path) - elif self.file_path: - filename = Path(Path(self.file_path).name) else: - filename = Path.cwd() / self.file_id + filename = Path(Path(self.file_path).name) buf = await self.get_bot().request.retrieve( url, @@ -247,7 +246,9 @@ async def download_to_memory( .. versionadded:: 20.0 .. versionchanged:: NEXT.VERSION - Raises :exc:`RuntimeError` if :attr:`file_path` is not set. + Raises :exc:`RuntimeError` if :attr:`file_path` is not set. Note that files without + a :attr:`file_path` could never be downloaded, as this attribute is mandatory for that + operation. Args: out (:obj:`io.BufferedIOBase`): A file-like object. Must be opened for writing in @@ -302,7 +303,9 @@ async def download_as_bytearray( """Download this file and return it as a bytearray. .. versionchanged:: NEXT.VERSION - Raises :exc:`RuntimeError` if :attr:`file_path` is not set. + Raises :exc:`RuntimeError` if :attr:`file_path` is not set. Note that files without + a :attr:`file_path` could never be downloaded, as this attribute is mandatory for that + operation. Args: buf (:obj:`bytearray`, optional): Extend the given bytearray with the downloaded data. diff --git a/tests/_files/test_animation.py b/tests/_files/test_animation.py index 0f581259db9..6face27d667 100644 --- a/tests/_files/test_animation.py +++ b/tests/_files/test_animation.py @@ -33,7 +33,7 @@ check_shortcut_signature, ) from tests.auxil.build_messages import make_message -from tests.auxil.files import data_file +from tests.auxil.files import data_file, stable_get_file from tests.auxil.slots import mro_slots @@ -245,7 +245,7 @@ async def test_send_all_args(self, bot, chat_id, animation_file, animation, thum pytest.xfail("This is a bug on Telegram's end") async def test_get_and_download(self, bot, animation, tmp_file): - new_file = await bot.get_file(animation.file_id) + new_file = await stable_get_file(bot, animation.file_id) assert new_file.file_path.startswith("https://") diff --git a/tests/_files/test_audio.py b/tests/_files/test_audio.py index 08e598cb267..358aae57063 100644 --- a/tests/_files/test_audio.py +++ b/tests/_files/test_audio.py @@ -33,7 +33,7 @@ check_shortcut_signature, ) from tests.auxil.build_messages import make_message -from tests.auxil.files import data_file +from tests.auxil.files import data_file, stable_get_file from tests.auxil.slots import mro_slots @@ -244,7 +244,7 @@ async def test_send_all_args(self, bot, chat_id, audio_file, thumb_file): assert message.has_protected_content async def test_get_and_download(self, bot, chat_id, audio, tmp_file): - new_file = await bot.get_file(audio.file_id) + new_file = await stable_get_file(bot, audio.file_id) assert new_file.file_size == self.file_size assert new_file.file_unique_id == audio.file_unique_id diff --git a/tests/_files/test_chatphoto.py b/tests/_files/test_chatphoto.py index 5bbb6a8f71e..9b34986369c 100644 --- a/tests/_files/test_chatphoto.py +++ b/tests/_files/test_chatphoto.py @@ -31,7 +31,7 @@ check_shortcut_call, check_shortcut_signature, ) -from tests.auxil.files import data_file +from tests.auxil.files import data_file, stable_get_file from tests.auxil.networking import expect_bad_request from tests.auxil.slots import mro_slots @@ -158,7 +158,10 @@ async def make_assertion(*_, **kwargs): class TestChatPhotoWithRequest: async def test_get_and_download(self, bot, chat_photo, tmp_file): - tasks = {bot.get_file(chat_photo.small_file_id), bot.get_file(chat_photo.big_file_id)} + tasks = { + stable_get_file(bot, chat_photo.small_file_id), + stable_get_file(bot, chat_photo.big_file_id), + } asserts = [] for task in asyncio.as_completed(tasks): diff --git a/tests/_files/test_document.py b/tests/_files/test_document.py index e6037403408..b0456e103cc 100644 --- a/tests/_files/test_document.py +++ b/tests/_files/test_document.py @@ -33,7 +33,7 @@ check_shortcut_signature, ) from tests.auxil.build_messages import make_message -from tests.auxil.files import data_file +from tests.auxil.files import data_file, stable_get_file from tests.auxil.slots import mro_slots @@ -216,7 +216,7 @@ async def test_error_send_empty_file_id(self, bot, chat_id): await bot.send_document(chat_id=chat_id, document="") async def test_get_and_download(self, bot, document, chat_id, tmp_file): - new_file = await bot.get_file(document.file_id) + new_file = await stable_get_file(bot, document.file_id) assert new_file.file_size == document.file_size assert new_file.file_unique_id == document.file_unique_id diff --git a/tests/_files/test_file.py b/tests/_files/test_file.py index 83032a4d152..fbf71dc70ba 100644 --- a/tests/_files/test_file.py +++ b/tests/_files/test_file.py @@ -182,21 +182,6 @@ async def test(*args, **kwargs): os.close(file_handle) custom_path.unlink(missing_ok=True) - async def test_download_no_filename(self, monkeypatch, file): - async def test(*args, **kwargs): - return self.file_content - - file.file_path = None - - monkeypatch.setattr(file.get_bot().request, "retrieve", test) - out_file = await file.download_to_drive() - - assert str(out_file)[-len(file.file_id) :] == file.file_id - try: - assert out_file.read_bytes() == self.file_content - finally: - out_file.unlink(missing_ok=True) - async def test_download_file_obj(self, monkeypatch, file): async def test(*args, **kwargs): return self.file_content diff --git a/tests/_files/test_inputfile.py b/tests/_files/test_inputfile.py index b7235497b92..e733b5ca1f7 100644 --- a/tests/_files/test_inputfile.py +++ b/tests/_files/test_inputfile.py @@ -25,7 +25,7 @@ from telegram import InputFile from telegram._utils.strings import TextEncoding -from tests.auxil.files import data_file +from tests.auxil.files import data_file, stable_get_file from tests.auxil.slots import mro_slots @@ -214,7 +214,7 @@ async def test_send_bytes(self, bot, chat_id): message = await bot.send_document(chat_id, data_file("text_file.txt").read_bytes()) out = BytesIO() - await (await message.document.get_file()).download_to_memory(out=out) + await (await stable_get_file(bot, message.document)).download_to_memory(out=out) out.seek(0) assert out.read().decode(TextEncoding.UTF_8) == "PTB Rocks! ⅞" @@ -227,7 +227,7 @@ async def test_send_string(self, bot, chat_id): ) out = BytesIO() - await (await message.document.get_file()).download_to_memory(out=out) + await (await stable_get_file(bot, message.document)).download_to_memory(out=out) out.seek(0) assert out.read().decode(TextEncoding.UTF_8) == "PTB Rocks! ⅞" diff --git a/tests/_files/test_sticker.py b/tests/_files/test_sticker.py index d77f93ac776..5ee8e4c514f 100644 --- a/tests/_files/test_sticker.py +++ b/tests/_files/test_sticker.py @@ -45,7 +45,7 @@ check_shortcut_signature, ) from tests.auxil.build_messages import make_message -from tests.auxil.files import data_file +from tests.auxil.files import data_file, stable_get_file from tests.auxil.slots import mro_slots @@ -336,7 +336,7 @@ async def test_send_all_args(self, bot, chat_id, sticker_file, sticker): assert message.sticker.thumbnail.file_size == sticker.thumbnail.file_size async def test_get_and_download(self, bot, sticker, tmp_file): - new_file = await bot.get_file(sticker.file_id) + new_file = await stable_get_file(bot, sticker.file_id) assert new_file.file_size == sticker.file_size assert new_file.file_unique_id == sticker.file_unique_id diff --git a/tests/_files/test_video.py b/tests/_files/test_video.py index 66230389f7e..9594be16c82 100644 --- a/tests/_files/test_video.py +++ b/tests/_files/test_video.py @@ -33,7 +33,7 @@ check_shortcut_signature, ) from tests.auxil.build_messages import make_message -from tests.auxil.files import data_file +from tests.auxil.files import data_file, stable_get_file from tests.auxil.slots import mro_slots @@ -257,7 +257,7 @@ async def test_send_all_args(self, bot, chat_id, video_file, video, thumb_file): assert message.show_caption_above_media async def test_get_and_download(self, bot, video, chat_id, tmp_file): - new_file = await bot.get_file(video.file_id) + new_file = await stable_get_file(bot, video.file_id) assert new_file.file_size == self.file_size assert new_file.file_unique_id == video.file_unique_id diff --git a/tests/_files/test_videonote.py b/tests/_files/test_videonote.py index ce69fa8f850..ed16ddd393a 100644 --- a/tests/_files/test_videonote.py +++ b/tests/_files/test_videonote.py @@ -32,7 +32,7 @@ check_shortcut_signature, ) from tests.auxil.build_messages import make_message -from tests.auxil.files import data_file +from tests.auxil.files import data_file, stable_get_file from tests.auxil.slots import mro_slots @@ -249,7 +249,7 @@ async def test_send_all_args(self, bot, chat_id, video_note_file, video_note, th assert message.has_protected_content async def test_get_and_download(self, bot, video_note, tmp_file): - new_file = await bot.get_file(video_note.file_id) + new_file = await stable_get_file(bot, video_note.file_id) assert new_file.file_size == self.file_size assert new_file.file_unique_id == video_note.file_unique_id diff --git a/tests/_files/test_voice.py b/tests/_files/test_voice.py index e32bb195c8e..81dfc07dcd8 100644 --- a/tests/_files/test_voice.py +++ b/tests/_files/test_voice.py @@ -33,7 +33,7 @@ check_shortcut_signature, ) from tests.auxil.build_messages import make_message -from tests.auxil.files import data_file +from tests.auxil.files import data_file, stable_get_file from tests.auxil.slots import mro_slots @@ -227,7 +227,7 @@ async def test_send_all_args(self, bot, chat_id, voice_file, voice): assert message.has_protected_content async def test_get_and_download(self, bot, voice, chat_id, tmp_file): - new_file = await bot.get_file(voice.file_id) + new_file = await stable_get_file(bot, voice.file_id) assert new_file.file_size == voice.file_size assert new_file.file_unique_id == voice.file_unique_id diff --git a/tests/auxil/files.py b/tests/auxil/files.py index 6b1f87eacff..b2021bea9bb 100644 --- a/tests/auxil/files.py +++ b/tests/auxil/files.py @@ -16,7 +16,11 @@ # # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. +import logging from pathlib import Path +from typing import Protocol + +from telegram import Bot, File PROJECT_ROOT_PATH = Path(__file__).parent.parent.parent.resolve() TEST_DATA_PATH = PROJECT_ROOT_PATH / "tests" / "data" @@ -24,3 +28,20 @@ def data_file(filename: str) -> Path: return TEST_DATA_PATH / filename + + +class _HasGetFile(Protocol): + async def get_file(self) -> File: ... + + +async def stable_get_file(bot: Bot, obj: _HasGetFile | str) -> File: + """Temporary workaround for Telegram API returning file_path as None on first call to + get_file. This function will attempt to get the file 3 times before raising an error. + Remove this once https://github.com/tdlib/telegram-bot-api/issues/658 as closed.""" + for i in range(3): + file = await bot.get_file(obj) if isinstance(obj, str) else await obj.get_file() + if file.file_path: + logging.debug("File path returned by TG on attempt %s", i + 1) + return file + + raise RuntimeError("File path not found returned by TG after 3 attempts") diff --git a/tests/test_bot.py b/tests/test_bot.py index 7f060fb0992..0fd9482e51c 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -97,7 +97,7 @@ from tests.auxil.bot_method_checks import check_defaults_handling from tests.auxil.ci_bots import FALLBACKS from tests.auxil.envvars import GITHUB_ACTION, TEST_WITH_OPT_DEPS -from tests.auxil.files import data_file +from tests.auxil.files import data_file, stable_get_file from tests.auxil.networking import OfflineRequest, expect_bad_request from tests.auxil.pytest_classes import PytestBot, PytestExtBot, make_bot from tests.auxil.slots import mro_slots @@ -4231,7 +4231,7 @@ async def test_do_api_request_basic_and_files(self, bot, chat_id, return_type): assert result.chat_id == int(chat_id) assert result.caption == "test_caption" out = BytesIO() - await (await result.document.get_file()).download_to_memory(out) + await (await stable_get_file(bot, result.document)).download_to_memory(out) out.seek(0) assert out.read() == data_file("telegram.png").open("rb").read() assert result.document.file_name == "telegram.png" @@ -4270,7 +4270,7 @@ async def test_do_api_request_list_return_type(self, bot, chat_id, return_type): assert isinstance(message, Message) assert message.chat_id == int(chat_id) out = BytesIO() - await (await message.document.get_file()).download_to_memory(out) + await (await stable_get_file(bot, message.document)).download_to_memory(out) out.seek(0) assert out.read() == data_file(file_name).open("rb").read() assert message.document.file_name == file_name From 1e3b147a80bf7d96942d1ad28ff9cd6ebaa0bd4b Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sat, 2 Nov 2024 18:58:30 +0100 Subject: [PATCH 4/7] Use `Union` instead of `|` --- tests/auxil/files.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/auxil/files.py b/tests/auxil/files.py index b2021bea9bb..eead241cfe5 100644 --- a/tests/auxil/files.py +++ b/tests/auxil/files.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. import logging from pathlib import Path -from typing import Protocol +from typing import Protocol, Union from telegram import Bot, File @@ -34,7 +34,7 @@ class _HasGetFile(Protocol): async def get_file(self) -> File: ... -async def stable_get_file(bot: Bot, obj: _HasGetFile | str) -> File: +async def stable_get_file(bot: Bot, obj: Union[_HasGetFile, str]) -> File: """Temporary workaround for Telegram API returning file_path as None on first call to get_file. This function will attempt to get the file 3 times before raising an error. Remove this once https://github.com/tdlib/telegram-bot-api/issues/658 as closed.""" From d36f12c02e1d136fbd0bc5b1574ce6bf9fc319ee Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 3 Nov 2024 14:45:26 +0100 Subject: [PATCH 5/7] Revert "Use `Union` instead of `|`" This reverts commit 1e3b147a80bf7d96942d1ad28ff9cd6ebaa0bd4b. --- tests/auxil/files.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/auxil/files.py b/tests/auxil/files.py index eead241cfe5..b2021bea9bb 100644 --- a/tests/auxil/files.py +++ b/tests/auxil/files.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. import logging from pathlib import Path -from typing import Protocol, Union +from typing import Protocol from telegram import Bot, File @@ -34,7 +34,7 @@ class _HasGetFile(Protocol): async def get_file(self) -> File: ... -async def stable_get_file(bot: Bot, obj: Union[_HasGetFile, str]) -> File: +async def stable_get_file(bot: Bot, obj: _HasGetFile | str) -> File: """Temporary workaround for Telegram API returning file_path as None on first call to get_file. This function will attempt to get the file 3 times before raising an error. Remove this once https://github.com/tdlib/telegram-bot-api/issues/658 as closed.""" From 1ac480c5ee9f2fe41e839011f326e6578d826450 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 3 Nov 2024 14:50:32 +0100 Subject: [PATCH 6/7] Revert addition of `stable_get_file` --- tests/_files/test_animation.py | 4 ++-- tests/_files/test_audio.py | 4 ++-- tests/_files/test_chatphoto.py | 7 ++----- tests/_files/test_document.py | 4 ++-- tests/_files/test_inputfile.py | 6 +++--- tests/_files/test_sticker.py | 4 ++-- tests/_files/test_video.py | 4 ++-- tests/_files/test_videonote.py | 4 ++-- tests/_files/test_voice.py | 4 ++-- tests/auxil/files.py | 21 --------------------- tests/test_bot.py | 6 +++--- 11 files changed, 22 insertions(+), 46 deletions(-) diff --git a/tests/_files/test_animation.py b/tests/_files/test_animation.py index 6face27d667..0f581259db9 100644 --- a/tests/_files/test_animation.py +++ b/tests/_files/test_animation.py @@ -33,7 +33,7 @@ check_shortcut_signature, ) from tests.auxil.build_messages import make_message -from tests.auxil.files import data_file, stable_get_file +from tests.auxil.files import data_file from tests.auxil.slots import mro_slots @@ -245,7 +245,7 @@ async def test_send_all_args(self, bot, chat_id, animation_file, animation, thum pytest.xfail("This is a bug on Telegram's end") async def test_get_and_download(self, bot, animation, tmp_file): - new_file = await stable_get_file(bot, animation.file_id) + new_file = await bot.get_file(animation.file_id) assert new_file.file_path.startswith("https://") diff --git a/tests/_files/test_audio.py b/tests/_files/test_audio.py index 358aae57063..08e598cb267 100644 --- a/tests/_files/test_audio.py +++ b/tests/_files/test_audio.py @@ -33,7 +33,7 @@ check_shortcut_signature, ) from tests.auxil.build_messages import make_message -from tests.auxil.files import data_file, stable_get_file +from tests.auxil.files import data_file from tests.auxil.slots import mro_slots @@ -244,7 +244,7 @@ async def test_send_all_args(self, bot, chat_id, audio_file, thumb_file): assert message.has_protected_content async def test_get_and_download(self, bot, chat_id, audio, tmp_file): - new_file = await stable_get_file(bot, audio.file_id) + new_file = await bot.get_file(audio.file_id) assert new_file.file_size == self.file_size assert new_file.file_unique_id == audio.file_unique_id diff --git a/tests/_files/test_chatphoto.py b/tests/_files/test_chatphoto.py index 9b34986369c..5bbb6a8f71e 100644 --- a/tests/_files/test_chatphoto.py +++ b/tests/_files/test_chatphoto.py @@ -31,7 +31,7 @@ check_shortcut_call, check_shortcut_signature, ) -from tests.auxil.files import data_file, stable_get_file +from tests.auxil.files import data_file from tests.auxil.networking import expect_bad_request from tests.auxil.slots import mro_slots @@ -158,10 +158,7 @@ async def make_assertion(*_, **kwargs): class TestChatPhotoWithRequest: async def test_get_and_download(self, bot, chat_photo, tmp_file): - tasks = { - stable_get_file(bot, chat_photo.small_file_id), - stable_get_file(bot, chat_photo.big_file_id), - } + tasks = {bot.get_file(chat_photo.small_file_id), bot.get_file(chat_photo.big_file_id)} asserts = [] for task in asyncio.as_completed(tasks): diff --git a/tests/_files/test_document.py b/tests/_files/test_document.py index b0456e103cc..e6037403408 100644 --- a/tests/_files/test_document.py +++ b/tests/_files/test_document.py @@ -33,7 +33,7 @@ check_shortcut_signature, ) from tests.auxil.build_messages import make_message -from tests.auxil.files import data_file, stable_get_file +from tests.auxil.files import data_file from tests.auxil.slots import mro_slots @@ -216,7 +216,7 @@ async def test_error_send_empty_file_id(self, bot, chat_id): await bot.send_document(chat_id=chat_id, document="") async def test_get_and_download(self, bot, document, chat_id, tmp_file): - new_file = await stable_get_file(bot, document.file_id) + new_file = await bot.get_file(document.file_id) assert new_file.file_size == document.file_size assert new_file.file_unique_id == document.file_unique_id diff --git a/tests/_files/test_inputfile.py b/tests/_files/test_inputfile.py index e733b5ca1f7..b7235497b92 100644 --- a/tests/_files/test_inputfile.py +++ b/tests/_files/test_inputfile.py @@ -25,7 +25,7 @@ from telegram import InputFile from telegram._utils.strings import TextEncoding -from tests.auxil.files import data_file, stable_get_file +from tests.auxil.files import data_file from tests.auxil.slots import mro_slots @@ -214,7 +214,7 @@ async def test_send_bytes(self, bot, chat_id): message = await bot.send_document(chat_id, data_file("text_file.txt").read_bytes()) out = BytesIO() - await (await stable_get_file(bot, message.document)).download_to_memory(out=out) + await (await message.document.get_file()).download_to_memory(out=out) out.seek(0) assert out.read().decode(TextEncoding.UTF_8) == "PTB Rocks! ⅞" @@ -227,7 +227,7 @@ async def test_send_string(self, bot, chat_id): ) out = BytesIO() - await (await stable_get_file(bot, message.document)).download_to_memory(out=out) + await (await message.document.get_file()).download_to_memory(out=out) out.seek(0) assert out.read().decode(TextEncoding.UTF_8) == "PTB Rocks! ⅞" diff --git a/tests/_files/test_sticker.py b/tests/_files/test_sticker.py index 5ee8e4c514f..d77f93ac776 100644 --- a/tests/_files/test_sticker.py +++ b/tests/_files/test_sticker.py @@ -45,7 +45,7 @@ check_shortcut_signature, ) from tests.auxil.build_messages import make_message -from tests.auxil.files import data_file, stable_get_file +from tests.auxil.files import data_file from tests.auxil.slots import mro_slots @@ -336,7 +336,7 @@ async def test_send_all_args(self, bot, chat_id, sticker_file, sticker): assert message.sticker.thumbnail.file_size == sticker.thumbnail.file_size async def test_get_and_download(self, bot, sticker, tmp_file): - new_file = await stable_get_file(bot, sticker.file_id) + new_file = await bot.get_file(sticker.file_id) assert new_file.file_size == sticker.file_size assert new_file.file_unique_id == sticker.file_unique_id diff --git a/tests/_files/test_video.py b/tests/_files/test_video.py index 9594be16c82..66230389f7e 100644 --- a/tests/_files/test_video.py +++ b/tests/_files/test_video.py @@ -33,7 +33,7 @@ check_shortcut_signature, ) from tests.auxil.build_messages import make_message -from tests.auxil.files import data_file, stable_get_file +from tests.auxil.files import data_file from tests.auxil.slots import mro_slots @@ -257,7 +257,7 @@ async def test_send_all_args(self, bot, chat_id, video_file, video, thumb_file): assert message.show_caption_above_media async def test_get_and_download(self, bot, video, chat_id, tmp_file): - new_file = await stable_get_file(bot, video.file_id) + new_file = await bot.get_file(video.file_id) assert new_file.file_size == self.file_size assert new_file.file_unique_id == video.file_unique_id diff --git a/tests/_files/test_videonote.py b/tests/_files/test_videonote.py index ed16ddd393a..ce69fa8f850 100644 --- a/tests/_files/test_videonote.py +++ b/tests/_files/test_videonote.py @@ -32,7 +32,7 @@ check_shortcut_signature, ) from tests.auxil.build_messages import make_message -from tests.auxil.files import data_file, stable_get_file +from tests.auxil.files import data_file from tests.auxil.slots import mro_slots @@ -249,7 +249,7 @@ async def test_send_all_args(self, bot, chat_id, video_note_file, video_note, th assert message.has_protected_content async def test_get_and_download(self, bot, video_note, tmp_file): - new_file = await stable_get_file(bot, video_note.file_id) + new_file = await bot.get_file(video_note.file_id) assert new_file.file_size == self.file_size assert new_file.file_unique_id == video_note.file_unique_id diff --git a/tests/_files/test_voice.py b/tests/_files/test_voice.py index 81dfc07dcd8..e32bb195c8e 100644 --- a/tests/_files/test_voice.py +++ b/tests/_files/test_voice.py @@ -33,7 +33,7 @@ check_shortcut_signature, ) from tests.auxil.build_messages import make_message -from tests.auxil.files import data_file, stable_get_file +from tests.auxil.files import data_file from tests.auxil.slots import mro_slots @@ -227,7 +227,7 @@ async def test_send_all_args(self, bot, chat_id, voice_file, voice): assert message.has_protected_content async def test_get_and_download(self, bot, voice, chat_id, tmp_file): - new_file = await stable_get_file(bot, voice.file_id) + new_file = await bot.get_file(voice.file_id) assert new_file.file_size == voice.file_size assert new_file.file_unique_id == voice.file_unique_id diff --git a/tests/auxil/files.py b/tests/auxil/files.py index b2021bea9bb..6b1f87eacff 100644 --- a/tests/auxil/files.py +++ b/tests/auxil/files.py @@ -16,11 +16,7 @@ # # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. -import logging from pathlib import Path -from typing import Protocol - -from telegram import Bot, File PROJECT_ROOT_PATH = Path(__file__).parent.parent.parent.resolve() TEST_DATA_PATH = PROJECT_ROOT_PATH / "tests" / "data" @@ -28,20 +24,3 @@ def data_file(filename: str) -> Path: return TEST_DATA_PATH / filename - - -class _HasGetFile(Protocol): - async def get_file(self) -> File: ... - - -async def stable_get_file(bot: Bot, obj: _HasGetFile | str) -> File: - """Temporary workaround for Telegram API returning file_path as None on first call to - get_file. This function will attempt to get the file 3 times before raising an error. - Remove this once https://github.com/tdlib/telegram-bot-api/issues/658 as closed.""" - for i in range(3): - file = await bot.get_file(obj) if isinstance(obj, str) else await obj.get_file() - if file.file_path: - logging.debug("File path returned by TG on attempt %s", i + 1) - return file - - raise RuntimeError("File path not found returned by TG after 3 attempts") diff --git a/tests/test_bot.py b/tests/test_bot.py index 0fd9482e51c..7f060fb0992 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -97,7 +97,7 @@ from tests.auxil.bot_method_checks import check_defaults_handling from tests.auxil.ci_bots import FALLBACKS from tests.auxil.envvars import GITHUB_ACTION, TEST_WITH_OPT_DEPS -from tests.auxil.files import data_file, stable_get_file +from tests.auxil.files import data_file from tests.auxil.networking import OfflineRequest, expect_bad_request from tests.auxil.pytest_classes import PytestBot, PytestExtBot, make_bot from tests.auxil.slots import mro_slots @@ -4231,7 +4231,7 @@ async def test_do_api_request_basic_and_files(self, bot, chat_id, return_type): assert result.chat_id == int(chat_id) assert result.caption == "test_caption" out = BytesIO() - await (await stable_get_file(bot, result.document)).download_to_memory(out) + await (await result.document.get_file()).download_to_memory(out) out.seek(0) assert out.read() == data_file("telegram.png").open("rb").read() assert result.document.file_name == "telegram.png" @@ -4270,7 +4270,7 @@ async def test_do_api_request_list_return_type(self, bot, chat_id, return_type): assert isinstance(message, Message) assert message.chat_id == int(chat_id) out = BytesIO() - await (await stable_get_file(bot, message.document)).download_to_memory(out) + await (await message.document.get_file()).download_to_memory(out) out.seek(0) assert out.read() == data_file(file_name).open("rb").read() assert message.document.file_name == file_name From fe586174aadbefcda46c1589406f079b7941eac9 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 3 Nov 2024 14:57:42 +0100 Subject: [PATCH 7/7] Coverage problem --- telegram/_files/file.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telegram/_files/file.py b/telegram/_files/file.py index 667ae1d6c74..640dfc96884 100644 --- a/telegram/_files/file.py +++ b/telegram/_files/file.py @@ -279,7 +279,7 @@ async def download_to_memory( path = Path(self.file_path) if local_file else None if local_file: buf = path.read_bytes() - elif url: + else: buf = await self.get_bot().request.retrieve( url, read_timeout=read_timeout,