fix: change order of Union types for media properties

The order of Union types for media properties in various files was
reversed to `Union[str, InputFile]` for consistency. The changes were
made in the following files:
- aiogram/types/input_media_video.py
- aiogram/types/input_media_audio.py
- aiogram/types/input_media_document.py
- aiogram/types/input_media_animation.py
- aiogram/types/input_media_photo.py
- aiogram/utils/media_group.py

fix: update type check in base.py for TelegramObject (dev-3)

Replaced BotContextController with TelegramObject in the type check to
ensure proper handling of non-standard objects.

test: add TODO comments in test files (dev-3)

Added TODO comments to highlight parts of the code that need future
improvements in the following test files:
- tests/test_api/test_types/test_input_file.py
- tests/test_api/test_client/test_session/test
This commit is contained in:
zemf4you 2024-05-20 15:55:06 +07:00
parent 1ea41076cd
commit 534221a30a
12 changed files with 33 additions and 32 deletions

View file

@ -20,7 +20,9 @@ from typing import (
import aiofiles
from aiogram.utils.token import extract_bot_id, validate_token
from .default import Default, DefaultBotProperties
from .session.aiohttp import AiohttpSession
from .session.base import BaseSession
from ..methods import (
AddStickerToSet,
AnswerCallbackQuery,
@ -165,7 +167,6 @@ from ..types import (
ChatMemberOwner,
ChatMemberRestricted,
ChatPermissions,
DateTime,
Downloadable,
File,
ForceReply,
@ -234,9 +235,6 @@ from ..types import (
UserProfilePhotos,
WebhookInfo,
)
from .default import Default, DefaultBotProperties
from .session.aiohttp import AiohttpSession
from .session.base import BaseSession
T = TypeVar("T")
@ -2895,7 +2893,7 @@ class Bot:
explanation_parse_mode: Optional[Union[str, Default]] = Default("parse_mode"),
explanation_entities: Optional[List[MessageEntity]] = None,
open_period: Optional[int] = None,
close_date: Optional[DateTime] = None,
close_date: Optional[Union[datetime.datetime, datetime.timedelta, int]] = None,
is_closed: Optional[bool] = None,
disable_notification: Optional[bool] = None,
protect_content: Optional[Union[bool, Default]] = Default("protect_content"),

View file

@ -45,7 +45,7 @@ class TelegramObject(BotContextController, BaseModel):
Replacing `Default` placeholders with actual values from bot defaults.
Ensures JSON serialization backward compatibility by handling non-standard objects.
"""
if isinstance(self, BotContextController) and isinstance(self, BaseModel):
if isinstance(self, TelegramObject):
properties = self.bot.default if self.bot else DefaultBotProperties()
default_fields = {
key: properties[value.name] for key, value in self if isinstance(value, Default)

View file

@ -20,7 +20,7 @@ class InputMediaAnimation(InputMedia):
type: Literal[InputMediaType.ANIMATION] = InputMediaType.ANIMATION
"""Type of the result, must be *animation*"""
media: Union[InputFile, str]
media: Union[str, InputFile]
"""File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass 'attach://<file_attach_name>' to upload a new one using multipart/form-data under <file_attach_name> name. :ref:`More information on Sending Files » <sending-files>`"""
thumbnail: Optional[InputFile] = None
"""*Optional*. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass 'attach://<file_attach_name>' if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. :ref:`More information on Sending Files » <sending-files>`"""
@ -47,7 +47,7 @@ class InputMediaAnimation(InputMedia):
__pydantic__self__,
*,
type: Literal[InputMediaType.ANIMATION] = InputMediaType.ANIMATION,
media: Union[InputFile, str],
media: Union[str, InputFile],
thumbnail: Optional[InputFile] = None,
caption: Optional[str] = None,
parse_mode: Optional[Union[str, Default]] = Default("parse_mode"),

View file

@ -20,7 +20,7 @@ class InputMediaAudio(InputMedia):
type: Literal[InputMediaType.AUDIO] = InputMediaType.AUDIO
"""Type of the result, must be *audio*"""
media: Union[InputFile, str]
media: Union[str, InputFile]
"""File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass 'attach://<file_attach_name>' to upload a new one using multipart/form-data under <file_attach_name> name. :ref:`More information on Sending Files » <sending-files>`"""
thumbnail: Optional[InputFile] = None
"""*Optional*. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass 'attach://<file_attach_name>' if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. :ref:`More information on Sending Files » <sending-files>`"""
@ -45,7 +45,7 @@ class InputMediaAudio(InputMedia):
__pydantic__self__,
*,
type: Literal[InputMediaType.AUDIO] = InputMediaType.AUDIO,
media: Union[InputFile, str],
media: Union[str, InputFile],
thumbnail: Optional[InputFile] = None,
caption: Optional[str] = None,
parse_mode: Optional[Union[str, Default]] = Default("parse_mode"),

View file

@ -20,7 +20,7 @@ class InputMediaDocument(InputMedia):
type: Literal[InputMediaType.DOCUMENT] = InputMediaType.DOCUMENT
"""Type of the result, must be *document*"""
media: Union[InputFile, str]
media: Union[str, InputFile]
"""File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass 'attach://<file_attach_name>' to upload a new one using multipart/form-data under <file_attach_name> name. :ref:`More information on Sending Files » <sending-files>`"""
thumbnail: Optional[InputFile] = None
"""*Optional*. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass 'attach://<file_attach_name>' if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. :ref:`More information on Sending Files » <sending-files>`"""
@ -41,7 +41,7 @@ class InputMediaDocument(InputMedia):
__pydantic__self__,
*,
type: Literal[InputMediaType.DOCUMENT] = InputMediaType.DOCUMENT,
media: Union[InputFile, str],
media: Union[str, InputFile],
thumbnail: Optional[InputFile] = None,
caption: Optional[str] = None,
parse_mode: Optional[Union[str, Default]] = Default("parse_mode"),

View file

@ -20,7 +20,7 @@ class InputMediaPhoto(InputMedia):
type: Literal[InputMediaType.PHOTO] = InputMediaType.PHOTO
"""Type of the result, must be *photo*"""
media: Union[InputFile, str]
media: Union[str, InputFile]
"""File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass 'attach://<file_attach_name>' to upload a new one using multipart/form-data under <file_attach_name> name. :ref:`More information on Sending Files » <sending-files>`"""
caption: Optional[str] = None
"""*Optional*. Caption of the photo to be sent, 0-1024 characters after entities parsing"""
@ -39,7 +39,7 @@ class InputMediaPhoto(InputMedia):
__pydantic__self__,
*,
type: Literal[InputMediaType.PHOTO] = InputMediaType.PHOTO,
media: Union[InputFile, str],
media: Union[str, InputFile],
caption: Optional[str] = None,
parse_mode: Optional[Union[str, Default]] = Default("parse_mode"),
caption_entities: Optional[List[MessageEntity]] = None,

View file

@ -20,7 +20,7 @@ class InputMediaVideo(InputMedia):
type: Literal[InputMediaType.VIDEO] = InputMediaType.VIDEO
"""Type of the result, must be *video*"""
media: Union[InputFile, str]
media: Union[str, InputFile]
"""File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass 'attach://<file_attach_name>' to upload a new one using multipart/form-data under <file_attach_name> name. :ref:`More information on Sending Files » <sending-files>`"""
thumbnail: Optional[InputFile] = None
"""*Optional*. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass 'attach://<file_attach_name>' if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. :ref:`More information on Sending Files » <sending-files>`"""
@ -49,7 +49,7 @@ class InputMediaVideo(InputMedia):
__pydantic__self__,
*,
type: Literal[InputMediaType.VIDEO] = InputMediaType.VIDEO,
media: Union[InputFile, str],
media: Union[str, InputFile],
thumbnail: Optional[InputFile] = None,
caption: Optional[str] = None,
parse_mode: Optional[Union[str, Default]] = Default("parse_mode"),

View file

@ -63,7 +63,7 @@ class MediaGroupBuilder:
self,
*,
type: Literal[InputMediaType.AUDIO],
media: Union[InputFile, str],
media: Union[str, InputFile],
caption: Optional[str] = None,
parse_mode: Optional[str] = UNSET_PARSE_MODE,
caption_entities: Optional[List[MessageEntity]] = None,
@ -79,7 +79,7 @@ class MediaGroupBuilder:
self,
*,
type: Literal[InputMediaType.PHOTO],
media: Union[InputFile, str],
media: Union[str, InputFile],
caption: Optional[str] = None,
parse_mode: Optional[str] = UNSET_PARSE_MODE,
caption_entities: Optional[List[MessageEntity]] = None,
@ -93,7 +93,7 @@ class MediaGroupBuilder:
self,
*,
type: Literal[InputMediaType.VIDEO],
media: Union[InputFile, str],
media: Union[str, InputFile],
thumbnail: Optional[Union[InputFile, str]] = None,
caption: Optional[str] = None,
parse_mode: Optional[str] = UNSET_PARSE_MODE,
@ -112,7 +112,7 @@ class MediaGroupBuilder:
self,
*,
type: Literal[InputMediaType.DOCUMENT],
media: Union[InputFile, str],
media: Union[str, InputFile],
thumbnail: Optional[Union[InputFile, str]] = None,
caption: Optional[str] = None,
parse_mode: Optional[str] = UNSET_PARSE_MODE,
@ -144,7 +144,7 @@ class MediaGroupBuilder:
def add_audio(
self,
media: Union[InputFile, str],
media: Union[str, InputFile],
thumbnail: Optional[InputFile] = None,
caption: Optional[str] = None,
parse_mode: Optional[str] = UNSET_PARSE_MODE,
@ -194,7 +194,7 @@ class MediaGroupBuilder:
def add_photo(
self,
media: Union[InputFile, str],
media: Union[str, InputFile],
caption: Optional[str] = None,
parse_mode: Optional[str] = UNSET_PARSE_MODE,
caption_entities: Optional[List[MessageEntity]] = None,
@ -233,7 +233,7 @@ class MediaGroupBuilder:
def add_video(
self,
media: Union[InputFile, str],
media: Union[str, InputFile],
thumbnail: Optional[InputFile] = None,
caption: Optional[str] = None,
parse_mode: Optional[str] = UNSET_PARSE_MODE,
@ -295,7 +295,7 @@ class MediaGroupBuilder:
def add_document(
self,
media: Union[InputFile, str],
media: Union[str, InputFile],
thumbnail: Optional[InputFile] = None,
caption: Optional[str] = None,
parse_mode: Optional[str] = UNSET_PARSE_MODE,

View file

@ -8,6 +8,7 @@ from aiohttp import MultipartWriter, web
from aiohttp.abc import Application
from aiohttp.typedefs import Handler
from aiohttp.web_middlewares import middleware
from pydantic_core import from_json, to_json
from aiogram import Bot, Dispatcher, loggers
from aiogram.client.form import extract_files, form_serialize
@ -141,11 +142,11 @@ class BaseRequestHandler(ABC):
async def _handle_request_background(self, bot: Bot, request: web.Request) -> web.Response:
feed_update_task = asyncio.create_task(
self._background_feed_update(bot=bot, update=await request.json())
self._background_feed_update(bot=bot, update=await request.json(loads=from_json))
)
self._background_feed_update_tasks.add(feed_update_task)
feed_update_task.add_done_callback(self._background_feed_update_tasks.discard)
return web.json_response({})
return web.json_response({}) # TODO
def _build_response_writer(
self, bot: Bot, result: Optional[TelegramMethod[TelegramType]]

View file

@ -122,7 +122,7 @@ class TestBaseSession:
),
"1494994302",
],
[LinkPreviewOptions(is_disabled=True, url=None), '{"is_disabled":true}'],
[LinkPreviewOptions(is_disabled=True), '{"is_disabled":true}'],
[Default("parse_mode"), "HTML"],
[Default("protect_content"), "true"],
[Default("link_preview_is_disabled"), "true"],

View file

@ -20,15 +20,15 @@ class TestTelegramMethodRemoveUnset:
)
@pytest.mark.parametrize("obj", [TelegramMethod, TelegramObject])
def test_remove_unset(self, values, names, obj):
validated = obj.remove_unset(values)
validated = obj.remove_unset.wrapped(values)
assert set(validated.keys()) == names
@pytest.mark.parametrize("obj", [TelegramMethod, TelegramObject])
def test_remove_unset_non_dict(self, obj):
assert obj.remove_unset("") == ""
assert obj.remove_unset.wrapped("") == ""
class TestTelegramMethodJsonSerialize:
class TestTelegramMethodModelDumpJson:
@pytest.mark.parametrize(
"obj",
[
@ -39,7 +39,7 @@ class TestTelegramMethodJsonSerialize:
LinkPreviewOptions(),
],
)
def test_json_serialize(self, obj):
def test_model_dump_json(self, obj):
def has_defaults(dump: Dict[str, Any]) -> bool:
return any(isinstance(value, Default) for value in dump.values())

View file

@ -99,3 +99,5 @@ class TestInputFile:
assert chunk_size == 1
size += chunk_size
assert size == 10
# TODO: used_valid_bot