Avoid relying on .__iter__ to tell iterators apart

.send_file() would fail with stream objects (those from open())
since they are iterable, and asserting that they weren't bytes
or str was not enough.
This commit is contained in:
Lonami Exo 2018-02-26 14:12:21 +01:00
parent 6f16aeb553
commit 5a54e2279f
4 changed files with 19 additions and 7 deletions

View File

@ -14,7 +14,7 @@ def _into_id_set(client, chats):
if chats is None: if chats is None:
return None return None
if not hasattr(chats, '__iter__') or isinstance(chats, str): if not utils.is_list_like(chats):
chats = (chats,) chats = (chats,)
result = set() result = set()

View File

@ -355,14 +355,14 @@ class Session:
if not self.save_entities: if not self.save_entities:
return return
if not isinstance(tlo, TLObject) and hasattr(tlo, '__iter__'): if not isinstance(tlo, TLObject) and utils.is_list_like(tlo):
# This may be a list of users already for instance # This may be a list of users already for instance
entities = tlo entities = tlo
else: else:
entities = [] entities = []
if hasattr(tlo, 'chats') and hasattr(tlo.chats, '__iter__'): if hasattr(tlo, 'chats') and utils.is_list_like(tlo.chats):
entities.extend(tlo.chats) entities.extend(tlo.chats)
if hasattr(tlo, 'users') and hasattr(tlo.users, '__iter__'): if hasattr(tlo, 'users') and utils.is_list_like(tlo.users):
entities.extend(tlo.users) entities.extend(tlo.users)
if not entities: if not entities:
return return

View File

@ -975,7 +975,7 @@ class TelegramClient(TelegramBareClient):
""" """
if max_id is None: if max_id is None:
if message: if message:
if hasattr(message, '__iter__'): if utils.is_list_like(message):
max_id = max(msg.id for msg in message) max_id = max(msg.id for msg in message)
else: else:
max_id = message.id max_id = message.id
@ -1140,7 +1140,7 @@ class TelegramClient(TelegramBareClient):
""" """
# First check if the user passed an iterable, in which case # First check if the user passed an iterable, in which case
# we may want to send as an album if all are photo files. # we may want to send as an album if all are photo files.
if hasattr(file, '__iter__') and not isinstance(file, (str, bytes)): if utils.is_list_like(file):
# Convert to tuple so we can iterate several times # Convert to tuple so we can iterate several times
file = tuple(x for x in file) file = tuple(x for x in file)
if all(utils.is_image(x) for x in file): if all(utils.is_image(x) for x in file):
@ -1960,7 +1960,7 @@ class TelegramClient(TelegramBareClient):
``User``, ``Chat`` or ``Channel`` corresponding to the input ``User``, ``Chat`` or ``Channel`` corresponding to the input
entity. entity.
""" """
if hasattr(entity, '__iter__') and not isinstance(entity, str): if utils.is_list_like(entity):
single = False single = False
else: else:
single = True single = True

View File

@ -5,6 +5,7 @@ to convert between an entity like an User, Chat, etc. into its Input version)
import math import math
import mimetypes import mimetypes
import re import re
import types
from mimetypes import add_type, guess_extension from mimetypes import add_type, guess_extension
from .tl.types import ( from .tl.types import (
@ -341,6 +342,17 @@ def is_video(file):
(mimetypes.guess_type(file)[0] or '').startswith('video/')) (mimetypes.guess_type(file)[0] or '').startswith('video/'))
def is_list_like(obj):
"""
Returns True if the given object looks like a list.
Checking if hasattr(obj, '__iter__') and ignoring str/bytes is not
enough. Things like open() are also iterable (and probably many
other things), so just support the commonly known list-like objects.
"""
return isinstance(obj, (list, tuple, set, dict, types.GeneratorType))
def parse_phone(phone): def parse_phone(phone):
"""Parses the given phone, or returns None if it's invalid""" """Parses the given phone, or returns None if it's invalid"""
if isinstance(phone, int): if isinstance(phone, int):