Try to adhere to the 80-characters limit

This commit is contained in:
Lonami Exo 2017-06-12 10:16:24 +02:00
parent 76166cd1ec
commit 68a625b82b

View File

@ -19,7 +19,9 @@ output_base_depth = 2 # telethon/tl/
class TLGenerator: class TLGenerator:
@staticmethod @staticmethod
def tlobjects_exist(): def tlobjects_exist():
"""Determines whether the TLObjects were previously generated (hence exist) or not""" """Determines whether the TLObjects were previously
generated (hence exist) or not
"""
return os.path.isfile(get_output_path('all_tlobjects.py')) return os.path.isfile(get_output_path('all_tlobjects.py'))
@staticmethod @staticmethod
@ -36,25 +38,28 @@ class TLGenerator:
@staticmethod @staticmethod
def generate_tlobjects(scheme_file): def generate_tlobjects(scheme_file):
"""Generates all the TLObjects from scheme.tl to tl/functions and tl/types""" """Generates all the TLObjects from scheme.tl to
tl/functions and tl/types
"""
# First ensure that the required parent directories exist # First ensure that the required parent directories exist
os.makedirs(get_output_path('functions'), exist_ok=True) os.makedirs(get_output_path('functions'), exist_ok=True)
os.makedirs(get_output_path('types'), exist_ok=True) os.makedirs(get_output_path('types'), exist_ok=True)
# Step 0: Store the parsed file in a tuple to avoid parsing it on each iteration # Step 0: Cache the parsed file on a tuple
tlobjects = tuple(TLParser.parse_file(scheme_file)) tlobjects = tuple(TLParser.parse_file(scheme_file))
# Step 1: Ensure that no object has the same name as a namespace # Step 1: Ensure that no object has the same name as a namespace
# We must check this because Python will complain if it sees a # We must check this because Python will complain if it sees a
# file and a directory with the same name, which happens for example with "updates" # file and a directory with the same name, which happens for
# example with "updates".
# #
# We distinguish between function and type namespaces since we # We distinguish between function and type namespaces since we
# will later need to perform a relative import for them to be used # will later need to perform a relative import for them to be used
function_namespaces = set() function_namespaces = set()
type_namespaces = set() type_namespaces = set()
# Now that we're iterating over all the objects we also store Type: [Constructors] # Make use of this iteration to also store 'Type: [Constructors]'
type_constructors = defaultdict(list) type_constructors = defaultdict(list)
for tlobject in tlobjects: for tlobject in tlobjects:
if tlobject.is_function: if tlobject.is_function:
@ -72,8 +77,9 @@ class TLGenerator:
for tlobject in tlobjects: for tlobject in tlobjects:
if TLGenerator.get_file_name(tlobject, add_extension=False) \ if TLGenerator.get_file_name(tlobject, add_extension=False) \
in namespace_directories: in namespace_directories:
# If this TLObject isn't under the same directory as its name (i.e. "contacts"), # If this TLObject isn't under the same directory as its
# append "_tg" to avoid confusion between the file and the directory (i.e. "updates") # name (i.e. "contacts"), append "_tg" to avoid confusion
# between the file and the directory (i.e. "updates")
if tlobject.namespace != tlobject.name: if tlobject.namespace != tlobject.name:
tlobject.name += '_tg' tlobject.name += '_tg'
@ -95,7 +101,7 @@ class TLGenerator:
os.makedirs(out_dir, exist_ok=True) os.makedirs(out_dir, exist_ok=True)
# Also add this object to __init__.py, so we can import the whole packet at once # Add this object to __init__.py, so we can import *
init_py = os.path.join(out_dir, '__init__.py') init_py = os.path.join(out_dir, '__init__.py')
with open(init_py, 'a', encoding='utf-8') as file: with open(init_py, 'a', encoding='utf-8') as file:
with SourceBuilder(file) as builder: with SourceBuilder(file) as builder:
@ -112,7 +118,8 @@ class TLGenerator:
with open(filename, 'w', encoding='utf-8') as file: with open(filename, 'w', encoding='utf-8') as file:
# Let's build the source code! # Let's build the source code!
with SourceBuilder(file) as builder: with SourceBuilder(file) as builder:
# Both types and functions inherit from MTProtoRequest so they all can be sent # Both types and functions inherit from
# MTProtoRequest so they all can be sent
builder.writeln('from {}.tl.mtproto_request import MTProtoRequest' builder.writeln('from {}.tl.mtproto_request import MTProtoRequest'
.format('.' * depth)) .format('.' * depth))
@ -125,27 +132,28 @@ class TLGenerator:
builder.writeln('class {}(MTProtoRequest):'.format( builder.writeln('class {}(MTProtoRequest):'.format(
TLGenerator.get_class_name(tlobject))) TLGenerator.get_class_name(tlobject)))
# Write the original .tl definition, along with a "generated automatically" message # Write the original .tl definition,
# along with a "generated automatically" message
builder.writeln( builder.writeln(
'"""Class generated by TLObjects\' generator. ' '"""Class generated by TLObjects\' generator. '
'All changes will be ERASED. Original .tl definition below.') 'All changes will be ERASED. TL definition below.')
builder.writeln('{}"""'.format(repr(tlobject))) builder.writeln('{}"""'.format(repr(tlobject)))
builder.writeln() builder.writeln()
# Create an class-level variable that stores the TLObject's constructor ID # Class-level variable to store its constructor ID
builder.writeln( builder.writeln(
"# Telegram's constructor ID (and unique identifier) for this class") "# Telegram's constructor (U)ID for this class")
builder.writeln('constructor_id = {}'.format( builder.writeln('constructor_id = {}'.format(
hex(tlobject.id))) hex(tlobject.id)))
builder.writeln() builder.writeln()
# First sort the arguments so that those not being a flag come first # Flag arguments must go last
args = [ args = [
a for a in tlobject.sorted_args() a for a in tlobject.sorted_args()
if not a.flag_indicator and not a.generic_definition if not a.flag_indicator and not a.generic_definition
] ]
# Then convert the args to string parameters, the flags having =None # Convert the args to string parameters, flags having =None
args = [ args = [
(a.name if not a.is_flag and not a.can_be_inferred (a.name if not a.is_flag and not a.can_be_inferred
else '{}=None'.format(a.name)) else '{}=None'.format(a.name))
@ -160,26 +168,32 @@ class TLGenerator:
builder.writeln('def __init__(self):') builder.writeln('def __init__(self):')
# Now update args to have the TLObject arguments, _except_ # Now update args to have the TLObject arguments, _except_
# those which are generated automatically: flag indicator and generic definitions. # those which are calculated on send or ignored, this is
# We don't need the generic definitions in Python because arguments can be any type # 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 args = [arg for arg in tlobject.args
if not arg.flag_indicator and if not arg.flag_indicator and
not arg.generic_definition] not arg.generic_definition]
if args: if args:
# Write the docstring, so we know the type of the arguments # Write the docstring, to know the type of the args
builder.writeln('"""') builder.writeln('"""')
for arg in args: for arg in args:
if not arg.flag_indicator: if not arg.flag_indicator:
builder.write( builder.write(
':param {}: Telegram type: "{}".'.format( ':param {}: Telegram type: "{}".'
arg.name, arg.type)) .format(arg.name, arg.type)
)
if arg.is_vector: if arg.is_vector:
builder.write(' Must be a list.'.format( builder.write(
arg.name)) ' Must be a list.'.format(arg.name)
)
if arg.is_generic: if arg.is_generic:
builder.write( builder.write(
' This should be another MTProtoRequest.') ' Must be another MTProtoRequest.'
)
builder.writeln() builder.writeln()
# We also want to know what type this request returns # We also want to know what type this request returns
@ -243,21 +257,23 @@ class TLGenerator:
if args: if args:
builder.writeln('return {') builder.writeln('return {')
base_types = ["string", "bytes", "int", "long", "int128", "int256", "double", "Bool", "true", base_types = ['string', 'bytes', 'int', 'long',
"date"] 'int128', 'int256', 'double', 'Bool',
'true', 'date']
for arg in args: for arg in args:
builder.writeln("\'{}\': ".format(arg.name)) builder.writeln("'{}': ".format(arg.name))
if arg.is_vector: if arg.is_vector:
builder.writeln("[x{} for x in self.{}] if self.{} is not None else []," builder.writeln('[x{} for x in self.{}] if self.{} is not None else [],'
.format(".to_dict() if x is not None else None" .format('.to_dict() if x is not None else None'
if arg.type not in base_types else "", if arg.type not in base_types else '',
arg.name, arg.name)) arg.name, arg.name))
else: else:
builder.writeln("self.{}{}," builder.writeln(
.format(arg.name, 'self.{}{},'.format(
".to_dict() if self.{} is not None else None".format(arg.name) arg.name,
if arg.type not in base_types else "")) '.to_dict() if self.{} is not None else None'
.format(arg.name) if arg.type not in base_types else ''))
builder.write("}") builder.write("}")
else: else:
builder.writeln('return {}') builder.writeln('return {}')
@ -280,7 +296,7 @@ class TLGenerator:
builder.writeln('@staticmethod') builder.writeln('@staticmethod')
builder.writeln('def empty():') builder.writeln('def empty():')
builder.writeln( builder.writeln(
'"""Returns an "empty" instance (all attributes are None)"""') '"""Returns an "empty" instance (attributes=None)"""')
builder.writeln('return {}({})'.format( builder.writeln('return {}({})'.format(
TLGenerator.get_class_name(tlobject), ', '.join( TLGenerator.get_class_name(tlobject), ', '.join(
'None' for _ in range(len(args))))) 'None' for _ in range(len(args)))))
@ -288,7 +304,8 @@ class TLGenerator:
# Write the on_response(self, reader) function # Write the on_response(self, reader) function
builder.writeln('def on_response(self, reader):') builder.writeln('def on_response(self, reader):')
# Do not read constructor's ID, since that's already been read somewhere else # Do not read constructor's ID, since
# that's already been read somewhere else
if tlobject.is_function: if tlobject.is_function:
TLGenerator.write_request_result_code(builder, tlobject) TLGenerator.write_request_result_code(builder, tlobject)
else: else:
@ -297,7 +314,8 @@ class TLGenerator:
TLGenerator.write_onresponse_code( TLGenerator.write_onresponse_code(
builder, arg, tlobject.args) builder, arg, tlobject.args)
else: else:
# If there were no arguments, we still need an on_response method, and hence "pass" if empty # If there were no arguments, we still need an
# on_response method, and hence "pass" if empty
builder.writeln('pass') builder.writeln('pass')
builder.end_block() builder.end_block()
@ -308,7 +326,7 @@ class TLGenerator:
builder.writeln('def __str__(self):') builder.writeln('def __str__(self):')
builder.writeln('return {}'.format(str(tlobject))) builder.writeln('return {}'.format(str(tlobject)))
# builder.end_block() # There is no need to end the last block # 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')
@ -321,7 +339,8 @@ class TLGenerator:
file.write('from . import {}\n' file.write('from . import {}\n'
.format(', '.join(type_namespaces))) .format(', '.join(type_namespaces)))
# Step 4: Once all the objects have been generated, we can now group them in a single file # Step 4: Once all the objects have been generated,
# we can now group them in a single file
filename = os.path.join(get_output_path('all_tlobjects.py')) filename = os.path.join(get_output_path('all_tlobjects.py'))
with open(filename, 'w', encoding='utf-8') as file: with open(filename, 'w', encoding='utf-8') as file:
with SourceBuilder(file) as builder: with SourceBuilder(file) as builder:
@ -349,7 +368,9 @@ class TLGenerator:
constructor = '0x' + constructor[2:].zfill(8) constructor = '0x' + constructor[2:].zfill(8)
builder.write('{}: '.format(constructor)) builder.write('{}: '.format(constructor))
builder.write('functions' if tlobject.is_function else 'types') builder.write(
'functions' if tlobject.is_function else 'types')
if tlobject.namespace: if tlobject.namespace:
builder.write('.' + tlobject.namespace) builder.write('.' + tlobject.namespace)
@ -582,9 +603,10 @@ class TLGenerator:
:param tlobject: The TLObject for which the 'self.result = ' will be written :param tlobject: The TLObject for which the 'self.result = ' will be written
""" """
if tlobject.result.startswith('Vector<'): if tlobject.result.startswith('Vector<'):
# Vector results are a bit special since they can also be composed of # Vector results are a bit special since they can also be composed
# integer values and such; however, the result of requests is not # of integer values and such; however, the result of requests is
# parsed as arguments are and it's a bit harder to tell which is which. # not parsed as arguments are and it's a bit harder to tell which
# is which.
if tlobject.result == 'Vector<int>': if tlobject.result == 'Vector<int>':
builder.writeln('reader.read_int() # Vector id') builder.writeln('reader.read_int() # Vector id')
builder.writeln('count = reader.read_int()') builder.writeln('count = reader.read_int()')