Browse Source

Allow for optional custom error handler in tasks extension

pull/2634/head
Josh B 5 years ago
committed by Rapptz
parent
commit
20854de080
  1. 44
      discord/ext/tasks/__init__.py

44
discord/ext/tasks/__init__.py

@ -5,6 +5,8 @@ import websockets
import discord import discord
import inspect import inspect
import logging import logging
import sys
import traceback
from discord.backoff import ExponentialBackoff from discord.backoff import ExponentialBackoff
@ -50,15 +52,15 @@ class Loop:
if not inspect.iscoroutinefunction(self.coro): if not inspect.iscoroutinefunction(self.coro):
raise TypeError('Expected coroutine function, not {0.__name__!r}.'.format(type(self.coro))) raise TypeError('Expected coroutine function, not {0.__name__!r}.'.format(type(self.coro)))
async def _call_loop_function(self, name): async def _call_loop_function(self, name, *args, **kwargs):
coro = getattr(self, '_' + name) coro = getattr(self, '_' + name)
if coro is None: if coro is None:
return return
if self._injected is not None: if self._injected is not None:
await coro(self._injected) await coro(self._injected, *args, **kwargs)
else: else:
await coro() await coro(*args, **kwargs)
async def _loop(self, *args, **kwargs): async def _loop(self, *args, **kwargs):
backoff = ExponentialBackoff() backoff = ExponentialBackoff()
@ -89,10 +91,10 @@ class Loop:
except asyncio.CancelledError: except asyncio.CancelledError:
self._is_being_cancelled = True self._is_being_cancelled = True
raise raise
except Exception: except Exception as exc:
self._has_failed = True self._has_failed = True
log.exception('Internal background task failed.') await self._call_loop_function('error', exc)
raise raise exc
finally: finally:
await self._call_loop_function('after_loop') await self._call_loop_function('after_loop')
self._is_being_cancelled = False self._is_being_cancelled = False
@ -283,6 +285,10 @@ class Loop:
""" """
return not bool(self._task.done()) if self._task else False return not bool(self._task.done()) if self._task else False
async def _error(self, exception):
print('Unhandled exception in internal background task {0.__name__!r}.'.format(self.coro), file=sys.stderr)
traceback.print_exception(type(exception), exception, exception.__traceback__, file=sys.stderr)
def before_loop(self, coro): def before_loop(self, coro):
"""A decorator that registers a coroutine to be called before the loop starts running. """A decorator that registers a coroutine to be called before the loop starts running.
@ -336,6 +342,32 @@ class Loop:
self._after_loop = coro self._after_loop = coro
return coro return coro
def error(self, coro):
"""A decorator that registers a coroutine to be called if the task encounters an unhandled exception.
The coroutine must take only one argument the exception raised (except ``self`` in a class context).
By default this prints to :data:`sys.stderr` however it could be
overridden to have a different implementation.
.. versionadded:: 1.4
Parameters
------------
coro: :ref:`coroutine <coroutine>`
The coroutine to register in the event of an unhandled exception.
Raises
-------
TypeError
The function was not a coroutine.
"""
if not inspect.iscoroutinefunction(coro):
raise TypeError('Expected coroutine function, received {0.__name__!r}.'.format(type(coro)))
self._error = coro
return coro
def _get_next_sleep_time(self): def _get_next_sleep_time(self):
return self._last_iteration + datetime.timedelta(seconds=self._sleep) return self._last_iteration + datetime.timedelta(seconds=self._sleep)

Loading…
Cancel
Save