mirror of
				https://github.com/LonamiWebs/Telethon.git
				synced 2025-10-30 23:47:33 +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