From e3c56b0d98d6862d6fe03071f526652bd662d2b3 Mon Sep 17 00:00:00 2001 From: Lonami Exo Date: Fri, 19 Jan 2018 13:00:17 +0100 Subject: [PATCH] Reduce autocast overhead as much as possible Rationale: if the user is doing things right, the penalty for being friendly (i.e. autocasting to the right version, like User -> InputPeerUser), should be as little as possible. Removing the redundant type() call to access .SUBCLASS_OF_ID and assuming the user provided a TLObject (through excepting whenever the attribute is not available) is x2 and x4 times faster respectively. Of course, this is a micro-optimization, but I still consider it's good to benefit users doing things right or avoiding redundant calls. --- telethon/telegram_client.py | 32 ++++++++------- telethon/utils.py | 77 ++++++++++++++++++------------------- 2 files changed, 56 insertions(+), 53 deletions(-) diff --git a/telethon/telegram_client.py b/telethon/telegram_client.py index 5c4493f3..f09a62fa 100644 --- a/telethon/telegram_client.py +++ b/telethon/telegram_client.py @@ -818,10 +818,12 @@ class TelegramClient(TelegramBareClient): if isinstance(reply_to, int): return reply_to - if isinstance(reply_to, TLObject) and \ - type(reply_to).SUBCLASS_OF_ID == 0x790009e3: - # hex(crc32(b'Message')) = 0x790009e3 - return reply_to.id + try: + if reply_to.SUBCLASS_OF_ID == 0x790009e3: + # hex(crc32(b'Message')) = 0x790009e3 + return reply_to.id + except AttributeError: + pass raise TypeError('Invalid reply_to type: {}'.format(type(reply_to))) @@ -1191,9 +1193,14 @@ class TelegramClient(TelegramBareClient): """ photo = entity possible_names = [] - if not isinstance(entity, TLObject) or type(entity).SUBCLASS_OF_ID in ( + try: + is_entity = entity.SUBCLASS_OF_ID in ( 0x2da17977, 0xc5af5d94, 0x1f4661b9, 0xd49a2697 - ): + ) + except AttributeError: + return None # Not even a TLObject as attribute access failed + + if is_entity: # Maybe it is an user or a chat? Or their full versions? # # The hexadecimal numbers above are simply: @@ -1705,14 +1712,13 @@ class TelegramClient(TelegramBareClient): if isinstance(peer, int): peer = PeerUser(peer) is_peer = True - - elif isinstance(peer, TLObject): - is_peer = type(peer).SUBCLASS_OF_ID == 0x2d45687 # crc32(b'Peer') - if not is_peer: - try: + else: + try: + is_peer = peer.SUBCLASS_OF_ID == 0x2d45687 # crc32(b'Peer') + if not is_peer: return utils.get_input_peer(peer) - except TypeError: - pass + except (AttributeError, TypeError): + pass # Attribute if not TLObject, Type if not "casteable" if not is_peer: raise TypeError( diff --git a/telethon/utils.py b/telethon/utils.py index 8549e18d..a4850d4c 100644 --- a/telethon/utils.py +++ b/telethon/utils.py @@ -3,11 +3,9 @@ Utilities for working with the Telegram API itself (such as handy methods to convert between an entity like an User, Chat, etc. into its Input version) """ import math +import re from mimetypes import add_type, guess_extension -import re - -from .tl import TLObject from .tl.types import ( Channel, ChannelForbidden, Chat, ChatEmpty, ChatForbidden, ChatFull, ChatPhoto, InputPeerChannel, InputPeerChat, InputPeerUser, InputPeerEmpty, @@ -25,7 +23,6 @@ from .tl.types import ( InputMediaUploadedPhoto, DocumentAttributeFilename, photos ) - USERNAME_RE = re.compile( r'@|(?:https?://)?(?:telegram\.(?:me|dog)|t\.me)/(joinchat/)?' ) @@ -81,12 +78,12 @@ def _raise_cast_fail(entity, target): def get_input_peer(entity, allow_self=True): """Gets the input peer for the given "entity" (user, chat or channel). A TypeError is raised if the given entity isn't a supported type.""" - if not isinstance(entity, TLObject): + try: + if entity.SUBCLASS_OF_ID == 0xc91c90b6: # crc32(b'InputPeer') + return entity + except AttributeError: _raise_cast_fail(entity, 'InputPeer') - if type(entity).SUBCLASS_OF_ID == 0xc91c90b6: # crc32(b'InputPeer') - return entity - if isinstance(entity, User): if entity.is_self and allow_self: return InputPeerSelf() @@ -123,12 +120,12 @@ def get_input_peer(entity, allow_self=True): def get_input_channel(entity): """Similar to get_input_peer, but for InputChannel's alone""" - if not isinstance(entity, TLObject): + try: + if entity.SUBCLASS_OF_ID == 0x40f202fd: # crc32(b'InputChannel') + return entity + except AttributeError: _raise_cast_fail(entity, 'InputChannel') - if type(entity).SUBCLASS_OF_ID == 0x40f202fd: # crc32(b'InputChannel') - return entity - if isinstance(entity, (Channel, ChannelForbidden)): return InputChannel(entity.id, entity.access_hash or 0) @@ -140,12 +137,12 @@ def get_input_channel(entity): def get_input_user(entity): """Similar to get_input_peer, but for InputUser's alone""" - if not isinstance(entity, TLObject): + try: + if entity.SUBCLASS_OF_ID == 0xe669bf46: # crc32(b'InputUser'): + return entity + except AttributeError: _raise_cast_fail(entity, 'InputUser') - if type(entity).SUBCLASS_OF_ID == 0xe669bf46: # crc32(b'InputUser') - return entity - if isinstance(entity, User): if entity.is_self: return InputUserSelf() @@ -169,12 +166,12 @@ def get_input_user(entity): def get_input_document(document): """Similar to get_input_peer, but for documents""" - if not isinstance(document, TLObject): + try: + if document.SUBCLASS_OF_ID == 0xf33fdb68: # crc32(b'InputDocument'): + return document + except AttributeError: _raise_cast_fail(document, 'InputDocument') - if type(document).SUBCLASS_OF_ID == 0xf33fdb68: # crc32(b'InputDocument') - return document - if isinstance(document, Document): return InputDocument(id=document.id, access_hash=document.access_hash) @@ -192,12 +189,12 @@ def get_input_document(document): def get_input_photo(photo): """Similar to get_input_peer, but for documents""" - if not isinstance(photo, TLObject): + try: + if photo.SUBCLASS_OF_ID == 0x846363e0: # crc32(b'InputPhoto'): + return photo + except AttributeError: _raise_cast_fail(photo, 'InputPhoto') - if type(photo).SUBCLASS_OF_ID == 0x846363e0: # crc32(b'InputPhoto') - return photo - if isinstance(photo, photos.Photo): photo = photo.photo @@ -212,12 +209,12 @@ def get_input_photo(photo): def get_input_geo(geo): """Similar to get_input_peer, but for geo points""" - if not isinstance(geo, TLObject): + try: + if geo.SUBCLASS_OF_ID == 0x430d225: # crc32(b'InputGeoPoint'): + return geo + except AttributeError: _raise_cast_fail(geo, 'InputGeoPoint') - if type(geo).SUBCLASS_OF_ID == 0x430d225: # crc32(b'InputGeoPoint') - return geo - if isinstance(geo, GeoPoint): return InputGeoPoint(lat=geo.lat, long=geo.long) @@ -239,12 +236,12 @@ def get_input_media(media, user_caption=None, is_photo=False): If the media is a file location and is_photo is known to be True, it will be treated as an InputMediaUploadedPhoto. """ - if not isinstance(media, TLObject): + try: + if media.SUBCLASS_OF_ID == 0xfaf846f4: # crc32(b'InputMedia'): + return media + except AttributeError: _raise_cast_fail(media, 'InputMedia') - if type(media).SUBCLASS_OF_ID == 0xfaf846f4: # crc32(b'InputMedia') - return media - if isinstance(media, MessageMediaPhoto): return InputMediaPhoto( id=get_input_photo(media.photo), @@ -357,15 +354,15 @@ def get_peer_id(peer): a call to utils.resolve_id(marked_id). """ # First we assert it's a Peer TLObject, or early return for integers - if not isinstance(peer, TLObject): - if isinstance(peer, int): - return peer - else: - _raise_cast_fail(peer, 'int') + if isinstance(peer, int): + return peer - elif type(peer).SUBCLASS_OF_ID not in {0x2d45687, 0xc91c90b6}: - # Not a Peer or an InputPeer, so first get its Input version - peer = get_input_peer(peer, allow_self=False) + try: + if peer.SUBCLASS_OF_ID not in (0x2d45687, 0xc91c90b6): + # Not a Peer or an InputPeer, so first get its Input version + peer = get_input_peer(peer, allow_self=False) + except AttributeError: + _raise_cast_fail(peer, 'int') # Set the right ID/kind, or raise if the TLObject is not recognised if isinstance(peer, (PeerUser, InputPeerUser)):