mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2024-11-25 10:53:44 +03:00
Make the TLGenerator class a lot more readable
This commit is contained in:
parent
68a625b82b
commit
769692959f
|
@ -110,223 +110,14 @@ class TLGenerator:
|
|||
TLGenerator.get_class_name(tlobject)))
|
||||
|
||||
# Create the file for this TLObject
|
||||
filename = os.path.join(
|
||||
out_dir,
|
||||
TLGenerator.get_file_name(
|
||||
tlobject, add_extension=True))
|
||||
filename = os.path.join(out_dir, TLGenerator.get_file_name(
|
||||
tlobject, add_extension=True
|
||||
))
|
||||
|
||||
with open(filename, 'w', encoding='utf-8') as file:
|
||||
# Let's build the source code!
|
||||
with SourceBuilder(file) as builder:
|
||||
# Both types and functions inherit from
|
||||
# MTProtoRequest so they all can be sent
|
||||
builder.writeln('from {}.tl.mtproto_request import MTProtoRequest'
|
||||
.format('.' * depth))
|
||||
|
||||
if any(a for a in tlobject.args if a.can_be_inferred):
|
||||
# Currently only 'random_id' needs 'os' to be imported
|
||||
builder.writeln('import os')
|
||||
|
||||
builder.writeln()
|
||||
builder.writeln()
|
||||
builder.writeln('class {}(MTProtoRequest):'.format(
|
||||
TLGenerator.get_class_name(tlobject)))
|
||||
|
||||
# Write the original .tl definition,
|
||||
# along with a "generated automatically" message
|
||||
builder.writeln(
|
||||
'"""Class generated by TLObjects\' generator. '
|
||||
'All changes will be ERASED. TL definition below.')
|
||||
builder.writeln('{}"""'.format(repr(tlobject)))
|
||||
builder.writeln()
|
||||
|
||||
# Class-level variable to store its constructor ID
|
||||
builder.writeln(
|
||||
"# Telegram's constructor (U)ID for this class")
|
||||
builder.writeln('constructor_id = {}'.format(
|
||||
hex(tlobject.id)))
|
||||
builder.writeln()
|
||||
|
||||
# Flag arguments must go last
|
||||
args = [
|
||||
a for a in tlobject.sorted_args()
|
||||
if not a.flag_indicator and not a.generic_definition
|
||||
]
|
||||
|
||||
# Convert the args to string parameters, flags having =None
|
||||
args = [
|
||||
(a.name if not a.is_flag and not a.can_be_inferred
|
||||
else '{}=None'.format(a.name))
|
||||
for a in args
|
||||
]
|
||||
|
||||
# Write the __init__ function
|
||||
if args:
|
||||
builder.writeln('def __init__(self, {}):'.format(
|
||||
', '.join(args)))
|
||||
else:
|
||||
builder.writeln('def __init__(self):')
|
||||
|
||||
# Now update args to have the TLObject arguments, _except_
|
||||
# those which are calculated on send or ignored, this is
|
||||
# flag indicator and generic definitions.
|
||||
#
|
||||
# We don't need the generic definitions in Python
|
||||
# because arguments can be any type
|
||||
args = [arg for arg in tlobject.args
|
||||
if not arg.flag_indicator and
|
||||
not arg.generic_definition]
|
||||
|
||||
if args:
|
||||
# Write the docstring, to know the type of the args
|
||||
builder.writeln('"""')
|
||||
for arg in args:
|
||||
if not arg.flag_indicator:
|
||||
builder.write(
|
||||
':param {}: Telegram type: "{}".'
|
||||
.format(arg.name, arg.type)
|
||||
)
|
||||
if arg.is_vector:
|
||||
builder.write(
|
||||
' Must be a list.'.format(arg.name)
|
||||
)
|
||||
if arg.is_generic:
|
||||
builder.write(
|
||||
' Must be another MTProtoRequest.'
|
||||
)
|
||||
builder.writeln()
|
||||
|
||||
# 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 %s: ' % tlobject.result)
|
||||
else:
|
||||
builder.write('Constructor for %s: ' % 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 {}.'.format(
|
||||
TLGenerator.get_class_name(constructors[0])
|
||||
))
|
||||
else:
|
||||
builder.writeln('Instance of either {}.'.format(
|
||||
', '.join(TLGenerator.get_class_name(c)
|
||||
for c in constructors)
|
||||
))
|
||||
|
||||
builder.writeln('"""')
|
||||
|
||||
builder.writeln('super().__init__()')
|
||||
# Functions have a result object and are confirmed by default
|
||||
if tlobject.is_function:
|
||||
builder.writeln('self.result = None')
|
||||
builder.writeln(
|
||||
'self.confirmed = True # Confirmed by default')
|
||||
|
||||
# Set the arguments
|
||||
if args:
|
||||
# Leave an empty line if there are any args
|
||||
builder.writeln()
|
||||
|
||||
for arg in args:
|
||||
if arg.can_be_inferred:
|
||||
# Currently the only argument that can be
|
||||
# inferred are those called 'random_id'
|
||||
if arg.name == 'random_id':
|
||||
builder.writeln(
|
||||
"self.random_id = random_id if random_id "
|
||||
"is not None else int.from_bytes("
|
||||
"os.urandom({}), signed=True, "
|
||||
"byteorder='little')"
|
||||
.format(8 if arg.type == 'long' else 4)
|
||||
)
|
||||
else:
|
||||
raise ValueError(
|
||||
'Cannot infer a value for ', arg)
|
||||
else:
|
||||
builder.writeln('self.{0} = {0}'
|
||||
.format(arg.name))
|
||||
|
||||
builder.end_block()
|
||||
|
||||
# Write the to_dict(self) method
|
||||
builder.writeln('def to_dict(self):')
|
||||
if args:
|
||||
builder.writeln('return {')
|
||||
|
||||
base_types = ['string', 'bytes', 'int', 'long',
|
||||
'int128', 'int256', 'double', 'Bool',
|
||||
'true', 'date']
|
||||
|
||||
for arg in args:
|
||||
builder.writeln("'{}': ".format(arg.name))
|
||||
if arg.is_vector:
|
||||
builder.writeln('[x{} for x in self.{}] if self.{} is not None else [],'
|
||||
.format('.to_dict() if x is not None else None'
|
||||
if arg.type not in base_types else '',
|
||||
arg.name, arg.name))
|
||||
else:
|
||||
builder.writeln(
|
||||
'self.{}{},'.format(
|
||||
arg.name,
|
||||
'.to_dict() if self.{} is not None else None'
|
||||
.format(arg.name) if arg.type not in base_types else ''))
|
||||
builder.write("}")
|
||||
else:
|
||||
builder.writeln('return {}')
|
||||
builder.writeln()
|
||||
builder.end_block()
|
||||
|
||||
# Write the on_send(self, writer) function
|
||||
builder.writeln('def on_send(self, writer):')
|
||||
builder.writeln(
|
||||
'writer.write_int({}.constructor_id, signed=False)'
|
||||
.format(TLGenerator.get_class_name(tlobject)))
|
||||
|
||||
for arg in tlobject.args:
|
||||
TLGenerator.write_onsend_code(builder, arg,
|
||||
tlobject.args)
|
||||
builder.end_block()
|
||||
|
||||
# Write the empty() function, which returns an "empty"
|
||||
# instance, in which all attributes are set to None
|
||||
builder.writeln('@staticmethod')
|
||||
builder.writeln('def empty():')
|
||||
builder.writeln(
|
||||
'"""Returns an "empty" instance (attributes=None)"""')
|
||||
builder.writeln('return {}({})'.format(
|
||||
TLGenerator.get_class_name(tlobject), ', '.join(
|
||||
'None' for _ in range(len(args)))))
|
||||
builder.end_block()
|
||||
|
||||
# Write the on_response(self, reader) function
|
||||
builder.writeln('def on_response(self, reader):')
|
||||
# Do not read constructor's ID, since
|
||||
# that's already been read somewhere else
|
||||
if tlobject.is_function:
|
||||
TLGenerator.write_request_result_code(builder, tlobject)
|
||||
else:
|
||||
if tlobject.args:
|
||||
for arg in tlobject.args:
|
||||
TLGenerator.write_onresponse_code(
|
||||
builder, arg, tlobject.args)
|
||||
else:
|
||||
# If there were no arguments, we still need an
|
||||
# on_response method, and hence "pass" if empty
|
||||
builder.writeln('pass')
|
||||
builder.end_block()
|
||||
|
||||
# Write the __repr__(self) and __str__(self) functions
|
||||
builder.writeln('def __repr__(self):')
|
||||
builder.writeln("return '{}'".format(repr(tlobject)))
|
||||
builder.end_block()
|
||||
|
||||
builder.writeln('def __str__(self):')
|
||||
builder.writeln('return {}'.format(str(tlobject)))
|
||||
# builder.end_block() # No need to end the last block
|
||||
TLGenerator._write_source_code(
|
||||
tlobject, builder, depth, type_constructors)
|
||||
|
||||
# Step 3: Add the relative imports to the namespaces on __init__.py's
|
||||
init_py = os.path.join(get_output_path('functions'), '__init__.py')
|
||||
|
@ -380,6 +171,236 @@ class TLGenerator:
|
|||
builder.current_indent -= 1
|
||||
builder.writeln('}')
|
||||
|
||||
@staticmethod
|
||||
def _write_source_code(tlobject, builder, depth, type_constructors):
|
||||
"""Writes the source code corresponding to the given TLObject
|
||||
by making use of the 'builder' SourceBuilder.
|
||||
|
||||
Additional information such as file path depth and
|
||||
the Type: [Constructors] must be given for proper
|
||||
importing and documentation strings.
|
||||
'"""
|
||||
|
||||
# Both types and functions inherit from
|
||||
# MTProtoRequest so they all can be sent
|
||||
builder.writeln('from {}.tl.mtproto_request import MTProtoRequest'
|
||||
.format('.' * depth))
|
||||
|
||||
if any(a for a in tlobject.args if a.can_be_inferred):
|
||||
# Currently only 'random_id' needs 'os' to be imported
|
||||
builder.writeln('import os')
|
||||
|
||||
builder.writeln()
|
||||
builder.writeln()
|
||||
builder.writeln('class {}(MTProtoRequest):'.format(
|
||||
TLGenerator.get_class_name(tlobject)))
|
||||
|
||||
# Write the original .tl definition,
|
||||
# along with a "generated automatically" message
|
||||
builder.writeln(
|
||||
'"""Class generated by TLObjects\' generator. '
|
||||
'All changes will be ERASED. TL definition below.'
|
||||
)
|
||||
builder.writeln('{}"""'.format(repr(tlobject)))
|
||||
builder.writeln()
|
||||
|
||||
# Class-level variable to store its constructor ID
|
||||
builder.writeln("# Telegram's constructor (U)ID for this class")
|
||||
builder.writeln('constructor_id = {}'.format(hex(tlobject.id)))
|
||||
builder.writeln()
|
||||
|
||||
# Flag arguments must go last
|
||||
args = [
|
||||
a for a in tlobject.sorted_args()
|
||||
if not a.flag_indicator and not a.generic_definition
|
||||
]
|
||||
|
||||
# Convert the args to string parameters, flags having =None
|
||||
args = [
|
||||
(a.name if not a.is_flag and not a.can_be_inferred
|
||||
else '{}=None'.format(a.name))
|
||||
for a in args
|
||||
]
|
||||
|
||||
# Write the __init__ function
|
||||
if args:
|
||||
builder.writeln(
|
||||
'def __init__(self, {}):'.format(', '.join(args))
|
||||
)
|
||||
else:
|
||||
builder.writeln('def __init__(self):')
|
||||
|
||||
# Now update args to have the TLObject arguments, _except_
|
||||
# those which are calculated on send or ignored, this is
|
||||
# flag indicator and generic definitions.
|
||||
#
|
||||
# We don't need the generic definitions in Python
|
||||
# because arguments can be any type
|
||||
args = [arg for arg in tlobject.args
|
||||
if not arg.flag_indicator and
|
||||
not arg.generic_definition]
|
||||
|
||||
if args:
|
||||
# Write the docstring, to know the type of the args
|
||||
builder.writeln('"""')
|
||||
for arg in args:
|
||||
if not arg.flag_indicator:
|
||||
builder.write(
|
||||
':param {}: Telegram type: "{}".'
|
||||
.format(arg.name, arg.type)
|
||||
)
|
||||
if arg.is_vector:
|
||||
builder.write(' Must be a list.'.format(arg.name))
|
||||
|
||||
if arg.is_generic:
|
||||
builder.write(' Must be another MTProtoRequest.')
|
||||
|
||||
builder.writeln()
|
||||
|
||||
# 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 {}: '.format(tlobject.result))
|
||||
else:
|
||||
builder.write('Constructor for {}: '.format(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 {}.'.format(
|
||||
TLGenerator.get_class_name(constructors[0])
|
||||
))
|
||||
else:
|
||||
builder.writeln('Instance of either {}.'.format(
|
||||
', '.join(TLGenerator.get_class_name(c)
|
||||
for c in constructors)
|
||||
))
|
||||
|
||||
builder.writeln('"""')
|
||||
|
||||
builder.writeln('super().__init__()')
|
||||
# Functions have a result object and are confirmed by default
|
||||
if tlobject.is_function:
|
||||
builder.writeln('self.result = None')
|
||||
builder.writeln(
|
||||
'self.confirmed = True # Confirmed by default')
|
||||
|
||||
# Set the arguments
|
||||
if args:
|
||||
# Leave an empty line if there are any args
|
||||
builder.writeln()
|
||||
|
||||
for arg in args:
|
||||
if arg.can_be_inferred:
|
||||
# Currently the only argument that can be
|
||||
# inferred are those called 'random_id'
|
||||
if arg.name == 'random_id':
|
||||
builder.writeln(
|
||||
"self.random_id = random_id if random_id "
|
||||
"is not None else int.from_bytes("
|
||||
"os.urandom({}), signed=True, "
|
||||
"byteorder='little')"
|
||||
.format(8 if arg.type == 'long' else 4)
|
||||
)
|
||||
else:
|
||||
raise ValueError('Cannot infer a value for ', arg)
|
||||
else:
|
||||
builder.writeln('self.{0} = {0}'.format(arg.name))
|
||||
|
||||
builder.end_block()
|
||||
|
||||
# Write the to_dict(self) method
|
||||
builder.writeln('def to_dict(self):')
|
||||
if args:
|
||||
builder.writeln('return {')
|
||||
builder.current_indent += 1
|
||||
|
||||
base_types = ('string', 'bytes', 'int', 'long', 'int128',
|
||||
'int256', 'double', 'Bool', 'true', 'date')
|
||||
|
||||
for arg in args:
|
||||
builder.write("'{}': ".format(arg.name))
|
||||
if arg.type in base_types:
|
||||
if arg.is_vector:
|
||||
builder.write(
|
||||
'[] if self.{0} is None else self.{0}[:]'
|
||||
.format(arg.name)
|
||||
)
|
||||
else:
|
||||
builder.write('self.{}'.format(arg.name))
|
||||
else:
|
||||
if arg.is_vector:
|
||||
builder.write(
|
||||
'[] if self.{0} is None else [None '
|
||||
'if x is None else x.to_dict() for x in self.{0}]'
|
||||
.format(arg.name)
|
||||
)
|
||||
else:
|
||||
builder.write(
|
||||
'None if self.{0} is None else self.{0}.to_dict()'
|
||||
.format(arg.name)
|
||||
)
|
||||
builder.writeln(',')
|
||||
|
||||
builder.current_indent -= 1
|
||||
builder.write("}")
|
||||
else:
|
||||
builder.writeln('return {}')
|
||||
|
||||
builder.writeln()
|
||||
builder.end_block()
|
||||
|
||||
# Write the on_send(self, writer) function
|
||||
builder.writeln('def on_send(self, writer):')
|
||||
builder.writeln(
|
||||
'writer.write_int({}.constructor_id, signed=False)'
|
||||
.format(TLGenerator.get_class_name(tlobject)))
|
||||
|
||||
for arg in tlobject.args:
|
||||
TLGenerator.write_onsend_code(builder, arg,
|
||||
tlobject.args)
|
||||
builder.end_block()
|
||||
|
||||
# Write the empty() function, which returns an "empty"
|
||||
# instance, in which all attributes are set to None
|
||||
builder.writeln('@staticmethod')
|
||||
builder.writeln('def empty():')
|
||||
builder.writeln(
|
||||
'"""Returns an "empty" instance (attributes=None)"""')
|
||||
builder.writeln('return {}({})'.format(
|
||||
TLGenerator.get_class_name(tlobject), ', '.join(
|
||||
'None' for _ in range(len(args)))))
|
||||
builder.end_block()
|
||||
|
||||
# Write the on_response(self, reader) function
|
||||
builder.writeln('def on_response(self, reader):')
|
||||
# Do not read constructor's ID, since
|
||||
# that's already been read somewhere else
|
||||
if tlobject.is_function:
|
||||
TLGenerator.write_request_result_code(builder, tlobject)
|
||||
else:
|
||||
if tlobject.args:
|
||||
for arg in tlobject.args:
|
||||
TLGenerator.write_onresponse_code(
|
||||
builder, arg, tlobject.args)
|
||||
else:
|
||||
# If there were no arguments, we still need an
|
||||
# on_response method, and hence "pass" if empty
|
||||
builder.writeln('pass')
|
||||
builder.end_block()
|
||||
|
||||
# Write the __repr__(self) and __str__(self) functions
|
||||
builder.writeln('def __repr__(self):')
|
||||
builder.writeln("return '{}'".format(repr(tlobject)))
|
||||
builder.end_block()
|
||||
|
||||
builder.writeln('def __str__(self):')
|
||||
builder.writeln('return {}'.format(str(tlobject)))
|
||||
# builder.end_block() # No need to end the last block
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get_class_name(tlobject):
|
||||
"""Gets the class name following the Python style guidelines, in ThisClassFormat"""
|
||||
|
|
Loading…
Reference in New Issue
Block a user