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().
This commit is contained in:
Lonami Exo 2018-01-19 11:47:45 +01:00
parent 7c55d42287
commit 1c9fa76ede
3 changed files with 60 additions and 52 deletions

View File

@ -7,7 +7,7 @@ from signal import signal, SIGINT, SIGTERM, SIGABRT
from threading import Lock from threading import Lock
from time import sleep from time import sleep
from . import version from . import version, utils
from .crypto import rsa from .crypto import rsa
from .errors import ( from .errors import (
RPCError, BrokenAuthKeyError, ServerError, FloodWaitError, RPCError, BrokenAuthKeyError, ServerError, FloodWaitError,
@ -420,6 +420,9 @@ class TelegramBareClient:
if self._background_error: if self._background_error:
raise self._background_error raise self._background_error
for request in requests:
request.resolve(self, utils)
# For logging purposes # For logging purposes
if len(requests) == 1: if len(requests) == 1:
which = type(requests[0]).__name__ which = type(requests[0]).__name__

View File

@ -144,6 +144,9 @@ class TLObject:
raise TypeError('Cannot interpret "{}" as a date.'.format(dt)) raise TypeError('Cannot interpret "{}" as a date.'.format(dt))
# These should be overrode # These should be overrode
def resolve(self, client, utils):
pass
def to_dict(self, recursive=True): def to_dict(self, recursive=True):
return {} return {}

View File

@ -10,6 +10,15 @@ AUTO_GEN_NOTICE = \
'"""File generated by TLObjects\' generator. All changes will be ERASED"""' '"""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: class TLGenerator:
def __init__(self, output_dir): def __init__(self, output_dir):
self.output_dir = output_dir self.output_dir = output_dir
@ -257,10 +266,45 @@ class TLGenerator:
builder.writeln() builder.writeln()
for arg in args: 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() 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 # Write the to_dict(self) method
builder.writeln('def to_dict(self, recursive=True):') builder.writeln('def to_dict(self, recursive=True):')
if args: if args:
@ -370,59 +414,17 @@ class TLGenerator:
# builder.end_block() # No need to end the last block # builder.end_block() # No need to end the last block
@staticmethod @staticmethod
def _write_self_assigns(builder, tlobject, arg, args): def _write_self_assign(builder, arg, get_input_code):
if arg.can_be_inferred: """Writes self.arg = input.format(self.arg), considering vectors"""
# 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
"""
if arg.is_vector: if arg.is_vector:
builder.write('self.{0} = [{1}(_x) for _x in {0}]' builder.write('self.{0} = [{1} for _x in self.{0}]'
.format(arg.name, get_input_code)) .format(arg.name, get_input_code.format('_x')))
else: else:
builder.write('self.{0} = {1}({0})' builder.write('self.{} = {}'.format(
.format(arg.name, get_input_code)) arg.name, get_input_code.format('self.' + arg.name)))
builder.writeln( 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 @staticmethod