mirror of
				https://github.com/LonamiWebs/Telethon.git
				synced 2025-11-01 00:17:47 +03: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