mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2024-11-25 19:03:46 +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)))
|
TLGenerator.get_class_name(tlobject)))
|
||||||
|
|
||||||
# Create the file for this TLObject
|
# Create the file for this TLObject
|
||||||
filename = os.path.join(
|
filename = os.path.join(out_dir, TLGenerator.get_file_name(
|
||||||
out_dir,
|
tlobject, add_extension=True
|
||||||
TLGenerator.get_file_name(
|
))
|
||||||
tlobject, add_extension=True))
|
|
||||||
|
|
||||||
with open(filename, 'w', encoding='utf-8') as file:
|
with open(filename, 'w', encoding='utf-8') as file:
|
||||||
# Let's build the source code!
|
|
||||||
with SourceBuilder(file) as builder:
|
with SourceBuilder(file) as builder:
|
||||||
# Both types and functions inherit from
|
TLGenerator._write_source_code(
|
||||||
# MTProtoRequest so they all can be sent
|
tlobject, builder, depth, type_constructors)
|
||||||
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
|
|
||||||
|
|
||||||
# Step 3: Add the relative imports to the namespaces on __init__.py's
|
# Step 3: Add the relative imports to the namespaces on __init__.py's
|
||||||
init_py = os.path.join(get_output_path('functions'), '__init__.py')
|
init_py = os.path.join(get_output_path('functions'), '__init__.py')
|
||||||
|
@ -380,6 +171,236 @@ class TLGenerator:
|
||||||
builder.current_indent -= 1
|
builder.current_indent -= 1
|
||||||
builder.writeln('}')
|
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
|
@staticmethod
|
||||||
def get_class_name(tlobject):
|
def get_class_name(tlobject):
|
||||||
"""Gets the class name following the Python style guidelines, in ThisClassFormat"""
|
"""Gets the class name following the Python style guidelines, in ThisClassFormat"""
|
||||||
|
|
Loading…
Reference in New Issue
Block a user