Implement a mechanism to notify of connection failures (#852)

This commit is contained in:
Dan Elkouby 2018-06-17 20:29:41 +03:00 committed by Lonami
parent d9d586171f
commit d5b349e031
3 changed files with 44 additions and 3 deletions

View File

@ -218,6 +218,14 @@ class TelegramBaseClient(abc.ABC):
def loop(self):
return self._loop
@property
def disconnected(self):
"""
Future that resolves when the connection to Telegram
ends, either by user action or in the background.
"""
return self._sender.disconnected
# endregion
# region Connecting

View File

@ -14,6 +14,19 @@ class UpdateMethods(UserMethods):
# region Public methods
def run_until_disconnected(self):
"""
Runs the event loop until `disconnect` is called or if an error
while connecting/sending/receiving occurs in the background. In
the latter case, said error will ``raise`` so you have a chance
to ``except`` it on your own code.
This method shouldn't be called from ``async def`` as the loop
will be running already. Use ``await client.disconnected`` in
this situation instead.
"""
self.loop.run_until_complete(self.disconnected)
def on(self, event):
"""
Decorator helper method around add_event_handler().

View File

@ -63,6 +63,7 @@ class MTProtoSender:
# pending futures should be cancelled.
self._user_connected = False
self._reconnecting = False
self._disconnected = None
# We need to join the loops upon disconnection
self._send_loop_handle = None
@ -157,6 +158,10 @@ class MTProtoSender:
self._recv_loop_handle.cancel()
__log__.info('Disconnection from {} complete!'.format(self._ip))
if error:
self._disconnected.set_exception(error)
else:
self._disconnected.set_result(None)
def send(self, request, ordered=False):
"""
@ -199,6 +204,17 @@ class MTProtoSender:
self._send_queue.put_nowait(message)
return message.future
@property
def disconnected(self):
"""
Future that resolves when the connection to Telegram
ends, either by user action or in the background.
"""
if self._disconnected is not None:
return self._disconnected
else:
raise ConnectionError('Sender was never connected')
# Private methods
async def _connect(self):
@ -235,9 +251,10 @@ class MTProtoSender:
else:
break
else:
await self._disconnect()
raise ConnectionError('auth_key generation failed {} times'
.format(self._retries))
e = ConnectionError('auth_key generation failed {} times'
.format(self._retries))
await self._disconnect(error=e)
raise e
__log__.debug('Starting send loop')
self._send_loop_handle = self._loop.create_task(self._send_loop())
@ -245,6 +262,9 @@ class MTProtoSender:
__log__.debug('Starting receive loop')
self._recv_loop_handle = self._loop.create_task(self._recv_loop())
# First connection or manual reconnection after a failure
if self._disconnected is None or self._disconnected.done():
self._disconnected = asyncio.Future()
__log__.info('Connection to {} complete!'.format(self._ip))
async def _reconnect(self):