Add stringify back to custom Message

This commit is contained in:
Lonami Exo 2021-10-16 11:53:29 +02:00
parent 8de375323e
commit b566e59036
4 changed files with 152 additions and 70 deletions

View File

@ -526,6 +526,39 @@ If you still want the old behaviour, wrap the list inside another list:
#+ #+
Changes to the string and to_dict representation
------------------------------------------------
The string representation of raw API objects will now have its "printing depth" limited, meaning
very large and nested objects will be easier to read.
If you want to see the full object's representation, you should instead use Python's builtin
``repr`` method.
The ``.stringify`` method remains unchanged.
Here's a comparison table for a convenient overview:
+-------------------+---------------------------------------------+---------------------------------------------+
| | Telethon v1.x | Telethon v2.x |
+-------------------+-------------+--------------+----------------+-------------+--------------+----------------+
| | ``__str__`` | ``__repr__`` | ``.stringify`` | ``__str__`` | ``__repr__`` | ``.stringify`` |
+-------------------+-------------+--------------+----------------+-------------+--------------+----------------+
| Useful? | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ |
+-------------------+-------------+--------------+----------------+-------------+--------------+----------------+
| Multiline? | ❌ | ❌ | ✅ | ❌ | ❌ | ✅ |
+-------------------+-------------+--------------+----------------+-------------+--------------+----------------+
| Shows everything? | ✅ | ❌ | ✅ | ❌ | ✅ | ✅ |
+-------------------+-------------+--------------+----------------+-------------+--------------+----------------+
Both of the string representations may still change in the future without warning, as Telegram
adds, changes or removes fields. It should only be used for debugging. If you need a persistent
string representation, it is your job to decide which fields you care about and their format.
The ``Message`` representation now contains different properties, which should be more useful and
less confusing.
Changes on how to configure a different connection mode Changes on how to configure a different connection mode
------------------------------------------------------- -------------------------------------------------------

View File

@ -200,6 +200,73 @@ def _entity_type(entity):
# 'Empty' in name or not found, we don't care, not a valid entity. # 'Empty' in name or not found, we don't care, not a valid entity.
raise TypeError('{} does not have any entity type'.format(entity)) raise TypeError('{} does not have any entity type'.format(entity))
def pretty_print(obj, indent=None, max_depth=float('inf')):
max_depth -= 1
if max_depth < 0:
return '...'
to_d = getattr(obj, '_to_dict', None) or getattr(obj, 'to_dict', None)
if callable(to_d):
obj = to_d()
if indent is None:
if isinstance(obj, dict):
return '{}({})'.format(obj.get('_', 'dict'), ', '.join(
'{}={}'.format(k, pretty_print(v, indent, max_depth))
for k, v in obj.items() if k != '_'
))
elif isinstance(obj, str) or isinstance(obj, bytes):
return repr(obj)
elif hasattr(obj, '__iter__'):
return '[{}]'.format(
', '.join(pretty_print(x, indent, max_depth) for x in obj)
)
else:
return repr(obj)
else:
result = []
if isinstance(obj, dict):
result.append(obj.get('_', 'dict'))
result.append('(')
if obj:
result.append('\n')
indent += 1
for k, v in obj.items():
if k == '_':
continue
result.append('\t' * indent)
result.append(k)
result.append('=')
result.append(pretty_print(v, indent, max_depth))
result.append(',\n')
result.pop() # last ',\n'
indent -= 1
result.append('\n')
result.append('\t' * indent)
result.append(')')
elif isinstance(obj, str) or isinstance(obj, bytes):
result.append(repr(obj))
elif hasattr(obj, '__iter__'):
result.append('[\n')
indent += 1
for x in obj:
result.append('\t' * indent)
result.append(pretty_print(x, indent, max_depth))
result.append(',\n')
indent -= 1
result.append('\t' * indent)
result.append(']')
else:
result.append(repr(obj))
return ''.join(result)
# endregion # endregion
# region Cryptographic related utils # region Cryptographic related utils

View File

@ -3,6 +3,7 @@ import json
import struct import struct
from datetime import datetime, date, timedelta, timezone from datetime import datetime, date, timedelta, timezone
import time import time
from .helpers import pretty_print
_EPOCH_NAIVE = datetime(*time.gmtime(0)[:6]) _EPOCH_NAIVE = datetime(*time.gmtime(0)[:6])
_EPOCH_NAIVE_LOCAL = datetime(*time.localtime(0)[:6]) _EPOCH_NAIVE_LOCAL = datetime(*time.localtime(0)[:6])
@ -36,73 +37,6 @@ class TLObject:
CONSTRUCTOR_ID = None CONSTRUCTOR_ID = None
SUBCLASS_OF_ID = None SUBCLASS_OF_ID = None
@staticmethod
def pretty_format(obj, indent=None):
"""
Pretty formats the given object as a string which is returned.
If indent is None, a single line will be returned.
"""
if indent is None:
if isinstance(obj, TLObject):
obj = obj.to_dict()
if isinstance(obj, dict):
return '{}({})'.format(obj.get('_', 'dict'), ', '.join(
'{}={}'.format(k, TLObject.pretty_format(v))
for k, v in obj.items() if k != '_'
))
elif isinstance(obj, str) or isinstance(obj, bytes):
return repr(obj)
elif hasattr(obj, '__iter__'):
return '[{}]'.format(
', '.join(TLObject.pretty_format(x) for x in obj)
)
else:
return repr(obj)
else:
result = []
if isinstance(obj, TLObject):
obj = obj.to_dict()
if isinstance(obj, dict):
result.append(obj.get('_', 'dict'))
result.append('(')
if obj:
result.append('\n')
indent += 1
for k, v in obj.items():
if k == '_':
continue
result.append('\t' * indent)
result.append(k)
result.append('=')
result.append(TLObject.pretty_format(v, indent))
result.append(',\n')
result.pop() # last ',\n'
indent -= 1
result.append('\n')
result.append('\t' * indent)
result.append(')')
elif isinstance(obj, str) or isinstance(obj, bytes):
result.append(repr(obj))
elif hasattr(obj, '__iter__'):
result.append('[\n')
indent += 1
for x in obj:
result.append('\t' * indent)
result.append(TLObject.pretty_format(x, indent))
result.append(',\n')
indent -= 1
result.append('\t' * indent)
result.append(']')
else:
result.append(repr(obj))
return ''.join(result)
@staticmethod @staticmethod
def serialize_bytes(data): def serialize_bytes(data):
"""Write bytes by using Telegram guidelines""" """Write bytes by using Telegram guidelines"""
@ -164,11 +98,14 @@ class TLObject:
def __ne__(self, o): def __ne__(self, o):
return not isinstance(o, type(self)) or self.to_dict() != o.to_dict() return not isinstance(o, type(self)) or self.to_dict() != o.to_dict()
def __repr__(self):
return pretty_print(self)
def __str__(self): def __str__(self):
return TLObject.pretty_format(self) return pretty_print(self, max_depth=2)
def stringify(self): def stringify(self):
return TLObject.pretty_format(self, indent=0) return pretty_print(self, indent=0)
def to_dict(self): def to_dict(self):
res = {} res = {}

View File

@ -9,7 +9,7 @@ from .file import File
from .inputfile import InputFile from .inputfile import InputFile
from .inputmessage import InputMessage from .inputmessage import InputMessage
from .button import build_reply_markup from .button import build_reply_markup
from ..._misc import utils, tlobject from ..._misc import utils, helpers, tlobject
from ... import _tl, _misc from ... import _tl, _misc
@ -1366,5 +1366,50 @@ class Message(ChatGetter, SenderGetter):
# endregion Private Methods # endregion Private Methods
def to_dict(self):
return self._message.to_dict()
def _to_dict(self):
return {
'_': 'Message',
'id': self.id,
'out': self.out,
'date': self.date,
'text': self.text,
'sender': self.sender,
'chat': self.chat,
'mentioned': self.mentioned,
'media_unread': self.media_unread,
'silent': self.silent,
'post': self.post,
'from_scheduled': self.from_scheduled,
'legacy': self.legacy,
'edit_hide': self.edit_hide,
'pinned': self.pinned,
'forward': self.forward,
'via_bot': self.via_bot,
'reply_to': self.reply_to,
'reply_markup': self.reply_markup,
'views': self.views,
'forwards': self.forwards,
'replies': self.replies,
'edit_date': self.edit_date,
'post_author': self.post_author,
'grouped_id': self.grouped_id,
'ttl_period': self.ttl_period,
'action': self.action,
'media': self.media,
'action_entities': self.action_entities,
}
def __repr__(self):
return helpers.pretty_print(self)
def __str__(self):
return helpers.pretty_print(self, max_depth=2)
def stringify(self):
return helpers.pretty_print(self, indent=0)
# TODO set md by default if commonmark is installed else nothing # TODO set md by default if commonmark is installed else nothing