mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2025-01-08 19:34:12 +01:00
Clean up Bot code a bit (#673)
* Clean up Bot code a bit - Move decorators to module. It really wasn't clear how decorators inside classes work, and why they didn't have a self parameter, but still wasn't static. This also makes them effectively private without having to underscore them, which I think we should have done long time ago atm. Note that this might break backwards compatibility slightly (only if people are daft enough to have used the decorators themselves) - Don't call _message_wrapper directly. Ever. Instead always use the message decorator, since it's what it's there for. Closes #627 - Don't use the message decorator if the method isn't supposed to return a message. The decorator could handle values like True (which is often the return value), but to someone reading the code, it seems like it's a message returning method even when it wasn't. - Always document timeout and **kwargs - Log all methods * Add test to make sure timeout propagates properly despite decorators
This commit is contained in:
parent
9b5e014a0a
commit
faddb92395
3 changed files with 95 additions and 121 deletions
195
telegram/bot.py
195
telegram/bot.py
|
@ -31,10 +31,45 @@ from telegram.utils.request import Request
|
||||||
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
||||||
|
|
||||||
|
|
||||||
|
def info(func):
|
||||||
|
@functools.wraps(func)
|
||||||
|
def decorator(self, *args, **kwargs):
|
||||||
|
if not self.bot:
|
||||||
|
self.get_me()
|
||||||
|
|
||||||
|
result = func(self, *args, **kwargs)
|
||||||
|
return result
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
def log(func):
|
||||||
|
logger = logging.getLogger(func.__module__)
|
||||||
|
|
||||||
|
@functools.wraps(func)
|
||||||
|
def decorator(self, *args, **kwargs):
|
||||||
|
logger.debug('Entering: %s', func.__name__)
|
||||||
|
result = func(self, *args, **kwargs)
|
||||||
|
logger.debug(result)
|
||||||
|
logger.debug('Exiting: %s', func.__name__)
|
||||||
|
return result
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
def message(func):
|
||||||
|
@functools.wraps(func)
|
||||||
|
def decorator(self, *args, **kwargs):
|
||||||
|
url, data = func(self, *args, **kwargs)
|
||||||
|
return self._message_wrapper(url, data, *args, **kwargs)
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
class Bot(TelegramObject):
|
class Bot(TelegramObject):
|
||||||
"""This object represents a Telegram Bot.
|
"""This object represents a Telegram Bot.
|
||||||
|
|
||||||
Attributes:
|
Properties:
|
||||||
id (int): Unique identifier for this bot.
|
id (int): Unique identifier for this bot.
|
||||||
first_name (str): Bot's first name.
|
first_name (str): Bot's first name.
|
||||||
last_name (str): Bot's last name.
|
last_name (str): Bot's last name.
|
||||||
|
@ -80,18 +115,6 @@ class Bot(TelegramObject):
|
||||||
|
|
||||||
return token
|
return token
|
||||||
|
|
||||||
def info(func):
|
|
||||||
|
|
||||||
@functools.wraps(func)
|
|
||||||
def decorator(self, *args, **kwargs):
|
|
||||||
if not self.bot:
|
|
||||||
self.get_me()
|
|
||||||
|
|
||||||
result = func(self, *args, **kwargs)
|
|
||||||
return result
|
|
||||||
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@info
|
@info
|
||||||
def id(self):
|
def id(self):
|
||||||
|
@ -116,19 +139,6 @@ class Bot(TelegramObject):
|
||||||
def name(self):
|
def name(self):
|
||||||
return '@{0}'.format(self.username)
|
return '@{0}'.format(self.username)
|
||||||
|
|
||||||
def log(func):
|
|
||||||
logger = logging.getLogger(func.__module__)
|
|
||||||
|
|
||||||
@functools.wraps(func)
|
|
||||||
def decorator(self, *args, **kwargs):
|
|
||||||
logger.debug('Entering: %s', func.__name__)
|
|
||||||
result = func(self, *args, **kwargs)
|
|
||||||
logger.debug(result)
|
|
||||||
logger.debug('Exiting: %s', func.__name__)
|
|
||||||
return result
|
|
||||||
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
def _message_wrapper(self, url, data, *args, **kwargs):
|
def _message_wrapper(self, url, data, *args, **kwargs):
|
||||||
if kwargs.get('reply_to_message_id'):
|
if kwargs.get('reply_to_message_id'):
|
||||||
data['reply_to_message_id'] = kwargs.get('reply_to_message_id')
|
data['reply_to_message_id'] = kwargs.get('reply_to_message_id')
|
||||||
|
@ -150,15 +160,6 @@ class Bot(TelegramObject):
|
||||||
|
|
||||||
return Message.de_json(result, self)
|
return Message.de_json(result, self)
|
||||||
|
|
||||||
def message(func):
|
|
||||||
|
|
||||||
@functools.wraps(func)
|
|
||||||
def decorator(self, *args, **kwargs):
|
|
||||||
url, data = func(self, *args, **kwargs)
|
|
||||||
return Bot._message_wrapper(self, url, data, *args, **kwargs)
|
|
||||||
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
@log
|
@log
|
||||||
def get_me(self, timeout=None, **kwargs):
|
def get_me(self, timeout=None, **kwargs):
|
||||||
"""A simple method for testing your bot's auth token.
|
"""A simple method for testing your bot's auth token.
|
||||||
|
@ -242,8 +243,7 @@ class Bot(TelegramObject):
|
||||||
return url, data
|
return url, data
|
||||||
|
|
||||||
@log
|
@log
|
||||||
@message
|
def delete_message(self, chat_id, message_id, timeout=None, **kwargs):
|
||||||
def delete_message(self, chat_id, message_id):
|
|
||||||
"""Use this method to delete a message. A message can only be deleted if it was sent less
|
"""Use this method to delete a message. A message can only be deleted if it was sent less
|
||||||
than 48 hours ago. Any such recently sent outgoing message may be deleted. Additionally,
|
than 48 hours ago. Any such recently sent outgoing message may be deleted. Additionally,
|
||||||
if the bot is an administrator in a group chat, it can delete any message. If the bot is
|
if the bot is an administrator in a group chat, it can delete any message. If the bot is
|
||||||
|
@ -257,6 +257,10 @@ class Bot(TelegramObject):
|
||||||
username of the target channel (in the format
|
username of the target channel (in the format
|
||||||
@channelusername).
|
@channelusername).
|
||||||
message_id (int): Unique message identifier.
|
message_id (int): Unique message identifier.
|
||||||
|
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||||
|
from the server (instead of the one specified during creation of the connection
|
||||||
|
pool).
|
||||||
|
**kwargs (dict): Arbitrary keyword arguments.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: On success, `True` is returned.
|
bool: On success, `True` is returned.
|
||||||
|
@ -269,7 +273,9 @@ class Bot(TelegramObject):
|
||||||
|
|
||||||
data = {'chat_id': chat_id, 'message_id': message_id}
|
data = {'chat_id': chat_id, 'message_id': message_id}
|
||||||
|
|
||||||
return url, data
|
result = self._request.post(url, data, timeout=timeout)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
@log
|
@log
|
||||||
@message
|
@message
|
||||||
|
@ -315,6 +321,7 @@ class Bot(TelegramObject):
|
||||||
return url, data
|
return url, data
|
||||||
|
|
||||||
@log
|
@log
|
||||||
|
@message
|
||||||
def send_photo(self,
|
def send_photo(self,
|
||||||
chat_id,
|
chat_id,
|
||||||
photo,
|
photo,
|
||||||
|
@ -356,19 +363,10 @@ class Bot(TelegramObject):
|
||||||
if caption:
|
if caption:
|
||||||
data['caption'] = caption
|
data['caption'] = caption
|
||||||
|
|
||||||
return self._message_wrapper(
|
return url, data
|
||||||
url,
|
|
||||||
data,
|
|
||||||
chat_id=chat_id,
|
|
||||||
photo=photo,
|
|
||||||
caption=caption,
|
|
||||||
disable_notification=disable_notification,
|
|
||||||
reply_to_message_id=reply_to_message_id,
|
|
||||||
reply_markup=reply_markup,
|
|
||||||
timeout=timeout,
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
@log
|
@log
|
||||||
|
@message
|
||||||
def send_audio(self,
|
def send_audio(self,
|
||||||
chat_id,
|
chat_id,
|
||||||
audio,
|
audio,
|
||||||
|
@ -431,22 +429,10 @@ class Bot(TelegramObject):
|
||||||
if caption:
|
if caption:
|
||||||
data['caption'] = caption
|
data['caption'] = caption
|
||||||
|
|
||||||
return self._message_wrapper(
|
return url, data
|
||||||
url,
|
|
||||||
data,
|
|
||||||
chat_id=chat_id,
|
|
||||||
audio=audio,
|
|
||||||
duration=duration,
|
|
||||||
performer=performer,
|
|
||||||
title=title,
|
|
||||||
caption=caption,
|
|
||||||
disable_notification=disable_notification,
|
|
||||||
reply_to_message_id=reply_to_message_id,
|
|
||||||
reply_markup=reply_markup,
|
|
||||||
timeout=timeout,
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
@log
|
@log
|
||||||
|
@message
|
||||||
def send_document(self,
|
def send_document(self,
|
||||||
chat_id,
|
chat_id,
|
||||||
document,
|
document,
|
||||||
|
@ -493,18 +479,7 @@ class Bot(TelegramObject):
|
||||||
if caption:
|
if caption:
|
||||||
data['caption'] = caption
|
data['caption'] = caption
|
||||||
|
|
||||||
return self._message_wrapper(
|
return url, data
|
||||||
url,
|
|
||||||
data,
|
|
||||||
chat_id=chat_id,
|
|
||||||
document=document,
|
|
||||||
filename=filename,
|
|
||||||
caption=caption,
|
|
||||||
disable_notification=disable_notification,
|
|
||||||
reply_to_message_id=reply_to_message_id,
|
|
||||||
reply_markup=reply_markup,
|
|
||||||
timeout=timeout,
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
@log
|
@log
|
||||||
@message
|
@message
|
||||||
|
@ -549,6 +524,7 @@ class Bot(TelegramObject):
|
||||||
return url, data
|
return url, data
|
||||||
|
|
||||||
@log
|
@log
|
||||||
|
@message
|
||||||
def send_video(self,
|
def send_video(self,
|
||||||
chat_id,
|
chat_id,
|
||||||
video,
|
video,
|
||||||
|
@ -595,20 +571,10 @@ class Bot(TelegramObject):
|
||||||
if caption:
|
if caption:
|
||||||
data['caption'] = caption
|
data['caption'] = caption
|
||||||
|
|
||||||
return self._message_wrapper(
|
return url, data
|
||||||
url,
|
|
||||||
data,
|
|
||||||
chat_id=chat_id,
|
|
||||||
video=video,
|
|
||||||
duration=duration,
|
|
||||||
caption=caption,
|
|
||||||
disable_notification=disable_notification,
|
|
||||||
reply_to_message_id=reply_to_message_id,
|
|
||||||
reply_markup=reply_markup,
|
|
||||||
timeout=timeout,
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
@log
|
@log
|
||||||
|
@message
|
||||||
def send_voice(self,
|
def send_voice(self,
|
||||||
chat_id,
|
chat_id,
|
||||||
voice,
|
voice,
|
||||||
|
@ -658,20 +624,10 @@ class Bot(TelegramObject):
|
||||||
if caption:
|
if caption:
|
||||||
data['caption'] = caption
|
data['caption'] = caption
|
||||||
|
|
||||||
return self._message_wrapper(
|
return url, data
|
||||||
url,
|
|
||||||
data,
|
|
||||||
chat_id=chat_id,
|
|
||||||
voice=voice,
|
|
||||||
duration=duration,
|
|
||||||
caption=caption,
|
|
||||||
disable_notification=disable_notification,
|
|
||||||
reply_to_message_id=reply_to_message_id,
|
|
||||||
reply_markup=reply_markup,
|
|
||||||
timeout=timeout,
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
@log
|
@log
|
||||||
|
@message
|
||||||
def send_video_note(self,
|
def send_video_note(self,
|
||||||
chat_id,
|
chat_id,
|
||||||
video_note,
|
video_note,
|
||||||
|
@ -718,18 +674,7 @@ class Bot(TelegramObject):
|
||||||
if length is not None:
|
if length is not None:
|
||||||
data['length'] = length
|
data['length'] = length
|
||||||
|
|
||||||
return self._message_wrapper(
|
return url, data
|
||||||
url,
|
|
||||||
data,
|
|
||||||
chat_id=chat_id,
|
|
||||||
video_note=video_note,
|
|
||||||
duration=duration,
|
|
||||||
length=length,
|
|
||||||
disable_notification=disable_notification,
|
|
||||||
reply_to_message_id=reply_to_message_id,
|
|
||||||
reply_markup=reply_markup,
|
|
||||||
timeout=timeout,
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
@log
|
@log
|
||||||
@message
|
@message
|
||||||
|
@ -898,8 +843,6 @@ class Bot(TelegramObject):
|
||||||
channel (in the format @channelusername).
|
channel (in the format @channelusername).
|
||||||
game_short_name (str): Short name of the game, serves as the unique identifier for the
|
game_short_name (str): Short name of the game, serves as the unique identifier for the
|
||||||
game.
|
game.
|
||||||
|
|
||||||
Keyword Args:
|
|
||||||
disable_notification (Optional[bool]): Sends the message silently. iOS users will not
|
disable_notification (Optional[bool]): Sends the message silently. iOS users will not
|
||||||
receive a notification, Android users will receive a notification with no sound.
|
receive a notification, Android users will receive a notification with no sound.
|
||||||
reply_to_message_id (Optional[int]): If the message is a reply,
|
reply_to_message_id (Optional[int]): If the message is a reply,
|
||||||
|
@ -910,6 +853,7 @@ class Bot(TelegramObject):
|
||||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||||
from the server (instead of the one specified during creation of the connection
|
from the server (instead of the one specified during creation of the connection
|
||||||
pool).
|
pool).
|
||||||
|
**kwargs (dict): Arbitrary keyword arguments.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
:class:`telegram.Message`: On success, the sent message is returned.
|
:class:`telegram.Message`: On success, the sent message is returned.
|
||||||
|
@ -925,7 +869,6 @@ class Bot(TelegramObject):
|
||||||
return url, data
|
return url, data
|
||||||
|
|
||||||
@log
|
@log
|
||||||
@message
|
|
||||||
def send_chat_action(self, chat_id, action, timeout=None, **kwargs):
|
def send_chat_action(self, chat_id, action, timeout=None, **kwargs):
|
||||||
"""Use this method when you need to tell the user that something is happening on the bot's
|
"""Use this method when you need to tell the user that something is happening on the bot's
|
||||||
side. The status is set for 5 seconds or less (when a message arrives from your bot,
|
side. The status is set for 5 seconds or less (when a message arrives from your bot,
|
||||||
|
@ -952,7 +895,9 @@ class Bot(TelegramObject):
|
||||||
|
|
||||||
data = {'chat_id': chat_id, 'action': action}
|
data = {'chat_id': chat_id, 'action': action}
|
||||||
|
|
||||||
return url, data
|
result = self._request.post(url, data, timeout=timeout)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
@log
|
@log
|
||||||
def answer_inline_query(self,
|
def answer_inline_query(self,
|
||||||
|
@ -1698,6 +1643,7 @@ class Bot(TelegramObject):
|
||||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||||
from the server (instead of the one specified during creation of the connection
|
from the server (instead of the one specified during creation of the connection
|
||||||
pool).
|
pool).
|
||||||
|
**kwargs (dict): Arbitrary keyword arguments.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
:class: `telegram.WebhookInfo`
|
:class: `telegram.WebhookInfo`
|
||||||
|
@ -1711,6 +1657,8 @@ class Bot(TelegramObject):
|
||||||
|
|
||||||
return WebhookInfo.de_json(result, self)
|
return WebhookInfo.de_json(result, self)
|
||||||
|
|
||||||
|
@log
|
||||||
|
@message
|
||||||
def set_game_score(self,
|
def set_game_score(self,
|
||||||
user_id,
|
user_id,
|
||||||
score,
|
score,
|
||||||
|
@ -1743,6 +1691,7 @@ class Bot(TelegramObject):
|
||||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||||
from the server (instead of the one specified during creation of the connection
|
from the server (instead of the one specified during creation of the connection
|
||||||
pool).
|
pool).
|
||||||
|
**kwargs (dict): Arbitrary keyword arguments.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
:class:`telegram.Message` or True: The edited message, or if the
|
:class:`telegram.Message` or True: The edited message, or if the
|
||||||
|
@ -1770,12 +1719,9 @@ class Bot(TelegramObject):
|
||||||
else:
|
else:
|
||||||
warnings.warn('edit_message is ignored when disable_edit_message is used')
|
warnings.warn('edit_message is ignored when disable_edit_message is used')
|
||||||
|
|
||||||
result = self._request.post(url, data, timeout=timeout)
|
return url, data
|
||||||
if result is True:
|
|
||||||
return result
|
|
||||||
else:
|
|
||||||
return Message.de_json(result, self)
|
|
||||||
|
|
||||||
|
@log
|
||||||
def get_game_high_scores(self,
|
def get_game_high_scores(self,
|
||||||
user_id,
|
user_id,
|
||||||
chat_id=None,
|
chat_id=None,
|
||||||
|
@ -1797,6 +1743,7 @@ class Bot(TelegramObject):
|
||||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||||
from the server (instead of the one specified during creation of the connection
|
from the server (instead of the one specified during creation of the connection
|
||||||
pool).
|
pool).
|
||||||
|
**kwargs (dict): Arbitrary keyword arguments.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[:class:`telegram.GameHighScore`]: Scores of the specified user and several of his
|
list[:class:`telegram.GameHighScore`]: Scores of the specified user and several of his
|
||||||
|
@ -1927,6 +1874,7 @@ class Bot(TelegramObject):
|
||||||
|
|
||||||
return url, data
|
return url, data
|
||||||
|
|
||||||
|
@log
|
||||||
def answer_shipping_query(self,
|
def answer_shipping_query(self,
|
||||||
shipping_query_id,
|
shipping_query_id,
|
||||||
ok,
|
ok,
|
||||||
|
@ -1950,6 +1898,9 @@ class Bot(TelegramObject):
|
||||||
form that explains why it is impossible to complete the order (e.g. "Sorry,
|
form that explains why it is impossible to complete the order (e.g. "Sorry,
|
||||||
delivery to your desired address is unavailable'). Telegram will display this
|
delivery to your desired address is unavailable'). Telegram will display this
|
||||||
message to the user.
|
message to the user.
|
||||||
|
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||||
|
from the server (instead of the one specified during creation of the connection
|
||||||
|
pool).
|
||||||
**kwargs (dict): Arbitrary keyword arguments.
|
**kwargs (dict): Arbitrary keyword arguments.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
@ -1985,6 +1936,7 @@ class Bot(TelegramObject):
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@log
|
||||||
def answer_pre_checkout_query(self, pre_checkout_query_id, ok,
|
def answer_pre_checkout_query(self, pre_checkout_query_id, ok,
|
||||||
error_message=None, timeout=None, **kwargs):
|
error_message=None, timeout=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -2001,6 +1953,9 @@ class Bot(TelegramObject):
|
||||||
"Sorry, somebody just bought the last of our amazing black T-shirts while you were
|
"Sorry, somebody just bought the last of our amazing black T-shirts while you were
|
||||||
busy filling out your payment details. Please choose a different color or
|
busy filling out your payment details. Please choose a different color or
|
||||||
garment!"). Telegram will display this message to the user.
|
garment!"). Telegram will display this message to the user.
|
||||||
|
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||||
|
from the server (instead of the one specified during creation of the connection
|
||||||
|
pool).
|
||||||
**kwargs (dict): Arbitrary keyword arguments.
|
**kwargs (dict): Arbitrary keyword arguments.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
|
@ -224,7 +224,7 @@ class Request(object):
|
||||||
"""Request an URL.
|
"""Request an URL.
|
||||||
Args:
|
Args:
|
||||||
url (str): The web location we want to retrieve.
|
url (str): The web location we want to retrieve.
|
||||||
data (dict[str, str]): A dict of key/value pairs. Note: On py2.7 value is unicode.
|
data (dict[str, str|int]): A dict of key/value pairs. Note: On py2.7 value is unicode.
|
||||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||||
from the server (instead of the one specified during creation of the connection
|
from the server (instead of the one specified during creation of the connection
|
||||||
pool).
|
pool).
|
||||||
|
|
|
@ -31,6 +31,7 @@ from flaky import flaky
|
||||||
sys.path.append('.')
|
sys.path.append('.')
|
||||||
|
|
||||||
import telegram
|
import telegram
|
||||||
|
from telegram.utils.request import Request
|
||||||
from telegram.error import BadRequest
|
from telegram.error import BadRequest
|
||||||
from tests.base import BaseTest, timeout
|
from tests.base import BaseTest, timeout
|
||||||
|
|
||||||
|
@ -467,6 +468,24 @@ class BotTest(BaseTest, unittest.TestCase):
|
||||||
self.assertEqual(name, message.contact.first_name)
|
self.assertEqual(name, message.contact.first_name)
|
||||||
self.assertEqual(last, message.contact.last_name)
|
self.assertEqual(last, message.contact.last_name)
|
||||||
|
|
||||||
|
def test_timeout_propagation(self):
|
||||||
|
class OkException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class MockRequest(Request):
|
||||||
|
def post(self, url, data, timeout=None):
|
||||||
|
raise OkException(timeout)
|
||||||
|
|
||||||
|
_request = self._bot._request
|
||||||
|
self._bot._request = MockRequest()
|
||||||
|
|
||||||
|
timeout = 500
|
||||||
|
|
||||||
|
with self.assertRaises(OkException) as ok:
|
||||||
|
self._bot.send_photo(self._chat_id, open('tests/data/telegram.jpg'), timeout=timeout)
|
||||||
|
self.assertEqual(ok.exception.args[0], timeout)
|
||||||
|
|
||||||
|
self._bot._request = _request
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
Loading…
Reference in a new issue