mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2025-06-18 19:16:43 +00:00
Fix a couple of inconsistencies in the public interface (#1102)
* Create `_NOT_A_REQUEST` when needed. Currently, modifications in the raised exception would be "global". * `retries` parameters were actually attempts. This has been fixed to actually be the amount of retries, so 0 now means don't retry. * Helper function to deal with retries instead of using a range with different styles every time.
This commit is contained in:
parent
c9e9b82eac
commit
c8f16a4e89
@ -53,7 +53,7 @@ class _TakeoutClient:
|
|||||||
wrapped = []
|
wrapped = []
|
||||||
for r in requests:
|
for r in requests:
|
||||||
if not isinstance(r, TLRequest):
|
if not isinstance(r, TLRequest):
|
||||||
raise _NOT_A_REQUEST
|
raise _NOT_A_REQUEST()
|
||||||
await r.resolve(self, utils)
|
await r.resolve(self, utils)
|
||||||
wrapped.append(functions.InvokeWithTakeoutRequest(takeout_id, r))
|
wrapped.append(functions.InvokeWithTakeoutRequest(takeout_id, r))
|
||||||
|
|
||||||
|
@ -71,23 +71,23 @@ class TelegramBaseClient(abc.ABC):
|
|||||||
invoked requests, and you should use ``asyncio.wait`` or
|
invoked requests, and you should use ``asyncio.wait`` or
|
||||||
``asyncio.wait_for`` for that.
|
``asyncio.wait_for`` for that.
|
||||||
|
|
||||||
request_retries (`int`, optional):
|
request_retries (`int` | `None`, optional):
|
||||||
How many times a request should be retried. Request are retried
|
How many times a request should be retried. Request are retried
|
||||||
when Telegram is having internal issues (due to either
|
when Telegram is having internal issues (due to either
|
||||||
``errors.ServerError`` or ``errors.RpcCallFailError``),
|
``errors.ServerError`` or ``errors.RpcCallFailError``),
|
||||||
when there is a ``errors.FloodWaitError`` less than
|
when there is a ``errors.FloodWaitError`` less than
|
||||||
`flood_sleep_threshold`, or when there's a migrate error.
|
`flood_sleep_threshold`, or when there's a migrate error.
|
||||||
|
|
||||||
May set to a false-y value (``0`` or ``None``) for infinite
|
May take a negative or ``None`` value for infinite retries, but
|
||||||
retries, but this is not recommended, since some requests can
|
this is not recommended, since some requests can always trigger
|
||||||
always trigger a call fail (such as searching for messages).
|
a call fail (such as searching for messages).
|
||||||
|
|
||||||
connection_retries (`int`, optional):
|
connection_retries (`int` | `None`, optional):
|
||||||
How many times the reconnection should retry, either on the
|
How many times the reconnection should retry, either on the
|
||||||
initial connection or when Telegram disconnects us. May be
|
initial connection or when Telegram disconnects us. May be
|
||||||
set to a false-y value (``0`` or ``None``) for infinite
|
set to a negative or ``None`` value for infinite retries, but
|
||||||
retries, but this is not recommended, since the program can
|
this is not recommended, since the program can get stuck in an
|
||||||
get stuck in an infinite loop.
|
infinite loop.
|
||||||
|
|
||||||
retry_delay (`int` | `float`, optional):
|
retry_delay (`int` | `float`, optional):
|
||||||
The delay in seconds to sleep between automatic reconnections.
|
The delay in seconds to sleep between automatic reconnections.
|
||||||
@ -236,8 +236,8 @@ class TelegramBaseClient(abc.ABC):
|
|||||||
self.api_id = int(api_id)
|
self.api_id = int(api_id)
|
||||||
self.api_hash = api_hash
|
self.api_hash = api_hash
|
||||||
|
|
||||||
self._request_retries = request_retries or sys.maxsize
|
self._request_retries = request_retries
|
||||||
self._connection_retries = connection_retries or sys.maxsize
|
self._connection_retries = connection_retries
|
||||||
self._retry_delay = retry_delay or 0
|
self._retry_delay = retry_delay or 0
|
||||||
self._proxy = proxy
|
self._proxy = proxy
|
||||||
self._timeout = timeout
|
self._timeout = timeout
|
||||||
|
@ -6,8 +6,9 @@ from .telegrambaseclient import TelegramBaseClient
|
|||||||
from .. import errors, utils
|
from .. import errors, utils
|
||||||
from ..errors import MultiError, RPCError
|
from ..errors import MultiError, RPCError
|
||||||
from ..tl import TLObject, TLRequest, types, functions
|
from ..tl import TLObject, TLRequest, types, functions
|
||||||
|
from ..helpers import retry_range
|
||||||
|
|
||||||
_NOT_A_REQUEST = TypeError('You can only invoke requests, not types!')
|
_NOT_A_REQUEST = lambda: TypeError('You can only invoke requests, not types!')
|
||||||
|
|
||||||
|
|
||||||
class UserMethods(TelegramBaseClient):
|
class UserMethods(TelegramBaseClient):
|
||||||
@ -15,7 +16,7 @@ class UserMethods(TelegramBaseClient):
|
|||||||
requests = (request if utils.is_list_like(request) else (request,))
|
requests = (request if utils.is_list_like(request) else (request,))
|
||||||
for r in requests:
|
for r in requests:
|
||||||
if not isinstance(r, TLRequest):
|
if not isinstance(r, TLRequest):
|
||||||
raise _NOT_A_REQUEST
|
raise _NOT_A_REQUEST()
|
||||||
await r.resolve(self, utils)
|
await r.resolve(self, utils)
|
||||||
|
|
||||||
# Avoid making the request if it's already in a flood wait
|
# Avoid making the request if it's already in a flood wait
|
||||||
@ -34,7 +35,7 @@ class UserMethods(TelegramBaseClient):
|
|||||||
|
|
||||||
request_index = 0
|
request_index = 0
|
||||||
self._last_request = time.time()
|
self._last_request = time.time()
|
||||||
for _ in range(self._request_retries):
|
for attempt in retry_range(self._request_retries):
|
||||||
try:
|
try:
|
||||||
future = self._sender.send(request, ordered=ordered)
|
future = self._sender.send(request, ordered=ordered)
|
||||||
if isinstance(future, list):
|
if isinstance(future, list):
|
||||||
@ -86,7 +87,8 @@ class UserMethods(TelegramBaseClient):
|
|||||||
raise
|
raise
|
||||||
await self._switch_dc(e.new_dc)
|
await self._switch_dc(e.new_dc)
|
||||||
|
|
||||||
raise ValueError('Number of retries reached 0')
|
raise ValueError('Request was unsuccessful {} time(s)'
|
||||||
|
.format(attempt))
|
||||||
|
|
||||||
# region Public methods
|
# region Public methods
|
||||||
|
|
||||||
|
@ -72,6 +72,21 @@ def strip_text(text, entities):
|
|||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def retry_range(retries):
|
||||||
|
"""
|
||||||
|
Generates an integer sequence starting from 1. If `retries` is
|
||||||
|
negative or ``None`` then sequence is infinite, otherwise it will
|
||||||
|
end at `retries + 1`.
|
||||||
|
"""
|
||||||
|
yield 1
|
||||||
|
if retries is None:
|
||||||
|
retries = -1
|
||||||
|
attempt = 0
|
||||||
|
while attempt != retries:
|
||||||
|
attempt += 1
|
||||||
|
yield 1 + attempt
|
||||||
|
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
# region Cryptographic related utils
|
# region Cryptographic related utils
|
||||||
|
@ -22,6 +22,7 @@ from ..tl.types import (
|
|||||||
MsgsStateInfo, MsgsAllInfo, MsgResendReq, upload
|
MsgsStateInfo, MsgsAllInfo, MsgResendReq, upload
|
||||||
)
|
)
|
||||||
from ..crypto import AuthKey
|
from ..crypto import AuthKey
|
||||||
|
from ..helpers import retry_range
|
||||||
|
|
||||||
|
|
||||||
def _cancellable(func):
|
def _cancellable(func):
|
||||||
@ -211,26 +212,27 @@ class MTProtoSender:
|
|||||||
receive loops.
|
receive loops.
|
||||||
"""
|
"""
|
||||||
self._log.info('Connecting to %s...', self._connection)
|
self._log.info('Connecting to %s...', self._connection)
|
||||||
for retry in range(1, self._retries + 1):
|
for attempt in retry_range(self._retries):
|
||||||
try:
|
try:
|
||||||
self._log.debug('Connection attempt {}...'.format(retry))
|
self._log.debug('Connection attempt {}...'.format(attempt))
|
||||||
await self._connection.connect(timeout=self._connect_timeout)
|
await self._connection.connect(timeout=self._connect_timeout)
|
||||||
except (ConnectionError, asyncio.TimeoutError) as e:
|
except (ConnectionError, asyncio.TimeoutError) as e:
|
||||||
self._log.warning('Attempt {} at connecting failed: {}: {}'
|
self._log.warning('Attempt {} at connecting failed: {}: {}'
|
||||||
.format(retry, type(e).__name__, e))
|
.format(attempt, type(e).__name__, e))
|
||||||
await asyncio.sleep(self._delay)
|
await asyncio.sleep(self._delay)
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
raise ConnectionError('Connection to Telegram failed {} times'
|
raise ConnectionError('Connection to Telegram failed {} time(s)'
|
||||||
.format(self._retries))
|
.format(attempt))
|
||||||
|
|
||||||
self._log.debug('Connection success!')
|
self._log.debug('Connection success!')
|
||||||
if not self.auth_key:
|
if not self.auth_key:
|
||||||
plain = MTProtoPlainSender(self._connection, loggers=self._loggers)
|
plain = MTProtoPlainSender(self._connection, loggers=self._loggers)
|
||||||
for retry in range(1, self._retries + 1):
|
for attempt in retry_range(self._retries):
|
||||||
try:
|
try:
|
||||||
self._log.debug('New auth_key attempt {}...'.format(retry))
|
self._log.debug('New auth_key attempt {}...'
|
||||||
|
.format(attempt))
|
||||||
self.auth_key.key, self._state.time_offset =\
|
self.auth_key.key, self._state.time_offset =\
|
||||||
await authenticator.do_authentication(plain)
|
await authenticator.do_authentication(plain)
|
||||||
|
|
||||||
@ -244,11 +246,11 @@ class MTProtoSender:
|
|||||||
break
|
break
|
||||||
except (SecurityError, AssertionError) as e:
|
except (SecurityError, AssertionError) as e:
|
||||||
self._log.warning('Attempt {} at new auth_key failed: {}'
|
self._log.warning('Attempt {} at new auth_key failed: {}'
|
||||||
.format(retry, e))
|
.format(attempt, e))
|
||||||
await asyncio.sleep(self._delay)
|
await asyncio.sleep(self._delay)
|
||||||
else:
|
else:
|
||||||
e = ConnectionError('auth_key generation failed {} times'
|
e = ConnectionError('auth_key generation failed {} time(s)'
|
||||||
.format(self._retries))
|
.format(attempt))
|
||||||
self._disconnect(error=e)
|
self._disconnect(error=e)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
@ -321,17 +323,17 @@ class MTProtoSender:
|
|||||||
self._state.reset()
|
self._state.reset()
|
||||||
|
|
||||||
retries = self._retries if self._auto_reconnect else 0
|
retries = self._retries if self._auto_reconnect else 0
|
||||||
for retry in range(1, retries + 1):
|
for attempt in retry_range(retries):
|
||||||
try:
|
try:
|
||||||
await self._connect()
|
await self._connect()
|
||||||
except (ConnectionError, asyncio.TimeoutError) as e:
|
except (ConnectionError, asyncio.TimeoutError) as e:
|
||||||
self._log.info('Failed reconnection retry %d/%d with %s',
|
self._log.info('Failed reconnection attempt %d with %s',
|
||||||
retry, retries, e.__class__.__name__)
|
attempt, e.__class__.__name__)
|
||||||
|
|
||||||
await asyncio.sleep(self._delay)
|
await asyncio.sleep(self._delay)
|
||||||
except Exception:
|
except Exception:
|
||||||
self._log.exception('Unexpected exception reconnecting on '
|
self._log.exception('Unexpected exception reconnecting on '
|
||||||
'retry %d/%d', retry, retries)
|
'attempt %d', attempt)
|
||||||
|
|
||||||
await asyncio.sleep(self._delay)
|
await asyncio.sleep(self._delay)
|
||||||
else:
|
else:
|
||||||
@ -343,7 +345,8 @@ class MTProtoSender:
|
|||||||
|
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
self._log.error('Failed to reconnect automatically.')
|
self._log.error('Automatic reconnection failed {} time(s)'
|
||||||
|
.format(attempt))
|
||||||
self._disconnect(error=ConnectionError())
|
self._disconnect(error=ConnectionError())
|
||||||
|
|
||||||
def _start_reconnect(self):
|
def _start_reconnect(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user