From 5439a67056caa293b6218c56b1c84ab39f34b9fb Mon Sep 17 00:00:00 2001 From: Sebastian Law Date: Sat, 5 Mar 2022 02:12:22 -0800 Subject: [PATCH] [tasks] Fix sleep handling behaviour depending on interval type Relative time intervals can be thought of as: for _ in range(count): await body() await asyncio.sleep(interval) While explicit time intervals should be thought of as: times = [1pm, 2pm, 3pm, 12am] current = 0 for _ in range(count): time = times.wrapping_index(current) # magic to wrap around await utils.sleep_until(time) await body() current += 1 --- discord/ext/tasks/__init__.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/discord/ext/tasks/__init__.py b/discord/ext/tasks/__init__.py index 84cc1eaeb..cc4b5f28d 100644 --- a/discord/ext/tasks/__init__.py +++ b/discord/ext/tasks/__init__.py @@ -160,9 +160,14 @@ class Loop(Generic[LF]): self._next_iteration = self._get_next_sleep_time() else: self._next_iteration = datetime.datetime.now(datetime.timezone.utc) + await asyncio.sleep(0) # allows canceling in before_loop try: - await self._try_sleep_until(self._next_iteration) + if self._stop_next_iteration: # allow calling stop() before first iteration + return while True: + # sleep before the body of the task for explicit time intervals + if self._time is not MISSING: + await self._try_sleep_until(self._next_iteration) if not self._last_iteration_failed: self._last_iteration = self._next_iteration self._next_iteration = self._get_next_sleep_time() @@ -178,7 +183,9 @@ class Loop(Generic[LF]): if self._stop_next_iteration: return - await self._try_sleep_until(self._next_iteration) + # sleep after the body of the task for relative time intervals + if self._time is MISSING: + await self._try_sleep_until(self._next_iteration) self._current_loop += 1 if self._current_loop == self.count: @@ -346,6 +353,9 @@ class Loop(Generic[LF]): before stopping via :meth:`clear_exception_types` or use :meth:`cancel` instead. + .. versionchanged:: 2.0 + Calling this method in :meth:`before_loop` will stop the first iteration from running. + .. versionadded:: 1.2 """ if self._task is not MISSING and not self._task.done(): @@ -473,6 +483,9 @@ class Loop(Generic[LF]): The coroutine must take no arguments (except ``self`` in a class context). + .. versionchanged:: 2.0 + Calling :meth:`stop` in this coroutine will stop the initial iteration from running. + Parameters ------------ coro: :ref:`coroutine `