From 1c9fa76edeedc6d17325216cab9c574a23ca0caa Mon Sep 17 00:00:00 2001 From: Lonami Exo Date: Fri, 19 Jan 2018 11:47:45 +0100 Subject: [PATCH] Add new method to .resolve() parameters instead on init TLObject's __init__ used to call utils.get_input_* methods and similar to auto-cast things like User into InputPeerUser as required. Now there's a custom .resolve() method for this purpose with several advantages: - Old behaviour still works, autocasts work like usual. - A request can be constructed and later modified, before the autocast only occured on the constructor but now while invoking. - This allows us to not only use the utils module but also the client, so it's even possible to use usernames or phone numbers for things that require an InputPeer. This actually assumes the TelegramClient subclass is being used and not the bare version which would fail when calling .get_input_peer(). --- telethon/telegram_bare_client.py | 5 +- telethon/tl/tlobject.py | 3 + telethon_generator/tl_generator.py | 104 +++++++++++++++-------------- 3 files changed, 60 insertions(+), 52 deletions(-) diff --git a/telethon/telegram_bare_client.py b/telethon/telegram_bare_client.py index 9684a034..ba6ae374 100644 --- a/telethon/telegram_bare_client.py +++ b/telethon/telegram_bare_client.py @@ -7,7 +7,7 @@ from signal import signal, SIGINT, SIGTERM, SIGABRT from threading import Lock from time import sleep -from . import version +from . import version, utils from .crypto import rsa from .errors import ( RPCError, BrokenAuthKeyError, ServerError, FloodWaitError, @@ -420,6 +420,9 @@ class TelegramBareClient: if self._background_error: raise self._background_error + for request in requests: + request.resolve(self, utils) + # For logging purposes if len(requests) == 1: which = type(requests[0]).__name__ diff --git a/telethon/tl/tlobject.py b/telethon/tl/tlobject.py index ad930f9c..7c86a24a 100644 --- a/telethon/tl/tlobject.py +++ b/telethon/tl/tlobject.py @@ -144,6 +144,9 @@ class TLObject: raise TypeError('Cannot interpret "{}" as a date.'.format(dt)) # These should be overrode + def resolve(self, client, utils): + pass + def to_dict(self, recursive=True): return {} diff --git a/telethon_generator/tl_generator.py b/telethon_generator/tl_generator.py index 3116003a..39bad15f 100644 --- a/telethon_generator/tl_generator.py +++ b/telethon_generator/tl_generator.py @@ -10,6 +10,15 @@ AUTO_GEN_NOTICE = \ '"""File generated by TLObjects\' generator. All changes will be ERASED"""' +AUTO_CASTS = { + 'InputPeer': 'utils.get_input_peer(client.get_input_entity({}))', + 'InputChannel': 'utils.get_input_channel(client.get_input_entity({}))', + 'InputUser': 'utils.get_input_user(client.get_input_entity({}))', + 'InputMedia': 'utils.get_input_media({})', + 'InputPhoto': 'utils.get_input_photo({})' +} + + class TLGenerator: def __init__(self, output_dir): self.output_dir = output_dir @@ -257,10 +266,45 @@ class TLGenerator: builder.writeln() for arg in args: - TLGenerator._write_self_assigns(builder, tlobject, arg, args) + if not arg.can_be_inferred: + builder.writeln('self.{0} = {0}'.format(arg.name)) + continue + + # Currently the only argument that can be + # inferred are those called 'random_id' + if arg.name == 'random_id': + # Endianness doesn't really matter, and 'big' is shorter + code = "int.from_bytes(os.urandom({}), 'big', signed=True)" \ + .format(8 if arg.type == 'long' else 4) + + if arg.is_vector: + # Currently for the case of "messages.forwardMessages" + # Ensure we can infer the length from id:Vector<> + if not next( + a for a in args if a.name == 'id').is_vector: + raise ValueError( + 'Cannot infer list of random ids for ', tlobject + ) + code = '[{} for _ in range(len(id))]'.format(code) + + builder.writeln( + "self.random_id = random_id if random_id " + "is not None else {}".format(code) + ) + else: + raise ValueError('Cannot infer a value for ', arg) builder.end_block() + # Write the resolve(self, client, utils) method + if any(arg.type in AUTO_CASTS for arg in args): + builder.writeln('def resolve(self, client, utils):') + for arg in args: + ac = AUTO_CASTS.get(arg.type, None) + if ac: + TLGenerator._write_self_assign(builder, arg, ac) + builder.end_block() + # Write the to_dict(self) method builder.writeln('def to_dict(self, recursive=True):') if args: @@ -370,59 +414,17 @@ class TLGenerator: # builder.end_block() # No need to end the last block @staticmethod - def _write_self_assigns(builder, tlobject, arg, args): - if arg.can_be_inferred: - # Currently the only argument that can be - # inferred are those called 'random_id' - if arg.name == 'random_id': - # Endianness doesn't really matter, and 'big' is shorter - code = "int.from_bytes(os.urandom({}), 'big', signed=True)"\ - .format(8 if arg.type == 'long' else 4) - - if arg.is_vector: - # Currently for the case of "messages.forwardMessages" - # Ensure we can infer the length from id:Vector<> - if not next(a for a in args if a.name == 'id').is_vector: - raise ValueError( - 'Cannot infer list of random ids for ', tlobject - ) - code = '[{} for _ in range(len(id))]'.format(code) - - builder.writeln( - "self.random_id = random_id if random_id " - "is not None else {}".format(code) - ) - else: - raise ValueError('Cannot infer a value for ', arg) - - # Well-known cases, auto-cast it to the right type - elif arg.type == 'InputPeer' and tlobject.is_function: - TLGenerator.write_get_input(builder, arg, 'get_input_peer') - elif arg.type == 'InputChannel' and tlobject.is_function: - TLGenerator.write_get_input(builder, arg, 'get_input_channel') - elif arg.type == 'InputUser' and tlobject.is_function: - TLGenerator.write_get_input(builder, arg, 'get_input_user') - elif arg.type == 'InputMedia' and tlobject.is_function: - TLGenerator.write_get_input(builder, arg, 'get_input_media') - elif arg.type == 'InputPhoto' and tlobject.is_function: - TLGenerator.write_get_input(builder, arg, 'get_input_photo') - - else: - builder.writeln('self.{0} = {0}'.format(arg.name)) - - @staticmethod - def write_get_input(builder, arg, get_input_code): - """Returns "True" if the get_input_* code was written when assigning - a parameter upon creating the request. Returns False otherwise - """ + def _write_self_assign(builder, arg, get_input_code): + """Writes self.arg = input.format(self.arg), considering vectors""" if arg.is_vector: - builder.write('self.{0} = [{1}(_x) for _x in {0}]' - .format(arg.name, get_input_code)) + builder.write('self.{0} = [{1} for _x in self.{0}]' + .format(arg.name, get_input_code.format('_x'))) else: - builder.write('self.{0} = {1}({0})' - .format(arg.name, get_input_code)) + builder.write('self.{} = {}'.format( + arg.name, get_input_code.format('self.' + arg.name))) + builder.writeln( - ' if {} else None'.format(arg.name) if arg.is_flag else '' + ' if self.{} else None'.format(arg.name) if arg.is_flag else '' ) @staticmethod