Merge branch 'aiogram:dev-3.x' into dev-3.x

This commit is contained in:
m-xim 2025-02-14 01:32:22 +03:00 committed by GitHub
commit b939b4e95d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 27 additions and 14 deletions

1
CHANGES/1612.misc.rst Normal file
View file

@ -0,0 +1 @@
Removed redundant :code:`Path` to :code:`str` convertion on file download.

1
CHANGES/1628.bugfix.rst Normal file
View file

@ -0,0 +1 @@
Change the :code:`Downloadable` protocol to be non-writable to shut up type checking that checks code that uses the :code:`bot.download(...)` method

1
CHANGES/1630.bugfix.rst Normal file
View file

@ -0,0 +1 @@
Fix the regex pattern that finds the "bad characters" for deeplink payload.

1
CHANGES/1631.misc.rst Normal file
View file

@ -0,0 +1 @@
Increased max :code:`redis` version support from “<5.1.0” to “<5.3.0”

View file

@ -391,7 +391,7 @@ class Bot:
@classmethod @classmethod
async def __aiofiles_reader( async def __aiofiles_reader(
cls, file: str, chunk_size: int = 65536 cls, file: Union[str, pathlib.Path], chunk_size: int = 65536
) -> AsyncGenerator[bytes, None]: ) -> AsyncGenerator[bytes, None]:
async with aiofiles.open(file, "rb") as f: async with aiofiles.open(file, "rb") as f:
while chunk := await f.read(chunk_size): while chunk := await f.read(chunk_size):
@ -399,7 +399,7 @@ class Bot:
async def download_file( async def download_file(
self, self,
file_path: str, file_path: Union[str, pathlib.Path],
destination: Optional[Union[BinaryIO, pathlib.Path, str]] = None, destination: Optional[Union[BinaryIO, pathlib.Path, str]] = None,
timeout: int = 30, timeout: int = 30,
chunk_size: int = 65536, chunk_size: int = 65536,
@ -423,7 +423,7 @@ class Bot:
close_stream = False close_stream = False
if self.session.api.is_local: if self.session.api.is_local:
stream = self.__aiofiles_reader( stream = self.__aiofiles_reader(
str(self.session.api.wrap_local_file.to_local(file_path)), chunk_size=chunk_size self.session.api.wrap_local_file.to_local(file_path), chunk_size=chunk_size
) )
close_stream = True close_stream = True
else: else:

View file

@ -67,7 +67,7 @@ class TelegramAPIServer:
""" """
return self.base.format(token=token, method=method) return self.base.format(token=token, method=method)
def file_url(self, token: str, path: str) -> str: def file_url(self, token: str, path: Union[str, Path]) -> str:
""" """
Generate URL for downloading files Generate URL for downloading files

View file

@ -2,4 +2,5 @@ from typing import Protocol
class Downloadable(Protocol): class Downloadable(Protocol):
file_id: str @property
def file_id(self) -> str: ...

View file

@ -18,7 +18,7 @@ from aiogram.utils.payload import decode_payload, encode_payload
if TYPE_CHECKING: if TYPE_CHECKING:
from aiogram import Bot from aiogram import Bot
BAD_PATTERN = re.compile(r"[^A-z0-9-]") BAD_PATTERN = re.compile(r"[^a-zA-Z0-9-_]")
async def create_start_link( async def create_start_link(

View file

@ -173,7 +173,7 @@ msgstr ""
#: ../../../README.rst:71 #: ../../../README.rst:71
msgid "If you have any questions, you can visit our community chats on Telegram:" msgid "If you have any questions, you can visit our community chats on Telegram:"
msgstr "Якщо є якість додаткові запитання, ласкаво просимо до онлайн-спільнот:" msgstr "Якщо є додаткові запитання, ласкаво просимо до онлайн-спільнот:"
#: ../../../README.rst:73 #: ../../../README.rst:73
msgid "🇺🇸 `@aiogram <https://t.me/aiogram>`_" msgid "🇺🇸 `@aiogram <https://t.me/aiogram>`_"

View file

@ -60,7 +60,7 @@ fast = [
"aiodns>=3.0.0", "aiodns>=3.0.0",
] ]
redis = [ redis = [
"redis[hiredis]>=5.0.1,<5.1.0", "redis[hiredis]>=5.0.1,<5.3.0",
] ]
mongo = [ mongo = [
"motor>=3.3.2,<3.7.0", "motor>=3.3.2,<3.7.0",

View file

@ -1,5 +1,7 @@
from pathlib import Path from pathlib import Path
import pytest
from aiogram.client.telegram import ( from aiogram.client.telegram import (
PRODUCTION, PRODUCTION,
BareFilesPathWrapper, BareFilesPathWrapper,
@ -13,15 +15,17 @@ class TestAPIServer:
method_url = PRODUCTION.api_url(token="42:TEST", method="apiMethod") method_url = PRODUCTION.api_url(token="42:TEST", method="apiMethod")
assert method_url == "https://api.telegram.org/bot42:TEST/apiMethod" assert method_url == "https://api.telegram.org/bot42:TEST/apiMethod"
def test_file_url(self): @pytest.mark.parametrize("path", ["path", Path("path")])
file_url = PRODUCTION.file_url(token="42:TEST", path="path") def test_file_url(self, path):
file_url = PRODUCTION.file_url(token="42:TEST", path=path)
assert file_url == "https://api.telegram.org/file/bot42:TEST/path" assert file_url == "https://api.telegram.org/file/bot42:TEST/path"
def test_from_base(self): @pytest.mark.parametrize("path", ["path", Path("path")])
def test_from_base(self, path):
local_server = TelegramAPIServer.from_base("http://localhost:8081", is_local=True) local_server = TelegramAPIServer.from_base("http://localhost:8081", is_local=True)
method_url = local_server.api_url("42:TEST", method="apiMethod") method_url = local_server.api_url("42:TEST", method="apiMethod")
file_url = local_server.file_url(token="42:TEST", path="path") file_url = local_server.file_url(token="42:TEST", path=path)
assert method_url == "http://localhost:8081/bot42:TEST/apiMethod" assert method_url == "http://localhost:8081/bot42:TEST/apiMethod"
assert file_url == "http://localhost:8081/file/bot42:TEST/path" assert file_url == "http://localhost:8081/file/bot42:TEST/path"

View file

@ -1,5 +1,6 @@
import io import io
import os import os
from pathlib import Path
from tempfile import mkstemp from tempfile import mkstemp
from unittest.mock import AsyncMock, MagicMock, patch from unittest.mock import AsyncMock, MagicMock, patch
@ -112,7 +113,8 @@ class TestBot:
mocked_close.assert_not_awaited() mocked_close.assert_not_awaited()
await session.close() await session.close()
async def test_download_file(self, aresponses: ResponsesMockServer): @pytest.mark.parametrize("file_path", ["file.png", Path("file.png")])
async def test_download_file(self, aresponses: ResponsesMockServer, file_path):
aresponses.add( aresponses.add(
method_pattern="get", method_pattern="get",
response=aresponses.Response(status=200, body=b"\f" * 10), response=aresponses.Response(status=200, body=b"\f" * 10),
@ -127,7 +129,7 @@ class TestBot:
async with Bot("42:TEST").context() as bot: async with Bot("42:TEST").context() as bot:
with patch("aiofiles.threadpool.sync_open", return_value=mock_file): with patch("aiofiles.threadpool.sync_open", return_value=mock_file):
await bot.download_file("TEST", "file.png") await bot.download_file("TEST", file_path)
mock_file.write.assert_called_once_with(b"\f" * 10) mock_file.write.assert_called_once_with(b"\f" * 10)
async def test_download_file_default_destination( async def test_download_file_default_destination(

View file

@ -10,12 +10,14 @@ PAYLOADS = [
"aaBBccDDeeFF5544332211", "aaBBccDDeeFF5544332211",
-12345678901234567890, -12345678901234567890,
12345678901234567890, 12345678901234567890,
"underscore_and-dash",
] ]
WRONG_PAYLOADS = [ WRONG_PAYLOADS = [
"@BotFather", "@BotFather",
"Some:special$characters#=", "Some:special$characters#=",
"spaces spaces spaces", "spaces spaces spaces",
1234567890123456789.0, 1234567890123456789.0,
"has`backtick",
] ]