Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create a wrapper over discord.ext.tasks.loop #186

Open
wookie184 opened this issue Jul 8, 2023 · 1 comment
Open

Create a wrapper over discord.ext.tasks.loop #186

wookie184 opened this issue Jul 8, 2023 · 1 comment

Comments

@wookie184
Copy link
Contributor

discord.ext.tasks.loop automatically reruns tasks by default on some errors. The implementation doesn't seem great though because:

  • The error is never logged.
  • There is no limit on the number of times it will retry.
  • The error may not be something that will be fixed by retrying.

A solution would be to default to not reconnecting. A util in botcore gives us more flexibility to change how things work in the future (e.g. error handling, custom retry logic in some cases, etc).

@shtlrs
Copy link
Member

shtlrs commented Jul 9, 2023

We'll need to define exactly what we want to do with the exception.

The thing is, our error handler handles errors raised inside commands, which isn't the case for tasks.

We could generate a "fake context" then dispatch that to the error handler cog.
But it comes with:

  • An signature that needs a bot instance, which is a bit ugly when it comes to usage compared to the previous loop function, since we have to do is either as
from bot import instance as bot_instance
....
@tasks.loop(bot=bot_instance, seconds=20)
async def my_task(self)
     ....

OR

class MyCog():

    def __init__(self, bot):
        self.task = loop(bot=bot, secondes=10)
        self.task.start()

    async def my_task(self):
        ....

What we could also do is override the Loop class' on_error callback and define the "generic" exception handling there.

We'd then have, in pydis_core.ext.tasks

from discord.ext.tasks import Loop as BaseLoop, LF
from discord.utils import MISSING
from typing import Any, Callable, Optional, Union, Sequence, Generic
from datetime import datetime
from pydis_core.utils.logging import get_logger

logger = get_logger(__name__)


class Loop(BaseLoop, Generic[LF]):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    async def _error(self, *args: Any) -> None:
        exception: Exception = args[-1]
       # Define what we want to do here.
        logger.exception(
            msg=f"Task: {self.coro.__qualname__} threw an unexpected {exception.__class__.__name__} exception.",
            exc_info=exception
        )
        return


def loop(
    *,
    seconds: float = MISSING,
    minutes: float = MISSING,
    hours: float = MISSING,
    time: Union[datetime.time, Sequence[datetime.time]] = MISSING,
    count: Optional[int] = None,
    reconnect: bool = True,
) -> Callable[[LF], Loop[LF]]:

    def decorator(func: LF) -> Loop[LF]:
        return Loop[LF](
            coro=func,
            seconds=seconds,
            minutes=minutes,
            hours=hours,
            count=count,
            time=time,
            reconnect=reconnect,
        )

    return decorator

And then, in some cog

from pydis_core.ext.tasks import loop

class MyCog(Cog):

    @loop(minute=5)
    async def my_task(self):
         # body goes here
2023-07-09 01:11:59 | pydis_core.exts.tasks | ERROR | Task: MyCog.my_task threw an unexpected {{ExceptionClass}} exception.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants