diff --git a/docs/generate.py b/docs/generate.py index 14708b74..096ddceb 100755 --- a/docs/generate.py +++ b/docs/generate.py @@ -3,7 +3,7 @@ import os import re import sys import shutil -from docs.docs_writer import DocsWriter +from .docs_writer import DocsWriter # Small trick so importing telethon_generator works sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) diff --git a/telethon/__init__.py b/telethon/__init__.py index e16e51ab..bd0d6c28 100644 --- a/telethon/__init__.py +++ b/telethon/__init__.py @@ -1,3 +1,4 @@ from .errors import * from .telegram_client import TelegramClient from .interactive_telegram_client import InteractiveTelegramClient +from . import tl diff --git a/telethon/crypto/auth_key.py b/telethon/crypto/auth_key.py index 7643958c..88623658 100644 --- a/telethon/crypto/auth_key.py +++ b/telethon/crypto/auth_key.py @@ -1,5 +1,5 @@ -import telethon.helpers as utils -from telethon.utils import BinaryReader, BinaryWriter +from .. import helpers as utils +from ..utils import BinaryReader, BinaryWriter class AuthKey: diff --git a/telethon/crypto/rsa.py b/telethon/crypto/rsa.py index 8cc7e437..3e425ba7 100644 --- a/telethon/crypto/rsa.py +++ b/telethon/crypto/rsa.py @@ -1,7 +1,7 @@ import os -import telethon.helpers as utils -from telethon.utils import BinaryWriter +from .. import helpers as utils +from ..utils import BinaryWriter class RSAServerKey: diff --git a/telethon/interactive_telegram_client.py b/telethon/interactive_telegram_client.py index 4393cfa1..ab7636fa 100644 --- a/telethon/interactive_telegram_client.py +++ b/telethon/interactive_telegram_client.py @@ -1,9 +1,10 @@ import shutil from getpass import getpass -from telethon import RPCError, TelegramClient -from telethon.tl.types import UpdateShortChatMessage, UpdateShortMessage -from telethon.utils import get_display_name +from . import TelegramClient +from .errors import RPCError +from .tl.types import UpdateShortChatMessage, UpdateShortMessage +from .utils import get_display_name # Get the (current) number of lines in the terminal cols, rows = shutil.get_terminal_size() diff --git a/telethon/network/authenticator.py b/telethon/network/authenticator.py index 4414066c..b603226f 100644 --- a/telethon/network/authenticator.py +++ b/telethon/network/authenticator.py @@ -1,10 +1,10 @@ import os import time -import telethon.helpers as utils -from telethon.crypto import AES, RSA, AuthKey, Factorizator -from telethon.network import MtProtoPlainSender -from telethon.utils import BinaryReader, BinaryWriter +from .. import helpers as utils +from ..crypto import AES, RSA, AuthKey, Factorizator +from ..network import MtProtoPlainSender +from ..utils import BinaryReader, BinaryWriter def do_authentication(transport): diff --git a/telethon/network/mtproto_plain_sender.py b/telethon/network/mtproto_plain_sender.py index 1721d2a3..cd1b1dd4 100644 --- a/telethon/network/mtproto_plain_sender.py +++ b/telethon/network/mtproto_plain_sender.py @@ -1,7 +1,7 @@ import random import time -from telethon.utils import BinaryReader, BinaryWriter +from ..utils import BinaryReader, BinaryWriter class MtProtoPlainSender: diff --git a/telethon/network/mtproto_sender.py b/telethon/network/mtproto_sender.py index b112ecbb..57e9d13d 100644 --- a/telethon/network/mtproto_sender.py +++ b/telethon/network/mtproto_sender.py @@ -3,14 +3,15 @@ from datetime import timedelta from threading import Event, RLock, Thread from time import sleep, time -import telethon.helpers as utils -from telethon.crypto import AES -from telethon.errors import * -from telethon.tl.all_tlobjects import tlobjects -from telethon.tl.functions.updates import GetStateRequest -from telethon.tl.types import MsgsAck -from telethon.tl.functions import PingRequest -from telethon.utils import BinaryReader, BinaryWriter +from .. import helpers as utils +from ..crypto import AES +from ..errors import (BadMessageError, RPCError, + InvalidDCError, ReadCancelledError) +from ..tl.all_tlobjects import tlobjects +from ..tl.functions import PingRequest +from ..tl.functions.updates import GetStateRequest +from ..tl.types import MsgsAck +from ..utils import BinaryReader, BinaryWriter import logging logging.getLogger(__name__).addHandler(logging.NullHandler()) diff --git a/telethon/network/tcp_client.py b/telethon/network/tcp_client.py index 6266ceb5..f08209a1 100644 --- a/telethon/network/tcp_client.py +++ b/telethon/network/tcp_client.py @@ -4,8 +4,8 @@ import time from datetime import datetime, timedelta from threading import Event, Lock -from telethon.errors import ReadCancelledError -from telethon.utils import BinaryWriter +from ..errors import ReadCancelledError +from ..utils import BinaryWriter class TcpClient: diff --git a/telethon/network/tcp_transport.py b/telethon/network/tcp_transport.py index 8ad2363d..512ac1d5 100644 --- a/telethon/network/tcp_transport.py +++ b/telethon/network/tcp_transport.py @@ -1,9 +1,9 @@ from binascii import crc32 from datetime import timedelta -from telethon.errors import * -from telethon.network import TcpClient -from telethon.utils import BinaryWriter +from ..errors import InvalidChecksumError +from ..network import TcpClient +from ..utils import BinaryWriter class TcpTransport: diff --git a/telethon/parser/markdown_parser.py b/telethon/parser/markdown_parser.py index 59bc913b..5bed6c0a 100644 --- a/telethon/parser/markdown_parser.py +++ b/telethon/parser/markdown_parser.py @@ -1,5 +1,5 @@ -from telethon.tl.types import (MessageEntityBold, MessageEntityCode, - MessageEntityItalic, MessageEntityTextUrl) +from ..tl.types import (MessageEntityBold, MessageEntityCode, + MessageEntityItalic, MessageEntityTextUrl) def parse_message_entities(msg): diff --git a/telethon/telegram_client.py b/telethon/telegram_client.py index 232bf79a..055ad379 100644 --- a/telethon/telegram_client.py +++ b/telethon/telegram_client.py @@ -5,37 +5,36 @@ from mimetypes import guess_type from os import listdir, path # Import some externalized utilities to work with the Telegram types and more -import telethon.helpers as utils -import telethon.network.authenticator as authenticator -from telethon.errors import * -from telethon.network import MtProtoSender, TcpTransport -from telethon.parser.markdown_parser import parse_message_entities +from . import helpers as utils +from .errors import RPCError, InvalidDCError, InvalidParameterError +from .network import authenticator, MtProtoSender, TcpTransport +from .parser.markdown_parser import parse_message_entities # For sending and receiving requests -from telethon.tl import MTProtoRequest, Session -from telethon.tl.all_tlobjects import layer -from telethon.tl.functions import InitConnectionRequest, InvokeWithLayerRequest +from .tl import MTProtoRequest, Session +from .tl.all_tlobjects import layer +from .tl.functions import InitConnectionRequest, InvokeWithLayerRequest # The following is required to get the password salt -from telethon.tl.functions.account import GetPasswordRequest -from telethon.tl.functions.auth import (CheckPasswordRequest, LogOutRequest, - SendCodeRequest, SignInRequest, - SignUpRequest) -from telethon.tl.functions.auth import ImportBotAuthorizationRequest -from telethon.tl.functions.help import GetConfigRequest -from telethon.tl.functions.messages import ( +from .tl.functions.account import GetPasswordRequest +from .tl.functions.auth import (CheckPasswordRequest, LogOutRequest, + SendCodeRequest, SignInRequest, + SignUpRequest) +from .tl.functions.auth import ImportBotAuthorizationRequest +from .tl.functions.help import GetConfigRequest +from .tl.functions.messages import ( GetDialogsRequest, GetHistoryRequest, ReadHistoryRequest, SendMediaRequest, SendMessageRequest) # The Requests and types that we'll be using -from telethon.tl.functions.upload import ( +from .tl.functions.upload import ( GetFileRequest, SaveBigFilePartRequest, SaveFilePartRequest) # All the types we need to work with -from telethon.tl.types import ( +from .tl.types import ( ChatPhotoEmpty, DocumentAttributeAudio, DocumentAttributeFilename, InputDocumentFileLocation, InputFile, InputFileBig, InputFileLocation, InputMediaUploadedDocument, InputMediaUploadedPhoto, InputPeerEmpty, MessageMediaContact, MessageMediaDocument, MessageMediaPhoto, UserProfilePhotoEmpty) -from telethon.utils import (find_user_or_chat, get_input_peer, - get_appropiate_part_size, get_extension) +from .utils import (find_user_or_chat, get_input_peer, + get_appropiate_part_size, get_extension) class TelegramClient: diff --git a/telethon/tl/__init__.py b/telethon/tl/__init__.py index 47104db9..0fa833da 100644 --- a/telethon/tl/__init__.py +++ b/telethon/tl/__init__.py @@ -1,2 +1,2 @@ -from telethon.tl.mtproto_request import MTProtoRequest -from telethon.tl.session import Session +from .mtproto_request import MTProtoRequest +from .session import Session diff --git a/telethon/tl/session.py b/telethon/tl/session.py index d2873889..ec181011 100644 --- a/telethon/tl/session.py +++ b/telethon/tl/session.py @@ -4,7 +4,7 @@ import random import time from os.path import isfile as file_exists -import telethon.helpers as utils +from .. import helpers as utils class Session: diff --git a/telethon/utils/binary_reader.py b/telethon/utils/binary_reader.py index 04103ae7..c3016527 100644 --- a/telethon/utils/binary_reader.py +++ b/telethon/utils/binary_reader.py @@ -3,8 +3,8 @@ from datetime import datetime from io import BufferedReader, BytesIO from struct import unpack -from telethon.errors import * -from telethon.tl.all_tlobjects import tlobjects +from ..errors import InvalidParameterError, TypeNotFoundError +from ..tl.all_tlobjects import tlobjects class BinaryReader: diff --git a/telethon/utils/tl_utils.py b/telethon/utils/tl_utils.py index 09d14b20..63c8b420 100644 --- a/telethon/utils/tl_utils.py +++ b/telethon/utils/tl_utils.py @@ -4,7 +4,7 @@ after all, both are the same attribute, IDs.""" from mimetypes import add_type, guess_extension -from telethon.tl.types import ( +from ..tl.types import ( Channel, Chat, ChatPhoto, InputPeerChannel, InputPeerChat, InputPeerUser, MessageMediaDocument, MessageMediaPhoto, PeerChannel, PeerChat, PeerUser, User, UserProfilePhoto) diff --git a/telethon_generator/tl_generator.py b/telethon_generator/tl_generator.py index 8a4a99f5..d498e734 100755 --- a/telethon_generator/tl_generator.py +++ b/telethon_generator/tl_generator.py @@ -12,6 +12,8 @@ except (ImportError, SystemError): def get_output_path(normal_path): return os.path.join('../telethon/tl', normal_path) +output_base_depth = 2 # telethon/tl/ + class TLGenerator: @staticmethod @@ -45,10 +47,22 @@ class TLGenerator: # 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" - namespace_directories = set() + # + # 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() for tlobject in tlobjects: - namespace_directories.add(tlobject.namespace) + if tlobject.namespace: + if tlobject.is_function: + function_namespaces.add(tlobject.namespace) + else: + type_namespaces.add(tlobject.namespace) + # Merge both namespaces to easily check if any namespace exists, + # though we could also distinguish between types and functions + # here, it's not worth doing + namespace_directories = function_namespaces | type_namespaces for tlobject in tlobjects: if TLGenerator.get_file_name(tlobject, add_extension=False) \ in namespace_directories: @@ -67,7 +81,10 @@ class TLGenerator: out_dir = get_output_path('functions' if tlobject.is_function else 'types') + # Path depth to perform relative import + depth = output_base_depth if tlobject.namespace: + depth += 1 out_dir = os.path.join(out_dir, tlobject.namespace) os.makedirs(out_dir, exist_ok=True) @@ -76,8 +93,8 @@ class TLGenerator: init_py = os.path.join(out_dir, '__init__.py') with open(init_py, 'a', encoding='utf-8') as file: with SourceBuilder(file) as builder: - builder.writeln('from {} import {}'.format( - TLGenerator.get_full_file_name(tlobject), + builder.writeln('from .{} import {}'.format( + TLGenerator.get_file_name(tlobject, add_extension=False), TLGenerator.get_class_name(tlobject))) # Create the file for this TLObject @@ -89,8 +106,8 @@ class TLGenerator: # 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 telethon.tl.mtproto_request import MTProtoRequest') + builder.writeln('from {}.tl.mtproto_request import MTProtoRequest' + .format('.' * depth)) builder.writeln() builder.writeln() builder.writeln('class {}(MTProtoRequest):'.format( @@ -214,7 +231,18 @@ class TLGenerator: builder.writeln('return {}'.format(str(tlobject))) # builder.end_block() # There is no need to end the last block - # Step 3: Once all the objects have been generated, we can now group them in a single file + # Step 3: Add the relative imports to the namespaces on __init__.py's + init_py = os.path.join(get_output_path('functions'), '__init__.py') + with open(init_py, 'a') as file: + file.write('from . import {}\n' + .format(', '.join(function_namespaces))) + + init_py = os.path.join(get_output_path('types'), '__init__.py') + with open(init_py, 'a') as file: + 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 filename = os.path.join(get_output_path('all_tlobjects.py')) with open(filename, 'w', encoding='utf-8') as file: with SourceBuilder(file) as builder: @@ -222,10 +250,7 @@ class TLGenerator: '"""File generated by TLObjects\' generator. All changes will be ERASED"""') builder.writeln() - # First add imports - for tlobject in tlobjects: - builder.writeln('import {}'.format( - TLGenerator.get_full_file_name(tlobject))) + builder.writeln('from ..tl import types, functions') builder.writeln() # Create a variable to indicate which layer this is @@ -239,9 +264,18 @@ class TLGenerator: # Fill the dictionary (0x1a2b3c4f: tl.full.type.path.Class) for tlobject in tlobjects: - builder.writeln('{}: {}.{},'.format( - hex(tlobject.id), TLGenerator.get_full_file_name( - tlobject), TLGenerator.get_class_name(tlobject))) + constructor = hex(tlobject.id) + if len(constructor) != 10: + # Make it a nice length 10 so it fits well + constructor = '0x' + constructor[2:].zfill(8) + + builder.write('{}: '.format(constructor)) + builder.write('functions' if tlobject.is_function else 'types') + if tlobject.namespace: + builder.write('.' + tlobject.namespace) + + builder.writeln('.{},'.format( + TLGenerator.get_class_name(tlobject))) builder.current_indent -= 1 builder.writeln('}') @@ -262,20 +296,7 @@ class TLGenerator: return result @staticmethod - def get_full_file_name(tlobject): - """Gets the full file name for the given TLObject (tl.type.full.path)""" - - fullname = TLGenerator.get_file_name(tlobject, add_extension=False) - if tlobject.namespace: - fullname = '{}.{}'.format(tlobject.namespace, fullname) - - if tlobject.is_function: - return 'telethon.tl.functions.{}'.format(fullname) - else: - return 'telethon.tl.types.{}'.format(fullname) - - @staticmethod - def get_file_name(tlobject, add_extension): + def get_file_name(tlobject, add_extension=False): """Gets the file name in file_name_format.py for the given TLObject""" # Courtesy of http://stackoverflow.com/a/1176023/4759433