mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2024-12-22 22:45:09 +01:00
Overhaul String Representation of TelegramObject
(#3234)
This commit is contained in:
parent
07f8dd1cb1
commit
f68663af7e
3 changed files with 84 additions and 4 deletions
|
@ -4,3 +4,4 @@ telegram.TelegramObject
|
||||||
.. autoclass:: telegram.TelegramObject
|
.. autoclass:: telegram.TelegramObject
|
||||||
:members:
|
:members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
:special-members: __repr__
|
||||||
|
|
|
@ -21,7 +21,7 @@ import inspect
|
||||||
import json
|
import json
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, Type, TypeVar, Union
|
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Sized, Tuple, Type, TypeVar, Union
|
||||||
|
|
||||||
from telegram._utils.types import JSONDict
|
from telegram._utils.types import JSONDict
|
||||||
from telegram._utils.warnings import warn
|
from telegram._utils.warnings import warn
|
||||||
|
@ -52,6 +52,9 @@ class TelegramObject:
|
||||||
* Removed argument and attribute ``bot`` for several subclasses. Use
|
* Removed argument and attribute ``bot`` for several subclasses. Use
|
||||||
:meth:`set_bot` and :meth:`get_bot` instead.
|
:meth:`set_bot` and :meth:`get_bot` instead.
|
||||||
* Removed the possibility to pass arbitrary keyword arguments for several subclasses.
|
* Removed the possibility to pass arbitrary keyword arguments for several subclasses.
|
||||||
|
* String representations objects of this type was overhauled. See :meth:`__repr__` for
|
||||||
|
details. As this class doesn't implement :meth:`object.__str__`, the default
|
||||||
|
implementation will be used, which is equivalent to :meth:`__repr__`.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
api_kwargs (Dict[:obj:`str`, any], optional): |toapikwargsarg|
|
api_kwargs (Dict[:obj:`str`, any], optional): |toapikwargsarg|
|
||||||
|
@ -100,8 +103,40 @@ class TelegramObject:
|
||||||
if getattr(self, key, True) is None:
|
if getattr(self, key, True) is None:
|
||||||
setattr(self, key, self.api_kwargs.pop(key))
|
setattr(self, key, self.api_kwargs.pop(key))
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return str(self.to_dict())
|
"""Gives a string representation of this object in the form
|
||||||
|
``ClassName(attr_1=value_1, attr_2=value_2, ...)``, where attributes are omitted if they
|
||||||
|
have the value :obj:`None` or empty instances of :class:`collections.abc.Sized` (e.g.
|
||||||
|
:class:`list`, :class:`dict`, :class:`set`, :class:`str`, etc.).
|
||||||
|
|
||||||
|
As this class doesn't implement :meth:`object.__str__`, the default implementation
|
||||||
|
will be used, which is equivalent to :meth:`__repr__`.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
:obj:`str`
|
||||||
|
"""
|
||||||
|
# * `__repr__` goal is to be unambiguous
|
||||||
|
# * `__str__` goal is to be readable
|
||||||
|
# * `str()` calls `__repr__`, if `__str__` is not defined
|
||||||
|
# In our case "unambiguous" and "readable" largely coincide, so we can use the same logic.
|
||||||
|
as_dict = self._get_attrs(recursive=False, include_private=False)
|
||||||
|
|
||||||
|
if not self.api_kwargs:
|
||||||
|
# Drop api_kwargs from the representation, if empty
|
||||||
|
as_dict.pop("api_kwargs", None)
|
||||||
|
|
||||||
|
contents = ", ".join(
|
||||||
|
f"{k}={as_dict[k]!r}"
|
||||||
|
for k in sorted(as_dict.keys())
|
||||||
|
if (
|
||||||
|
as_dict[k] is not None
|
||||||
|
and not (
|
||||||
|
isinstance(as_dict[k], Sized)
|
||||||
|
and len(as_dict[k]) == 0 # type: ignore[arg-type]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return f"{self.__class__.__name__}({contents})"
|
||||||
|
|
||||||
def __getitem__(self, item: str) -> object:
|
def __getitem__(self, item: str) -> object:
|
||||||
if item == "from":
|
if item == "from":
|
||||||
|
|
|
@ -24,7 +24,7 @@ from pathlib import Path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from telegram import Bot, Chat, Message, PhotoSize, TelegramObject, User
|
from telegram import Bot, BotCommand, Chat, Message, PhotoSize, TelegramObject, User
|
||||||
|
|
||||||
|
|
||||||
def all_subclasses(cls):
|
def all_subclasses(cls):
|
||||||
|
@ -284,3 +284,47 @@ class TestTelegramObject:
|
||||||
assert d._private == s._private # Can't test for identity since two equal strings is True
|
assert d._private == s._private # Can't test for identity since two equal strings is True
|
||||||
assert d._bot == s._bot and d._bot is s._bot
|
assert d._bot == s._bot and d._bot is s._bot
|
||||||
assert d.normal == s.normal
|
assert d.normal == s.normal
|
||||||
|
|
||||||
|
def test_string_representation(self):
|
||||||
|
class TGO(TelegramObject):
|
||||||
|
def __init__(self, api_kwargs=None):
|
||||||
|
super().__init__(api_kwargs=api_kwargs)
|
||||||
|
self.string_attr = "string"
|
||||||
|
self.int_attr = 42
|
||||||
|
self.to_attr = BotCommand("command", "description")
|
||||||
|
self.list_attr = [
|
||||||
|
BotCommand("command_1", "description_1"),
|
||||||
|
BotCommand("command_2", "description_2"),
|
||||||
|
]
|
||||||
|
self.dict_attr = {
|
||||||
|
BotCommand("command_1", "description_1"): BotCommand(
|
||||||
|
"command_2", "description_2"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
self.empty_tuple_attrs = ()
|
||||||
|
self.empty_str_attribute = ""
|
||||||
|
# Should not be included in string representation
|
||||||
|
self.none_attr = None
|
||||||
|
|
||||||
|
expected_without_api_kwargs = (
|
||||||
|
"TGO(dict_attr={BotCommand(command='command_1', description='description_1'): "
|
||||||
|
"BotCommand(command='command_2', description='description_2')}, int_attr=42, "
|
||||||
|
"list_attr=[BotCommand(command='command_1', description='description_1'), "
|
||||||
|
"BotCommand(command='command_2', description='description_2')], "
|
||||||
|
"string_attr='string', to_attr=BotCommand(command='command', "
|
||||||
|
"description='description'))"
|
||||||
|
)
|
||||||
|
assert str(TGO()) == expected_without_api_kwargs
|
||||||
|
assert repr(TGO()) == expected_without_api_kwargs
|
||||||
|
|
||||||
|
expected_with_api_kwargs = (
|
||||||
|
"TGO(api_kwargs={'foo': 'bar'}, dict_attr={BotCommand(command='command_1', "
|
||||||
|
"description='description_1'): BotCommand(command='command_2', "
|
||||||
|
"description='description_2')}, int_attr=42, "
|
||||||
|
"list_attr=[BotCommand(command='command_1', description='description_1'), "
|
||||||
|
"BotCommand(command='command_2', description='description_2')], "
|
||||||
|
"string_attr='string', to_attr=BotCommand(command='command', "
|
||||||
|
"description='description'))"
|
||||||
|
)
|
||||||
|
assert str(TGO(api_kwargs={"foo": "bar"})) == expected_with_api_kwargs
|
||||||
|
assert repr(TGO(api_kwargs={"foo": "bar"})) == expected_with_api_kwargs
|
||||||
|
|
Loading…
Reference in a new issue