More robust cleanup for Client.run.
This should prevent asyncio.CancelledError from being propagated more and suppressed "Task was destroyed but was pending!" warnings when doing graceful closes outside of using a KeyboardInterrupt. To make clean up a bit more robust, also add signal handlers for POSIX systems.
This commit is contained in:
parent
9fcbe5c678
commit
9885a946e1
@ -45,6 +45,7 @@ import logging, traceback
|
|||||||
import sys, re, io
|
import sys, re, io
|
||||||
import itertools
|
import itertools
|
||||||
import datetime
|
import datetime
|
||||||
|
import signal
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from os.path import split as path_split
|
from os.path import split as path_split
|
||||||
|
|
||||||
@ -207,7 +208,7 @@ class Client:
|
|||||||
pass
|
pass
|
||||||
except Exception:
|
except Exception:
|
||||||
try:
|
try:
|
||||||
yield from self.on_error(event_name, *args, **kwargs)
|
yield from asyncio.shield(self.on_error(event_name, *args, **kwargs))
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -453,6 +454,23 @@ class Client:
|
|||||||
yield from self.login(*args, bot=bot)
|
yield from self.login(*args, bot=bot)
|
||||||
yield from self.connect(reconnect=reconnect)
|
yield from self.connect(reconnect=reconnect)
|
||||||
|
|
||||||
|
|
||||||
|
def _do_cleanup(self):
|
||||||
|
self.loop.run_until_complete(self.close())
|
||||||
|
pending = asyncio.Task.all_tasks(loop=self.loop)
|
||||||
|
if pending:
|
||||||
|
log.info('Cleaning up after %s tasks', len(pending))
|
||||||
|
gathered = asyncio.gather(*pending, loop=self.loop)
|
||||||
|
try:
|
||||||
|
gathered.cancel()
|
||||||
|
self.loop.run_until_complete(gathered)
|
||||||
|
|
||||||
|
# we want to retrieve any exceptions to make sure that
|
||||||
|
# they don't nag us about it being un-retrieved.
|
||||||
|
gathered.exception()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
def run(self, *args, **kwargs):
|
def run(self, *args, **kwargs):
|
||||||
"""A blocking call that abstracts away the `event loop`_
|
"""A blocking call that abstracts away the `event loop`_
|
||||||
initialisation from you.
|
initialisation from you.
|
||||||
@ -477,23 +495,16 @@ class Client:
|
|||||||
is blocking. That means that registration of events or anything being
|
is blocking. That means that registration of events or anything being
|
||||||
called after this function call will not execute until it returns.
|
called after this function call will not execute until it returns.
|
||||||
"""
|
"""
|
||||||
|
if sys.platform != 'win32':
|
||||||
|
self.loop.add_signal_handler(signal.SIGINT, self._do_cleanup)
|
||||||
|
self.loop.add_signal_handler(signal.SIGTERM, self._do_cleanup)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.loop.run_until_complete(self.start(*args, **kwargs))
|
self.loop.run_until_complete(self.start(*args, **kwargs))
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
self.loop.run_until_complete(self.logout())
|
pass
|
||||||
pending = asyncio.Task.all_tasks(loop=self.loop)
|
|
||||||
gathered = asyncio.gather(*pending, loop=self.loop)
|
|
||||||
try:
|
|
||||||
gathered.cancel()
|
|
||||||
self.loop.run_until_complete(gathered)
|
|
||||||
|
|
||||||
# we want to retrieve any exceptions to make sure that
|
|
||||||
# they don't nag us about it being un-retrieved.
|
|
||||||
gathered.exception()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
finally:
|
finally:
|
||||||
|
self._do_cleanup()
|
||||||
self.loop.close()
|
self.loop.close()
|
||||||
|
|
||||||
# properties
|
# properties
|
||||||
|
@ -47,6 +47,8 @@ def wrap_callback(coro):
|
|||||||
ret = yield from coro(*args, **kwargs)
|
ret = yield from coro(*args, **kwargs)
|
||||||
except CommandError:
|
except CommandError:
|
||||||
raise
|
raise
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
return
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise CommandInvokeError(e) from e
|
raise CommandInvokeError(e) from e
|
||||||
return ret
|
return ret
|
||||||
@ -60,6 +62,8 @@ def hooked_wrapped_callback(command, ctx, coro):
|
|||||||
ret = yield from coro(*args, **kwargs)
|
ret = yield from coro(*args, **kwargs)
|
||||||
except CommandError:
|
except CommandError:
|
||||||
raise
|
raise
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
return
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise CommandInvokeError(e) from e
|
raise CommandInvokeError(e) from e
|
||||||
finally:
|
finally:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user