From 3f16c92eb32c342f9c5e31ea7b416e34490002e5 Mon Sep 17 00:00:00 2001 From: Lonami Exo Date: Tue, 12 Jun 2018 20:05:05 +0200 Subject: [PATCH] Subclass TLRequest for content-related objects --- telethon/client/users.py | 14 ++-- telethon/network/mtprotostate.py | 3 +- telethon/tl/__init__.py | 2 +- telethon/tl/core/gzippacked.py | 4 +- telethon/tl/core/messagecontainer.py | 3 - telethon/tl/tlobject.py | 38 +++++----- telethon_generator/generators/tlobject.py | 88 +++++++++++------------ 7 files changed, 71 insertions(+), 81 deletions(-) diff --git a/telethon/client/users.py b/telethon/client/users.py index 1d56d3e2..583a873b 100644 --- a/telethon/client/users.py +++ b/telethon/client/users.py @@ -3,17 +3,17 @@ import itertools from .telegrambaseclient import TelegramBaseClient from .. import errors, utils -from ..tl import TLObject, types, functions +from ..tl import TLObject, TLRequest, types, functions + + +_NOT_A_REQUEST = TypeError('You can only invoke requests, not types!') class UserMethods(TelegramBaseClient): async def __call__(self, request, retries=5, ordered=False): - requests = (request,) if not utils.is_list_like(request) else request - if not all(isinstance(x, TLObject) and - x.content_related for x in requests): - raise TypeError('You can only invoke requests, not types!') - - for r in requests: + for r in (request if utils.is_list_like(request) else (request,)): + if not isinstance(r, TLRequest): + raise _NOT_A_REQUEST await r.resolve(self, utils) for _ in range(retries): diff --git a/telethon/network/mtprotostate.py b/telethon/network/mtprotostate.py index bfcfa8fe..2eb64427 100644 --- a/telethon/network/mtprotostate.py +++ b/telethon/network/mtprotostate.py @@ -8,6 +8,7 @@ from ..crypto import AES from ..errors import SecurityError, BrokenAuthKeyError from ..extensions import BinaryReader from ..tl.core import TLMessage +from ..tl.tlobject import TLRequest __log__ = logging.getLogger(__name__) @@ -43,7 +44,7 @@ class MTProtoState: """ return TLMessage( msg_id=self._get_new_msg_id(), - seq_no=self._get_seq_no(obj.content_related), + seq_no=self._get_seq_no(isinstance(obj, TLRequest)), obj=obj, after_id=after.msg_id if after else None ) diff --git a/telethon/tl/__init__.py b/telethon/tl/__init__.py index b2ffbca8..e187537f 100644 --- a/telethon/tl/__init__.py +++ b/telethon/tl/__init__.py @@ -1 +1 @@ -from .tlobject import TLObject +from .tlobject import TLObject, TLRequest diff --git a/telethon/tl/core/gzippacked.py b/telethon/tl/core/gzippacked.py index 6ec61b49..7b146363 100644 --- a/telethon/tl/core/gzippacked.py +++ b/telethon/tl/core/gzippacked.py @@ -1,7 +1,7 @@ import gzip import struct -from .. import TLObject +from .. import TLObject, TLRequest class GzipPacked(TLObject): @@ -21,7 +21,7 @@ class GzipPacked(TLObject): """ data = bytes(request) # TODO This threshold could be configurable - if request.content_related and len(data) > 512: + if isinstance(request, TLRequest) and len(data) > 512: gzipped = bytes(GzipPacked(data)) return gzipped if len(gzipped) < len(data) else data else: diff --git a/telethon/tl/core/messagecontainer.py b/telethon/tl/core/messagecontainer.py index 0d56de33..bb033a91 100644 --- a/telethon/tl/core/messagecontainer.py +++ b/telethon/tl/core/messagecontainer.py @@ -11,13 +11,10 @@ class MessageContainer(TLObject): CONSTRUCTOR_ID = 0x73f1f8dc def __init__(self, messages): - super().__init__() - self.content_related = False self.messages = messages def to_dict(self, recursive=True): return { - 'content_related': self.content_related, 'messages': ([] if self.messages is None else [ None if x is None else x.to_dict() for x in self.messages diff --git a/telethon/tl/tlobject.py b/telethon/tl/tlobject.py index 337850d4..3eb24eb7 100644 --- a/telethon/tl/tlobject.py +++ b/telethon/tl/tlobject.py @@ -3,16 +3,11 @@ from datetime import datetime, date class TLObject: - def __init__(self): - # TODO Perhaps content_related makes more sense as another type? - # Something like class TLRequest(TLObject), request inherit this - self.content_related = False # Only requests/functions/queries are - - # These should not be overrode @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. + """ + 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): @@ -136,11 +131,6 @@ class TLObject: raise TypeError('Cannot interpret "{}" as a date.'.format(dt)) - # These are nearly always the same for all subclasses - @staticmethod - def read_result(reader): - return reader.tgread_object() - def __eq__(self, o): return isinstance(o, type(self)) and self.to_dict() == o.to_dict() @@ -153,16 +143,24 @@ class TLObject: def stringify(self): return TLObject.pretty_format(self, indent=0) - # These should be overrode - async def resolve(self, client, utils): - pass - def to_dict(self): - return {} + raise NotImplementedError def __bytes__(self): - return b'' + raise NotImplementedError @classmethod def from_reader(cls, reader): - return TLObject() + raise NotImplementedError + + +class TLRequest(TLObject): + """ + Represents a content-related `TLObject` (a request that can be sent). + """ + @staticmethod + def read_result(reader): + return reader.tgread_object() + + async def resolve(self, client, utils): + pass diff --git a/telethon_generator/generators/tlobject.py b/telethon_generator/generators/tlobject.py index 90820b81..c2c20ab8 100644 --- a/telethon_generator/generators/tlobject.py +++ b/telethon_generator/generators/tlobject.py @@ -32,7 +32,8 @@ BASE_TYPES = ('string', 'bytes', 'int', 'long', 'int128', 'int256', 'double', 'Bool', 'true', 'date') -def _write_modules(out_dir, depth, namespace_tlobjects, type_constructors): +def _write_modules( + out_dir, depth, kind, namespace_tlobjects, type_constructors): # namespace_tlobjects: {'namespace', [TLObject]} os.makedirs(out_dir, exist_ok=True) for ns, tlobjects in namespace_tlobjects.items(): @@ -41,7 +42,7 @@ def _write_modules(out_dir, depth, namespace_tlobjects, type_constructors): SourceBuilder(f) as builder: builder.writeln(AUTO_GEN_NOTICE) - builder.writeln('from {}.tl.tlobject import TLObject', '.' * depth) + builder.writeln('from {}.tl.tlobject import {}', '.' * depth, kind) builder.writeln('from typing import Optional, List, ' 'Union, TYPE_CHECKING') @@ -124,7 +125,7 @@ def _write_modules(out_dir, depth, namespace_tlobjects, type_constructors): # Generate the class for every TLObject for t in tlobjects: - _write_source_code(t, builder, type_constructors) + _write_source_code(t, kind, builder, type_constructors) builder.current_indent = 0 # Write the type definitions generated earlier. @@ -133,7 +134,7 @@ def _write_modules(out_dir, depth, namespace_tlobjects, type_constructors): builder.writeln(line) -def _write_source_code(tlobject, builder, type_constructors): +def _write_source_code(tlobject, kind, builder, type_constructors): """ Writes the source code corresponding to the given TLObject by making use of the ``builder`` `SourceBuilder`. @@ -142,7 +143,7 @@ def _write_source_code(tlobject, builder, type_constructors): the ``Type: [Constructors]`` must be given for proper importing and documentation strings. """ - _write_class_init(tlobject, type_constructors, builder) + _write_class_init(tlobject, kind, type_constructors, builder) _write_resolve(tlobject, builder) _write_to_dict(tlobject, builder) _write_to_bytes(tlobject, builder) @@ -150,10 +151,10 @@ def _write_source_code(tlobject, builder, type_constructors): _write_read_result(tlobject, builder) -def _write_class_init(tlobject, type_constructors, builder): +def _write_class_init(tlobject, kind, type_constructors, builder): builder.writeln() builder.writeln() - builder.writeln('class {}(TLObject):', tlobject.class_name) + builder.writeln('class {}({}):', tlobject.class_name, kind) # Class-level variable to store its Telegram's constructor ID builder.writeln('CONSTRUCTOR_ID = {:#x}', tlobject.id) @@ -165,46 +166,39 @@ def _write_class_init(tlobject, type_constructors, builder): args = [(a.name if not a.is_flag and not a.can_be_inferred else '{}=None'.format(a.name)) for a in tlobject.real_args] - # Write the __init__ function + # Write the __init__ function if it has any argument + if not tlobject.real_args: + return + builder.writeln('def __init__({}):', ', '.join(['self'] + args)) - if tlobject.real_args: - # Write the docstring, to know the type of the args - builder.writeln('"""') - for arg in tlobject.real_args: - if not arg.flag_indicator: - builder.writeln(':param {} {}:', arg.type_hint(), arg.name) - builder.current_indent -= 1 # It will auto-indent (':') + # Write the docstring, to know the type of the args + builder.writeln('"""') + for arg in tlobject.real_args: + if not arg.flag_indicator: + builder.writeln(':param {} {}:', arg.type_hint(), arg.name) + builder.current_indent -= 1 # It will auto-indent (':') - # We also want to know what type this request returns - # or to which type this constructor belongs to - builder.writeln() - if tlobject.is_function: - builder.write(':returns {}: ', tlobject.result) - else: - builder.write('Constructor for {}: ', tlobject.result) - - constructors = type_constructors[tlobject.result] - if not constructors: - builder.writeln('This type has no constructors.') - elif len(constructors) == 1: - builder.writeln('Instance of {}.', - constructors[0].class_name) - else: - builder.writeln('Instance of either {}.', ', '.join( - c.class_name for c in constructors)) - - builder.writeln('"""') - - builder.writeln('super().__init__()') - # Functions have a result object and are confirmed by default + # We also want to know what type this request returns + # or to which type this constructor belongs to + builder.writeln() if tlobject.is_function: - builder.writeln('self.result = None') - builder.writeln('self.content_related = True') + builder.write(':returns {}: ', tlobject.result) + else: + builder.write('Constructor for {}: ', tlobject.result) + + constructors = type_constructors[tlobject.result] + if not constructors: + builder.writeln('This type has no constructors.') + elif len(constructors) == 1: + builder.writeln('Instance of {}.', + constructors[0].class_name) + else: + builder.writeln('Instance of either {}.', ', '.join( + c.class_name for c in constructors)) + + builder.writeln('"""') # Set the arguments - if tlobject.real_args: - builder.writeln() - for arg in tlobject.real_args: if not arg.can_be_inferred: builder.writeln('self.{0} = {0} # type: {1}', @@ -453,7 +447,7 @@ def _write_arg_to_bytes(builder, arg, args, name=None): builder.write("struct.pack('