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:
@staticmethod
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'))
@staticmethod
@ -36,25 +38,28 @@ class TLGenerator:
@staticmethod
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
os.makedirs(get_output_path('functions'), 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))
# 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
# 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
# will later need to perform a relative import for them to be used
function_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)
for tlobject in tlobjects:
if tlobject.is_function:
@ -72,8 +77,9 @@ class TLGenerator:
for tlobject in tlobjects:
if TLGenerator.get_file_name(tlobject, add_extension=False) \
in namespace_directories:
# If this TLObject isn't under the same directory as its name (i.e. "contacts"),
# append "_tg" to avoid confusion between the file and the directory (i.e. "updates")
# If this TLObject isn't under the same directory as its
# name (i.e. "contacts"), append "_tg" to avoid confusion
# between the file and the directory (i.e. "updates")
if tlobject.namespace != tlobject.name:
tlobject.name += '_tg'
@ -95,7 +101,7 @@ class TLGenerator:
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')
with open(init_py, 'a', encoding='utf-8') as file:
with SourceBuilder(file) as builder:
@ -112,7 +118,8 @@ class TLGenerator:
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
# Both types and functions inherit from
# MTProtoRequest so they all can be sent
builder.writeln('from {}.tl.mtproto_request import MTProtoRequest'
.format('.' * depth))
@ -125,27 +132,28 @@ class TLGenerator:
builder.writeln('class {}(MTProtoRequest):'.format(
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(
'"""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()
# Create an class-level variable that stores the TLObject's constructor ID
# Class-level variable to store its constructor ID
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(
hex(tlobject.id)))
builder.writeln()
# First sort the arguments so that those not being a flag come first
# Flag arguments must go last
args = [
a for a in tlobject.sorted_args()
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 = [
(a.name if not a.is_flag and not a.can_be_inferred
else '{}=None'.format(a.name))
@ -160,26 +168,32 @@ class TLGenerator:
builder.writeln('def __init__(self):')
# Now update args to have the TLObject arguments, _except_
# those which are generated automatically: flag indicator and generic definitions.
# We don't need the generic definitions in Python because arguments can be any type
# 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, so we know the type of the arguments
# 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))
':param {}: Telegram type: "{}".'
.format(arg.name, arg.type)
)
if arg.is_vector:
builder.write(' Must be a list.'.format(
arg.name))
builder.write(
' Must be a list.'.format(arg.name)
)
if arg.is_generic:
builder.write(
' This should be another MTProtoRequest.')
' Must be another MTProtoRequest.'
)
builder.writeln()
# We also want to know what type this request returns
@ -243,21 +257,23 @@ class TLGenerator:
if args:
builder.writeln('return {')
base_types = ["string", "bytes", "int", "long", "int128", "int256", "double", "Bool", "true",
"date"]
base_types = ['string', 'bytes', 'int', 'long',
'int128', 'int256', 'double', 'Bool',
'true', 'date']
for arg in args:
builder.writeln("\'{}\': ".format(arg.name))
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 "",
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.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 {}')
@ -280,7 +296,7 @@ class TLGenerator:
builder.writeln('@staticmethod')
builder.writeln('def empty():')
builder.writeln(
'"""Returns an "empty" instance (all attributes are None)"""')
'"""Returns an "empty" instance (attributes=None)"""')
builder.writeln('return {}({})'.format(
TLGenerator.get_class_name(tlobject), ', '.join(
'None' for _ in range(len(args)))))
@ -288,7 +304,8 @@ class TLGenerator:
# 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
# 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:
@ -297,7 +314,8 @@ class TLGenerator:
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
# If there were no arguments, we still need an
# on_response method, and hence "pass" if empty
builder.writeln('pass')
builder.end_block()
@ -308,7 +326,7 @@ class TLGenerator:
builder.writeln('def __str__(self):')
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
init_py = os.path.join(get_output_path('functions'), '__init__.py')
@ -321,7 +339,8 @@ class TLGenerator:
file.write('from . import {}\n'
.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'))
with open(filename, 'w', encoding='utf-8') as file:
with SourceBuilder(file) as builder:
@ -349,7 +368,9 @@ class TLGenerator:
constructor = '0x' + constructor[2:].zfill(8)
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:
builder.write('.' + tlobject.namespace)
@ -582,9 +603,10 @@ class TLGenerator:
:param tlobject: The TLObject for which the 'self.result = ' will be written
"""
if tlobject.result.startswith('Vector<'):
# Vector results are a bit special since they can also be composed of
# integer values and such; however, the result of requests is not
# parsed as arguments are and it's a bit harder to tell which is which.
# Vector results are a bit special since they can also be composed
# of integer values and such; however, the result of requests is
# not parsed as arguments are and it's a bit harder to tell which
# is which.
if tlobject.result == 'Vector<int>':
builder.writeln('reader.read_int() # Vector id')
builder.writeln('count = reader.read_int()')