mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2024-12-22 14:35:00 +01:00
Add recursive
Parameter to TelegramObject.to_dict()
(#3276)
This commit is contained in:
parent
210f9afd66
commit
24d390e1aa
24 changed files with 97 additions and 70 deletions
|
@ -8205,7 +8205,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict: # skipcq: PYL-W0613
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data: JSONDict = {"id": self.id, "username": self.username, "first_name": self.first_name}
|
||||
|
||||
|
|
|
@ -151,9 +151,9 @@ class ChatInviteLink(TelegramObject):
|
|||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
data = super().to_dict(recursive=recursive)
|
||||
|
||||
data["expire_date"] = to_timestamp(self.expire_date)
|
||||
|
||||
|
|
|
@ -103,9 +103,9 @@ class ChatJoinRequest(TelegramObject):
|
|||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
data = super().to_dict(recursive=recursive)
|
||||
|
||||
data["date"] = to_timestamp(self.date)
|
||||
|
||||
|
|
|
@ -123,9 +123,9 @@ class ChatMember(TelegramObject):
|
|||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
data = super().to_dict(recursive=recursive)
|
||||
|
||||
if data.get("until_date", False):
|
||||
data["until_date"] = to_timestamp(data["until_date"])
|
||||
|
|
|
@ -122,9 +122,9 @@ class ChatMemberUpdated(TelegramObject):
|
|||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
data = super().to_dict(recursive=recursive)
|
||||
|
||||
# Required
|
||||
data["date"] = to_timestamp(self.date)
|
||||
|
|
|
@ -90,9 +90,9 @@ class InputMedia(TelegramObject):
|
|||
self.caption_entities = caption_entities
|
||||
self.parse_mode = parse_mode
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
data = super().to_dict(recursive=recursive)
|
||||
|
||||
if self.caption_entities:
|
||||
data["caption_entities"] = [ce.to_dict() for ce in self.caption_entities]
|
||||
|
|
|
@ -282,9 +282,9 @@ class StickerSet(TelegramObject):
|
|||
|
||||
return super()._de_json(data=data, bot=bot, api_kwargs=api_kwargs)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
data = super().to_dict(recursive=recursive)
|
||||
|
||||
data["stickers"] = [s.to_dict() for s in data.get("stickers")] # type: ignore[union-attr]
|
||||
|
||||
|
|
|
@ -117,9 +117,9 @@ class Game(TelegramObject):
|
|||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
data = super().to_dict(recursive=recursive)
|
||||
|
||||
data["photo"] = [p.to_dict() for p in self.photo]
|
||||
if self.text_entities:
|
||||
|
|
|
@ -68,9 +68,9 @@ class InlineKeyboardMarkup(TelegramObject):
|
|||
|
||||
self._id_attrs = (self.inline_keyboard,)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
data = super().to_dict(recursive=recursive)
|
||||
|
||||
data["inline_keyboard"] = []
|
||||
for inline_keyboard in self.inline_keyboard:
|
||||
|
|
|
@ -54,9 +54,9 @@ class InlineQueryResult(TelegramObject):
|
|||
|
||||
self._id_attrs = (self.id,)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
data = super().to_dict(recursive=recursive)
|
||||
|
||||
# pylint: disable=no-member
|
||||
if (
|
||||
|
|
|
@ -228,9 +228,9 @@ class InputInvoiceMessageContent(InputMessageContent):
|
|||
)
|
||||
)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
data = super().to_dict(recursive=recursive)
|
||||
|
||||
data["prices"] = [price.to_dict() for price in self.prices]
|
||||
|
||||
|
|
|
@ -84,9 +84,9 @@ class InputTextMessageContent(InputMessageContent):
|
|||
|
||||
self._id_attrs = (self.message_text,)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
data = super().to_dict(recursive=recursive)
|
||||
|
||||
if self.entities:
|
||||
data["entities"] = [ce.to_dict() for ce in self.entities]
|
||||
|
|
|
@ -161,9 +161,9 @@ class MenuButtonWebApp(MenuButton):
|
|||
|
||||
return super().de_json(data=data, bot=bot) # type: ignore[return-value]
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
data = super().to_dict(recursive=recursive)
|
||||
data["web_app"] = self.web_app.to_dict()
|
||||
return data
|
||||
|
||||
|
|
|
@ -719,9 +719,9 @@ class Message(TelegramObject):
|
|||
|
||||
return self._effective_attachment # type: ignore[return-value]
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
data = super().to_dict(recursive=recursive)
|
||||
|
||||
# Required
|
||||
data["date"] = to_timestamp(self.date)
|
||||
|
|
|
@ -404,9 +404,9 @@ class SecureValue(TelegramObject):
|
|||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
data = super().to_dict(recursive=recursive)
|
||||
|
||||
data["files"] = [p.to_dict() for p in self.files] # type: ignore[union-attr]
|
||||
data["translation"] = [p.to_dict() for p in self.translation] # type: ignore[union-attr]
|
||||
|
@ -450,9 +450,9 @@ class DataCredentials(_CredentialsBase):
|
|||
def __init__(self, data_hash: str, secret: str, *, api_kwargs: JSONDict = None):
|
||||
super().__init__(hash=data_hash, secret=secret, api_kwargs=api_kwargs)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
data = super().to_dict(recursive=recursive)
|
||||
|
||||
del data["file_hash"]
|
||||
del data["hash"]
|
||||
|
@ -479,9 +479,9 @@ class FileCredentials(_CredentialsBase):
|
|||
def __init__(self, file_hash: str, secret: str, *, api_kwargs: JSONDict = None):
|
||||
super().__init__(hash=file_hash, secret=secret, api_kwargs=api_kwargs)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
data = super().to_dict(recursive=recursive)
|
||||
|
||||
del data["data_hash"]
|
||||
del data["hash"]
|
||||
|
|
|
@ -245,9 +245,9 @@ class EncryptedPassportElement(TelegramObject):
|
|||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
data = super().to_dict(recursive=recursive)
|
||||
|
||||
if self.files:
|
||||
data["files"] = [p.to_dict() for p in self.files]
|
||||
|
|
|
@ -81,9 +81,9 @@ class PassportData(TelegramObject):
|
|||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
data = super().to_dict(recursive=recursive)
|
||||
|
||||
data["data"] = [e.to_dict() for e in self.data]
|
||||
|
||||
|
|
|
@ -65,9 +65,9 @@ class ShippingOption(TelegramObject):
|
|||
|
||||
self._id_attrs = (self.id,)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
data = super().to_dict(recursive=recursive)
|
||||
|
||||
data["prices"] = [p.to_dict() for p in self.prices]
|
||||
|
||||
|
|
|
@ -228,9 +228,9 @@ class Poll(TelegramObject):
|
|||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
data = super().to_dict(recursive=recursive)
|
||||
|
||||
data["options"] = [x.to_dict() for x in self.options]
|
||||
if self.explanation_entities:
|
||||
|
|
|
@ -119,9 +119,9 @@ class ReplyKeyboardMarkup(TelegramObject):
|
|||
|
||||
self._id_attrs = (self.keyboard,)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
data = super().to_dict(recursive=recursive)
|
||||
|
||||
data["keyboard"] = []
|
||||
for row in self.keyboard:
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
import inspect
|
||||
import json
|
||||
from copy import deepcopy
|
||||
from itertools import chain
|
||||
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, Type, TypeVar, Union
|
||||
|
||||
from telegram._utils.types import JSONDict
|
||||
|
@ -160,8 +161,8 @@ class TelegramObject:
|
|||
|
||||
Args:
|
||||
include_private (:obj:`bool`): Whether the result should include private variables.
|
||||
recursive (:obj:`bool`): If :obj:`True`, will convert any TelegramObjects (if found) in
|
||||
the attributes to a dictionary. Else, preserves it as an object itself.
|
||||
recursive (:obj:`bool`): If :obj:`True`, will convert any ``TelegramObjects`` (if
|
||||
found) in the attributes to a dictionary. Else, preserves it as an object itself.
|
||||
remove_bot (:obj:`bool`): Whether the bot should be included in the result.
|
||||
|
||||
Returns:
|
||||
|
@ -169,24 +170,19 @@ class TelegramObject:
|
|||
"""
|
||||
data = {}
|
||||
|
||||
if not recursive:
|
||||
try:
|
||||
# __dict__ has attrs from superclasses, so no need to put in the for loop below
|
||||
data.update(self.__dict__)
|
||||
except AttributeError:
|
||||
pass
|
||||
# We want to get all attributes for the class, using self.__slots__ only includes the
|
||||
# attributes used by that class itself, and not its superclass(es). Hence, we get its MRO
|
||||
# and then get their attributes. The `[:-1]` slice excludes the `object` class
|
||||
for cls in self.__class__.__mro__[:-1]:
|
||||
for key in cls.__slots__: # type: ignore[attr-defined]
|
||||
all_slots = (s for c in self.__class__.__mro__[:-1] for s in c.__slots__) # type: ignore
|
||||
# chain the class's slots with the user defined subclass __dict__ (class has no slots)
|
||||
for key in chain(self.__dict__, all_slots) if hasattr(self, "__dict__") else all_slots:
|
||||
if not include_private and key.startswith("_"):
|
||||
continue
|
||||
|
||||
value = getattr(self, key, None)
|
||||
if value is not None:
|
||||
if recursive and hasattr(value, "to_dict"):
|
||||
data[key] = value.to_dict() # pylint: disable=no-member
|
||||
data[key] = value.to_dict(recursive=True) # pylint: disable=no-member
|
||||
else:
|
||||
data[key] = value
|
||||
elif not recursive:
|
||||
|
@ -279,16 +275,23 @@ class TelegramObject:
|
|||
"""
|
||||
return json.dumps(self.to_dict())
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""Gives representation of object as :obj:`dict`.
|
||||
|
||||
.. versionchanged:: 20.0
|
||||
Now includes all entries of :attr:`api_kwargs`.
|
||||
|
||||
Args:
|
||||
recursive (:obj:`bool`, optional): If :obj:`True`, will convert any TelegramObjects
|
||||
(if found) in the attributes to a dictionary. Else, preserves it as an object
|
||||
itself. Defaults to :obj:`True`.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
|
||||
Returns:
|
||||
:obj:`dict`
|
||||
"""
|
||||
out = self._get_attrs(recursive=True)
|
||||
out = self._get_attrs(recursive=recursive)
|
||||
out.update(out.pop("api_kwargs", {})) # type: ignore[call-overload]
|
||||
return out
|
||||
|
||||
|
|
|
@ -69,9 +69,9 @@ class UserProfilePhotos(TelegramObject):
|
|||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
data = super().to_dict(recursive=recursive)
|
||||
|
||||
data["photos"] = []
|
||||
for photo in self.photos:
|
||||
|
|
|
@ -121,9 +121,9 @@ class VideoChatParticipantsInvited(TelegramObject):
|
|||
data["users"] = User.de_list(data.get("users", []), bot)
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
data = super().to_dict(recursive=recursive)
|
||||
|
||||
if self.users is not None:
|
||||
data["users"] = [u.to_dict() for u in self.users]
|
||||
|
@ -176,9 +176,9 @@ class VideoChatScheduled(TelegramObject):
|
|||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
def to_dict(self, recursive: bool = True) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
data = super().to_dict(recursive=recursive)
|
||||
|
||||
# Required
|
||||
data["start_date"] = to_timestamp(self.start_date)
|
||||
|
|
|
@ -138,6 +138,30 @@ class TestTelegramObject:
|
|||
to = TelegramObject(api_kwargs={"foo": "bar"})
|
||||
assert to.to_dict() == {"foo": "bar"}
|
||||
|
||||
def test_to_dict_recursion(self):
|
||||
class Recursive(TelegramObject):
|
||||
__slots__ = ("recursive",)
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.recursive = "recursive"
|
||||
|
||||
class SubClass(TelegramObject):
|
||||
"""This class doesn't have `__slots__`, so has `__dict__` instead."""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.subclass = Recursive()
|
||||
|
||||
to = SubClass()
|
||||
to_dict_no_recurse = to.to_dict(recursive=False)
|
||||
assert to_dict_no_recurse
|
||||
assert isinstance(to_dict_no_recurse["subclass"], Recursive)
|
||||
to_dict_recurse = to.to_dict(recursive=True)
|
||||
assert to_dict_recurse
|
||||
assert isinstance(to_dict_recurse["subclass"], dict)
|
||||
assert to_dict_recurse["subclass"]["recursive"] == "recursive"
|
||||
|
||||
def test_slot_behaviour(self, mro_slots):
|
||||
inst = TelegramObject()
|
||||
for attr in inst.__slots__:
|
||||
|
|
Loading…
Reference in a new issue