We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[tasks] Run-time changing of the sleep interval
### The Problem

No way to change the sleep interval at run-time.

### The Ideal Solution

Something like `task.change_interval(seconds=20.0)`. 

It should apply on the next loop. If someone wants to do it right away regardless of the current loop they can cancel it, change the interval, then start it again.

### The Current Solution

None

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `discord/ext/tasks/__init__.py`
Content:
```
1 import asyncio
2 import aiohttp
3 import websockets
4 import discord
5 import inspect
6 import logging
7 
8 from discord.backoff import ExponentialBackoff
9 
10 MAX_ASYNCIO_SECONDS = 3456000
11 
12 log = logging.getLogger(__name__)
13 
14 class Loop:
15     """A background task helper that abstracts the loop and reconnection logic for you.
16 
17     The main interface to create this is through :func:`loop`.
18     """
19     def __init__(self, coro, seconds, hours, minutes, count, reconnect, loop):
20         self.coro = coro
21         self.seconds = seconds
22         self.hours = hours
23         self.minutes = minutes
24         self.reconnect = reconnect
25         self.loop = loop or asyncio.get_event_loop()
26         self.count = count
27         self._current_loop = 0
28         self._task = None
29         self._injected = None
30         self._valid_exception = (
31             OSError,
32             discord.HTTPException,
33             discord.GatewayNotFound,
34             discord.ConnectionClosed,
35             aiohttp.ClientError,
36             asyncio.TimeoutError,
37             websockets.InvalidHandshake,
38             websockets.WebSocketProtocolError,
39         )
40 
41         self._before_loop = None
42         self._after_loop = None
43         self._is_being_cancelled = False
44         self._has_failed = False
45         self._stop_next_iteration = False
46 
47         if self.count is not None and self.count <= 0:
48             raise ValueError('count must be greater than 0 or None.')
49 
50         self._sleep = sleep = self.seconds + (self.minutes * 60.0) + (self.hours * 3600.0)
51         if sleep >= MAX_ASYNCIO_SECONDS:
52             fmt = 'Total number of seconds exceeds asyncio imposed limit of {0} seconds.'
53             raise ValueError(fmt.format(MAX_ASYNCIO_SECONDS))
54 
55         if sleep < 0:
56             raise ValueError('Total number of seconds cannot be less than zero.')
57 
58         if not inspect.iscoroutinefunction(self.coro):
59             raise TypeError('Expected coroutine function, not {0.__name__!r}.'.format(type(self.coro)))
60 
61     async def _call_loop_function(self, name):
62         coro = getattr(self, '_' + name)
63         if coro is None:
64             return
65 
66         if self._injected is not None:
67             await coro(self._injected)
68         else:
69             await coro()
70 
71     async def _loop(self, *args, **kwargs):
72         backoff = ExponentialBackoff()
73         await self._call_loop_function('before_loop')
74         try:
75             while True:
76                 try:
77                     await self.coro(*args, **kwargs)
78                 except self._valid_exception as exc:
79                     if not self.reconnect:
80                         raise
81                     await asyncio.sleep(backoff.delay())
82                 else:
83                     if self._stop_next_iteration:
84                         return
85                     self._current_loop += 1
86                     if self._current_loop == self.count:
87                         break
88 
89                     await asyncio.sleep(self._sleep)
90         except asyncio.CancelledError:
91             self._is_being_cancelled = True
92             raise
93         except Exception:
94             self._has_failed = True
95             log.exception('Internal background task failed.')
96             raise
97         finally:
98             await self._call_loop_function('after_loop')
99             self._is_being_cancelled = False
100             self._current_loop = 0
101             self._stop_next_iteration = False
102             self._has_failed = False
103 
104     def __get__(self, obj, objtype):
105         if obj is None:
106             return self
107         self._injected = obj
108         return self
109 
110     @property
111     def current_loop(self):
112         """:class:`int`: The current iteration of the loop."""
113         return self._current_loop
114 
115     def start(self, *args, **kwargs):
116         r"""Starts the internal task in the event loop.
117 
118         Parameters
119         ------------
120         \*args
121             The arguments to to use.
122         \*\*kwargs
123             The keyword arguments to use.
124 
125         Raises
126         --------
127         RuntimeError
128             A task has already been launched and is running.
129 
130         Returns
131         ---------
132         :class:`asyncio.Task`
133             The task that has been created.
134         """
135 
136         if self._task is not None and not self._task.done():
137             raise RuntimeError('Task is already launched and is not completed.')
138 
139         if self._injected is not None:
140             args = (self._injected, *args)
141 
142         self._task = self.loop.create_task(self._loop(*args, **kwargs))
143         return self._task
144 
145     def stop(self):
146         r"""Gracefully stops the task from running.
147 
148         Unlike :meth:`cancel`\, this allows the task to finish its
149         current iteration before gracefully exiting.
150 
151         .. note::
152 
153             If the internal function raises an error that can be
154             handled before finishing then it will retry until
155             it succeeds.
156 
157             If this is undesirable, either remove the error handling
158             before stopping via :meth:`clear_exception_types` or
159             use :meth:`cancel` instead.
160 
161         .. versionadded:: 1.2.0
162         """
163         if self._task and not self._task.done():
164             self._stop_next_iteration = True
165 
166     def _can_be_cancelled(self):
167         return not self._is_being_cancelled and self._task and not self._task.done()
168 
169     def cancel(self):
170         """Cancels the internal task, if it is running."""
171         if self._can_be_cancelled():
172             self._task.cancel()
173 
174     def restart(self, *args, **kwargs):
175         r"""A convenience method to restart the internal task.
176 
177         .. note::
178 
179             Due to the way this function works, the task is not
180             returned like :meth:`start`.
181 
182         Parameters
183         ------------
184         \*args
185             The arguments to to use.
186         \*\*kwargs
187             The keyword arguments to use.
188         """
189 
190         def restart_when_over(fut, *, args=args, kwargs=kwargs):
191             self._task.remove_done_callback(restart_when_over)
192             self.start(*args, **kwargs)
193 
194         if self._can_be_cancelled():
195             self._task.add_done_callback(restart_when_over)
196             self._task.cancel()
197 
198     def add_exception_type(self, exc):
199         r"""Adds an exception type to be handled during the reconnect logic.
200 
201         By default the exception types handled are those handled by
202         :meth:`discord.Client.connect`\, which includes a lot of internet disconnection
203         errors.
204 
205         This function is useful if you're interacting with a 3rd party library that
206         raises its own set of exceptions.
207 
208         Parameters
209         ------------
210         exc: Type[:class:`BaseException`]
211             The exception class to handle.
212 
213         Raises
214         --------
215         TypeError
216             The exception passed is either not a class or not inherited from :class:`BaseException`.
217         """
218 
219         if not inspect.isclass(exc):
220             raise TypeError('{0!r} must be a class.'.format(exc))
221         if not issubclass(exc, BaseException):
222             raise TypeError('{0!r} must inherit from BaseException.'.format(exc))
223 
224         self._valid_exception = (*self._valid_exception, exc)
225 
226     def clear_exception_types(self):
227         """Removes all exception types that are handled.
228 
229         .. note::
230 
231             This operation obviously cannot be undone!
232         """
233         self._valid_exception = tuple()
234 
235     def remove_exception_type(self, exc):
236         """Removes an exception type from being handled during the reconnect logic.
237 
238         Parameters
239         ------------
240         exc: Type[:class:`BaseException`]
241             The exception class to handle.
242 
243         Returns
244         ---------
245         :class:`bool`
246             Whether it was successfully removed.
247         """
248         old_length = len(self._valid_exception)
249         self._valid_exception = tuple(x for x in self._valid_exception if x is not exc)
250         return len(self._valid_exception) != old_length
251 
252     def get_task(self):
253         """Optional[:class:`asyncio.Task`]: Fetches the internal task or ``None`` if there isn't one running."""
254         return self._task
255 
256     def is_being_cancelled(self):
257         """:class:`bool`: Whether the task is being cancelled."""
258         return self._is_being_cancelled
259 
260     def failed(self):
261         """:class:`bool`: Whether the internal task has failed.
262 
263         .. versionadded:: 1.2.0
264         """
265         return self._has_failed
266 
267     def before_loop(self, coro):
268         """A decorator that registers a coroutine to be called before the loop starts running.
269 
270         This is useful if you want to wait for some bot state before the loop starts,
271         such as :meth:`discord.Client.wait_until_ready`.
272 
273         The coroutine must take no arguments (except ``self`` in a class context).
274 
275         Parameters
276         ------------
277         coro: :ref:`coroutine <coroutine>`
278             The coroutine to register before the loop runs.
279 
280         Raises
281         -------
282         TypeError
283             The function was not a coroutine.
284         """
285 
286         if not inspect.iscoroutinefunction(coro):
287             raise TypeError('Expected coroutine function, received {0.__name__!r}.'.format(type(coro)))
288 
289         self._before_loop = coro
290         return coro
291 
292     def after_loop(self, coro):
293         """A decorator that register a coroutine to be called after the loop finished running.
294 
295         The coroutine must take no arguments (except ``self`` in a class context).
296 
297         .. note::
298 
299             This coroutine is called even during cancellation. If it is desirable
300             to tell apart whether something was cancelled or not, check to see
301             whether :meth:`is_being_cancelled` is ``True`` or not.
302 
303         Parameters
304         ------------
305         coro: :ref:`coroutine <coroutine>`
306             The coroutine to register after the loop finishes.
307 
308         Raises
309         -------
310         TypeError
311             The function was not a coroutine.
312         """
313 
314         if not inspect.iscoroutinefunction(coro):
315             raise TypeError('Expected coroutine function, received {0.__name__!r}.'.format(type(coro)))
316 
317         self._after_loop = coro
318         return coro
319 
320 def loop(*, seconds=0, minutes=0, hours=0, count=None, reconnect=True, loop=None):
321     """A decorator that schedules a task in the background for you with
322     optional reconnect logic.
323 
324     Parameters
325     ------------
326     seconds: :class:`float`
327         The number of seconds between every iteration.
328     minutes: :class:`float`
329         The number of minutes between every iteration.
330     hours: :class:`float`
331         The number of hours between every iteration.
332     count: Optional[:class:`int`]
333         The number of loops to do, ``None`` if it should be an
334         infinite loop.
335     reconnect: :class:`bool`
336         Whether to handle errors and restart the task
337         using an exponential back-off algorithm similar to the
338         one used in :meth:`discord.Client.connect`.
339     loop: :class:`asyncio.AbstractEventLoop`
340         The loop to use to register the task, if not given
341         defaults to :func:`asyncio.get_event_loop`.
342 
343     Raises
344     --------
345     ValueError
346         An invalid value was given.
347     TypeError
348         The function was not a coroutine.
349 
350     Returns
351     ---------
352     :class:`Loop`
353         The loop helper that handles the background task.
354     """
355     def decorator(func):
356         return Loop(func, seconds=seconds, minutes=minutes, hours=hours,
357                           count=count, reconnect=reconnect, loop=loop)
358     return decorator
359
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/discord/ext/tasks/__init__.py b/discord/ext/tasks/__init__.py
--- a/discord/ext/tasks/__init__.py
+++ b/discord/ext/tasks/__init__.py
@@ -18,9 +18,6 @@
     """
     def __init__(self, coro, seconds, hours, minutes, count, reconnect, loop):
         self.coro = coro
-        self.seconds = seconds
-        self.hours = hours
-        self.minutes = minutes
         self.reconnect = reconnect
         self.loop = loop or asyncio.get_event_loop()
         self.count = count
@@ -47,13 +44,7 @@
         if self.count is not None and self.count <= 0:
             raise ValueError('count must be greater than 0 or None.')
 
-        self._sleep = sleep = self.seconds + (self.minutes * 60.0) + (self.hours * 3600.0)
-        if sleep >= MAX_ASYNCIO_SECONDS:
-            fmt = 'Total number of seconds exceeds asyncio imposed limit of {0} seconds.'
-            raise ValueError(fmt.format(MAX_ASYNCIO_SECONDS))
-
-        if sleep < 0:
-            raise ValueError('Total number of seconds cannot be less than zero.')
+        self.change_interval(seconds=seconds, minutes=minutes, hours=hours)
 
         if not inspect.iscoroutinefunction(self.coro):
             raise TypeError('Expected coroutine function, not {0.__name__!r}.'.format(type(self.coro)))
@@ -317,6 +308,42 @@
         self._after_loop = coro
         return coro
 
+    def change_interval(self, *, seconds=0, minutes=0, hours=0):
+        """Changes the interval for the sleep time.
+
+        .. note::
+
+            This only applies on the next loop iteration. If it is desirable for the change of interval
+            to be applied right away, cancel the task with :meth:`cancel`.
+
+        Parameters
+        ------------
+        seconds: :class:`float`
+            The number of seconds between every iteration.
+        minutes: :class:`float`
+            The number of minutes between every iteration.
+        hours: :class:`float`
+            The number of hours between every iteration.
+
+        Raises
+        -------
+        ValueError
+            An invalid value was given.
+        """
+
+        sleep = seconds + (minutes * 60.0) + (hours * 3600.0)
+        if sleep >= MAX_ASYNCIO_SECONDS:
+            fmt = 'Total number of seconds exceeds asyncio imposed limit of {0} seconds.'
+            raise ValueError(fmt.format(MAX_ASYNCIO_SECONDS))
+
+        if sleep < 0:
+            raise ValueError('Total number of seconds cannot be less than zero.')
+
+        self._sleep = sleep
+        self.seconds = seconds
+        self.hours = hours
+        self.minutes = minutes
+
 def loop(*, seconds=0, minutes=0, hours=0, count=None, reconnect=True, loop=None):
     """A decorator that schedules a task in the background for you with
     optional reconnect logic.

{"golden_diff": "diff --git a/discord/ext/tasks/__init__.py b/discord/ext/tasks/__init__.py\n--- a/discord/ext/tasks/__init__.py\n+++ b/discord/ext/tasks/__init__.py\n@@ -18,9 +18,6 @@\n     \"\"\"\n     def __init__(self, coro, seconds, hours, minutes, count, reconnect, loop):\n         self.coro = coro\n-        self.seconds = seconds\n-        self.hours = hours\n-        self.minutes = minutes\n         self.reconnect = reconnect\n         self.loop = loop or asyncio.get_event_loop()\n         self.count = count\n@@ -47,13 +44,7 @@\n         if self.count is not None and self.count <= 0:\n             raise ValueError('count must be greater than 0 or None.')\n \n-        self._sleep = sleep = self.seconds + (self.minutes * 60.0) + (self.hours * 3600.0)\n-        if sleep >= MAX_ASYNCIO_SECONDS:\n-            fmt = 'Total number of seconds exceeds asyncio imposed limit of {0} seconds.'\n-            raise ValueError(fmt.format(MAX_ASYNCIO_SECONDS))\n-\n-        if sleep < 0:\n-            raise ValueError('Total number of seconds cannot be less than zero.')\n+        self.change_interval(seconds=seconds, minutes=minutes, hours=hours)\n \n         if not inspect.iscoroutinefunction(self.coro):\n             raise TypeError('Expected coroutine function, not {0.__name__!r}.'.format(type(self.coro)))\n@@ -317,6 +308,42 @@\n         self._after_loop = coro\n         return coro\n \n+    def change_interval(self, *, seconds=0, minutes=0, hours=0):\n+        \"\"\"Changes the interval for the sleep time.\n+\n+        .. note::\n+\n+            This only applies on the next loop iteration. If it is desirable for the change of interval\n+            to be applied right away, cancel the task with :meth:`cancel`.\n+\n+        Parameters\n+        ------------\n+        seconds: :class:`float`\n+            The number of seconds between every iteration.\n+        minutes: :class:`float`\n+            The number of minutes between every iteration.\n+        hours: :class:`float`\n+            The number of hours between every iteration.\n+\n+        Raises\n+        -------\n+        ValueError\n+            An invalid value was given.\n+        \"\"\"\n+\n+        sleep = seconds + (minutes * 60.0) + (hours * 3600.0)\n+        if sleep >= MAX_ASYNCIO_SECONDS:\n+            fmt = 'Total number of seconds exceeds asyncio imposed limit of {0} seconds.'\n+            raise ValueError(fmt.format(MAX_ASYNCIO_SECONDS))\n+\n+        if sleep < 0:\n+            raise ValueError('Total number of seconds cannot be less than zero.')\n+\n+        self._sleep = sleep\n+        self.seconds = seconds\n+        self.hours = hours\n+        self.minutes = minutes\n+\n def loop(*, seconds=0, minutes=0, hours=0, count=None, reconnect=True, loop=None):\n     \"\"\"A decorator that schedules a task in the background for you with\n     optional reconnect logic.\n", "issue": "[tasks] Run-time changing of the sleep interval\n### The Problem\r\n\r\nNo way to change the sleep interval at run-time.\r\n\r\n### The Ideal Solution\r\n\r\nSomething like `task.change_interval(seconds=20.0)`. \r\n\r\nIt should apply on the next loop. If someone wants to do it right away regardless of the current loop they can cancel it, change the interval, then start it again.\r\n\r\n### The Current Solution\r\n\r\nNone\n", "before_files": [{"content": "import asyncio\nimport aiohttp\nimport websockets\nimport discord\nimport inspect\nimport logging\n\nfrom discord.backoff import ExponentialBackoff\n\nMAX_ASYNCIO_SECONDS = 3456000\n\nlog = logging.getLogger(__name__)\n\nclass Loop:\n    \"\"\"A background task helper that abstracts the loop and reconnection logic for you.\n\n    The main interface to create this is through :func:`loop`.\n    \"\"\"\n    def __init__(self, coro, seconds, hours, minutes, count, reconnect, loop):\n        self.coro = coro\n        self.seconds = seconds\n        self.hours = hours\n        self.minutes = minutes\n        self.reconnect = reconnect\n        self.loop = loop or asyncio.get_event_loop()\n        self.count = count\n        self._current_loop = 0\n        self._task = None\n        self._injected = None\n        self._valid_exception = (\n            OSError,\n            discord.HTTPException,\n            discord.GatewayNotFound,\n            discord.ConnectionClosed,\n            aiohttp.ClientError,\n            asyncio.TimeoutError,\n            websockets.InvalidHandshake,\n            websockets.WebSocketProtocolError,\n        )\n\n        self._before_loop = None\n        self._after_loop = None\n        self._is_being_cancelled = False\n        self._has_failed = False\n        self._stop_next_iteration = False\n\n        if self.count is not None and self.count <= 0:\n            raise ValueError('count must be greater than 0 or None.')\n\n        self._sleep = sleep = self.seconds + (self.minutes * 60.0) + (self.hours * 3600.0)\n        if sleep >= MAX_ASYNCIO_SECONDS:\n            fmt = 'Total number of seconds exceeds asyncio imposed limit of {0} seconds.'\n            raise ValueError(fmt.format(MAX_ASYNCIO_SECONDS))\n\n        if sleep < 0:\n            raise ValueError('Total number of seconds cannot be less than zero.')\n\n        if not inspect.iscoroutinefunction(self.coro):\n            raise TypeError('Expected coroutine function, not {0.__name__!r}.'.format(type(self.coro)))\n\n    async def _call_loop_function(self, name):\n        coro = getattr(self, '_' + name)\n        if coro is None:\n            return\n\n        if self._injected is not None:\n            await coro(self._injected)\n        else:\n            await coro()\n\n    async def _loop(self, *args, **kwargs):\n        backoff = ExponentialBackoff()\n        await self._call_loop_function('before_loop')\n        try:\n            while True:\n                try:\n                    await self.coro(*args, **kwargs)\n                except self._valid_exception as exc:\n                    if not self.reconnect:\n                        raise\n                    await asyncio.sleep(backoff.delay())\n                else:\n                    if self._stop_next_iteration:\n                        return\n                    self._current_loop += 1\n                    if self._current_loop == self.count:\n                        break\n\n                    await asyncio.sleep(self._sleep)\n        except asyncio.CancelledError:\n            self._is_being_cancelled = True\n            raise\n        except Exception:\n            self._has_failed = True\n            log.exception('Internal background task failed.')\n            raise\n        finally:\n            await self._call_loop_function('after_loop')\n            self._is_being_cancelled = False\n            self._current_loop = 0\n            self._stop_next_iteration = False\n            self._has_failed = False\n\n    def __get__(self, obj, objtype):\n        if obj is None:\n            return self\n        self._injected = obj\n        return self\n\n    @property\n    def current_loop(self):\n        \"\"\":class:`int`: The current iteration of the loop.\"\"\"\n        return self._current_loop\n\n    def start(self, *args, **kwargs):\n        r\"\"\"Starts the internal task in the event loop.\n\n        Parameters\n        ------------\n        \\*args\n            The arguments to to use.\n        \\*\\*kwargs\n            The keyword arguments to use.\n\n        Raises\n        --------\n        RuntimeError\n            A task has already been launched and is running.\n\n        Returns\n        ---------\n        :class:`asyncio.Task`\n            The task that has been created.\n        \"\"\"\n\n        if self._task is not None and not self._task.done():\n            raise RuntimeError('Task is already launched and is not completed.')\n\n        if self._injected is not None:\n            args = (self._injected, *args)\n\n        self._task = self.loop.create_task(self._loop(*args, **kwargs))\n        return self._task\n\n    def stop(self):\n        r\"\"\"Gracefully stops the task from running.\n\n        Unlike :meth:`cancel`\\, this allows the task to finish its\n        current iteration before gracefully exiting.\n\n        .. note::\n\n            If the internal function raises an error that can be\n            handled before finishing then it will retry until\n            it succeeds.\n\n            If this is undesirable, either remove the error handling\n            before stopping via :meth:`clear_exception_types` or\n            use :meth:`cancel` instead.\n\n        .. versionadded:: 1.2.0\n        \"\"\"\n        if self._task and not self._task.done():\n            self._stop_next_iteration = True\n\n    def _can_be_cancelled(self):\n        return not self._is_being_cancelled and self._task and not self._task.done()\n\n    def cancel(self):\n        \"\"\"Cancels the internal task, if it is running.\"\"\"\n        if self._can_be_cancelled():\n            self._task.cancel()\n\n    def restart(self, *args, **kwargs):\n        r\"\"\"A convenience method to restart the internal task.\n\n        .. note::\n\n            Due to the way this function works, the task is not\n            returned like :meth:`start`.\n\n        Parameters\n        ------------\n        \\*args\n            The arguments to to use.\n        \\*\\*kwargs\n            The keyword arguments to use.\n        \"\"\"\n\n        def restart_when_over(fut, *, args=args, kwargs=kwargs):\n            self._task.remove_done_callback(restart_when_over)\n            self.start(*args, **kwargs)\n\n        if self._can_be_cancelled():\n            self._task.add_done_callback(restart_when_over)\n            self._task.cancel()\n\n    def add_exception_type(self, exc):\n        r\"\"\"Adds an exception type to be handled during the reconnect logic.\n\n        By default the exception types handled are those handled by\n        :meth:`discord.Client.connect`\\, which includes a lot of internet disconnection\n        errors.\n\n        This function is useful if you're interacting with a 3rd party library that\n        raises its own set of exceptions.\n\n        Parameters\n        ------------\n        exc: Type[:class:`BaseException`]\n            The exception class to handle.\n\n        Raises\n        --------\n        TypeError\n            The exception passed is either not a class or not inherited from :class:`BaseException`.\n        \"\"\"\n\n        if not inspect.isclass(exc):\n            raise TypeError('{0!r} must be a class.'.format(exc))\n        if not issubclass(exc, BaseException):\n            raise TypeError('{0!r} must inherit from BaseException.'.format(exc))\n\n        self._valid_exception = (*self._valid_exception, exc)\n\n    def clear_exception_types(self):\n        \"\"\"Removes all exception types that are handled.\n\n        .. note::\n\n            This operation obviously cannot be undone!\n        \"\"\"\n        self._valid_exception = tuple()\n\n    def remove_exception_type(self, exc):\n        \"\"\"Removes an exception type from being handled during the reconnect logic.\n\n        Parameters\n        ------------\n        exc: Type[:class:`BaseException`]\n            The exception class to handle.\n\n        Returns\n        ---------\n        :class:`bool`\n            Whether it was successfully removed.\n        \"\"\"\n        old_length = len(self._valid_exception)\n        self._valid_exception = tuple(x for x in self._valid_exception if x is not exc)\n        return len(self._valid_exception) != old_length\n\n    def get_task(self):\n        \"\"\"Optional[:class:`asyncio.Task`]: Fetches the internal task or ``None`` if there isn't one running.\"\"\"\n        return self._task\n\n    def is_being_cancelled(self):\n        \"\"\":class:`bool`: Whether the task is being cancelled.\"\"\"\n        return self._is_being_cancelled\n\n    def failed(self):\n        \"\"\":class:`bool`: Whether the internal task has failed.\n\n        .. versionadded:: 1.2.0\n        \"\"\"\n        return self._has_failed\n\n    def before_loop(self, coro):\n        \"\"\"A decorator that registers a coroutine to be called before the loop starts running.\n\n        This is useful if you want to wait for some bot state before the loop starts,\n        such as :meth:`discord.Client.wait_until_ready`.\n\n        The coroutine must take no arguments (except ``self`` in a class context).\n\n        Parameters\n        ------------\n        coro: :ref:`coroutine <coroutine>`\n            The coroutine to register before the loop runs.\n\n        Raises\n        -------\n        TypeError\n            The function was not a coroutine.\n        \"\"\"\n\n        if not inspect.iscoroutinefunction(coro):\n            raise TypeError('Expected coroutine function, received {0.__name__!r}.'.format(type(coro)))\n\n        self._before_loop = coro\n        return coro\n\n    def after_loop(self, coro):\n        \"\"\"A decorator that register a coroutine to be called after the loop finished running.\n\n        The coroutine must take no arguments (except ``self`` in a class context).\n\n        .. note::\n\n            This coroutine is called even during cancellation. If it is desirable\n            to tell apart whether something was cancelled or not, check to see\n            whether :meth:`is_being_cancelled` is ``True`` or not.\n\n        Parameters\n        ------------\n        coro: :ref:`coroutine <coroutine>`\n            The coroutine to register after the loop finishes.\n\n        Raises\n        -------\n        TypeError\n            The function was not a coroutine.\n        \"\"\"\n\n        if not inspect.iscoroutinefunction(coro):\n            raise TypeError('Expected coroutine function, received {0.__name__!r}.'.format(type(coro)))\n\n        self._after_loop = coro\n        return coro\n\ndef loop(*, seconds=0, minutes=0, hours=0, count=None, reconnect=True, loop=None):\n    \"\"\"A decorator that schedules a task in the background for you with\n    optional reconnect logic.\n\n    Parameters\n    ------------\n    seconds: :class:`float`\n        The number of seconds between every iteration.\n    minutes: :class:`float`\n        The number of minutes between every iteration.\n    hours: :class:`float`\n        The number of hours between every iteration.\n    count: Optional[:class:`int`]\n        The number of loops to do, ``None`` if it should be an\n        infinite loop.\n    reconnect: :class:`bool`\n        Whether to handle errors and restart the task\n        using an exponential back-off algorithm similar to the\n        one used in :meth:`discord.Client.connect`.\n    loop: :class:`asyncio.AbstractEventLoop`\n        The loop to use to register the task, if not given\n        defaults to :func:`asyncio.get_event_loop`.\n\n    Raises\n    --------\n    ValueError\n        An invalid value was given.\n    TypeError\n        The function was not a coroutine.\n\n    Returns\n    ---------\n    :class:`Loop`\n        The loop helper that handles the background task.\n    \"\"\"\n    def decorator(func):\n        return Loop(func, seconds=seconds, minutes=minutes, hours=hours,\n                          count=count, reconnect=reconnect, loop=loop)\n    return decorator\n", "path": "discord/ext/tasks/__init__.py"}], "after_files": [{"content": "import asyncio\nimport aiohttp\nimport websockets\nimport discord\nimport inspect\nimport logging\n\nfrom discord.backoff import ExponentialBackoff\n\nMAX_ASYNCIO_SECONDS = 3456000\n\nlog = logging.getLogger(__name__)\n\nclass Loop:\n    \"\"\"A background task helper that abstracts the loop and reconnection logic for you.\n\n    The main interface to create this is through :func:`loop`.\n    \"\"\"\n    def __init__(self, coro, seconds, hours, minutes, count, reconnect, loop):\n        self.coro = coro\n        self.reconnect = reconnect\n        self.loop = loop or asyncio.get_event_loop()\n        self.count = count\n        self._current_loop = 0\n        self._task = None\n        self._injected = None\n        self._valid_exception = (\n            OSError,\n            discord.HTTPException,\n            discord.GatewayNotFound,\n            discord.ConnectionClosed,\n            aiohttp.ClientError,\n            asyncio.TimeoutError,\n            websockets.InvalidHandshake,\n            websockets.WebSocketProtocolError,\n        )\n\n        self._before_loop = None\n        self._after_loop = None\n        self._is_being_cancelled = False\n        self._has_failed = False\n        self._stop_next_iteration = False\n\n        if self.count is not None and self.count <= 0:\n            raise ValueError('count must be greater than 0 or None.')\n\n        self.change_interval(seconds=seconds, minutes=minutes, hours=hours)\n\n        if not inspect.iscoroutinefunction(self.coro):\n            raise TypeError('Expected coroutine function, not {0.__name__!r}.'.format(type(self.coro)))\n\n    async def _call_loop_function(self, name):\n        coro = getattr(self, '_' + name)\n        if coro is None:\n            return\n\n        if self._injected is not None:\n            await coro(self._injected)\n        else:\n            await coro()\n\n    async def _loop(self, *args, **kwargs):\n        backoff = ExponentialBackoff()\n        await self._call_loop_function('before_loop')\n        try:\n            while True:\n                try:\n                    await self.coro(*args, **kwargs)\n                except self._valid_exception as exc:\n                    if not self.reconnect:\n                        raise\n                    await asyncio.sleep(backoff.delay())\n                else:\n                    if self._stop_next_iteration:\n                        return\n                    self._current_loop += 1\n                    if self._current_loop == self.count:\n                        break\n\n                    await asyncio.sleep(self._sleep)\n        except asyncio.CancelledError:\n            self._is_being_cancelled = True\n            raise\n        except Exception:\n            self._has_failed = True\n            log.exception('Internal background task failed.')\n            raise\n        finally:\n            await self._call_loop_function('after_loop')\n            self._is_being_cancelled = False\n            self._current_loop = 0\n            self._stop_next_iteration = False\n            self._has_failed = False\n\n    def __get__(self, obj, objtype):\n        if obj is None:\n            return self\n        self._injected = obj\n        return self\n\n    @property\n    def current_loop(self):\n        \"\"\":class:`int`: The current iteration of the loop.\"\"\"\n        return self._current_loop\n\n    def start(self, *args, **kwargs):\n        r\"\"\"Starts the internal task in the event loop.\n\n        Parameters\n        ------------\n        \\*args\n            The arguments to to use.\n        \\*\\*kwargs\n            The keyword arguments to use.\n\n        Raises\n        --------\n        RuntimeError\n            A task has already been launched and is running.\n\n        Returns\n        ---------\n        :class:`asyncio.Task`\n            The task that has been created.\n        \"\"\"\n\n        if self._task is not None and not self._task.done():\n            raise RuntimeError('Task is already launched and is not completed.')\n\n        if self._injected is not None:\n            args = (self._injected, *args)\n\n        self._task = self.loop.create_task(self._loop(*args, **kwargs))\n        return self._task\n\n    def stop(self):\n        r\"\"\"Gracefully stops the task from running.\n\n        Unlike :meth:`cancel`\\, this allows the task to finish its\n        current iteration before gracefully exiting.\n\n        .. note::\n\n            If the internal function raises an error that can be\n            handled before finishing then it will retry until\n            it succeeds.\n\n            If this is undesirable, either remove the error handling\n            before stopping via :meth:`clear_exception_types` or\n            use :meth:`cancel` instead.\n\n        .. versionadded:: 1.2.0\n        \"\"\"\n        if self._task and not self._task.done():\n            self._stop_next_iteration = True\n\n    def _can_be_cancelled(self):\n        return not self._is_being_cancelled and self._task and not self._task.done()\n\n    def cancel(self):\n        \"\"\"Cancels the internal task, if it is running.\"\"\"\n        if self._can_be_cancelled():\n            self._task.cancel()\n\n    def restart(self, *args, **kwargs):\n        r\"\"\"A convenience method to restart the internal task.\n\n        .. note::\n\n            Due to the way this function works, the task is not\n            returned like :meth:`start`.\n\n        Parameters\n        ------------\n        \\*args\n            The arguments to to use.\n        \\*\\*kwargs\n            The keyword arguments to use.\n        \"\"\"\n\n        def restart_when_over(fut, *, args=args, kwargs=kwargs):\n            self._task.remove_done_callback(restart_when_over)\n            self.start(*args, **kwargs)\n\n        if self._can_be_cancelled():\n            self._task.add_done_callback(restart_when_over)\n            self._task.cancel()\n\n    def add_exception_type(self, exc):\n        r\"\"\"Adds an exception type to be handled during the reconnect logic.\n\n        By default the exception types handled are those handled by\n        :meth:`discord.Client.connect`\\, which includes a lot of internet disconnection\n        errors.\n\n        This function is useful if you're interacting with a 3rd party library that\n        raises its own set of exceptions.\n\n        Parameters\n        ------------\n        exc: Type[:class:`BaseException`]\n            The exception class to handle.\n\n        Raises\n        --------\n        TypeError\n            The exception passed is either not a class or not inherited from :class:`BaseException`.\n        \"\"\"\n\n        if not inspect.isclass(exc):\n            raise TypeError('{0!r} must be a class.'.format(exc))\n        if not issubclass(exc, BaseException):\n            raise TypeError('{0!r} must inherit from BaseException.'.format(exc))\n\n        self._valid_exception = (*self._valid_exception, exc)\n\n    def clear_exception_types(self):\n        \"\"\"Removes all exception types that are handled.\n\n        .. note::\n\n            This operation obviously cannot be undone!\n        \"\"\"\n        self._valid_exception = tuple()\n\n    def remove_exception_type(self, exc):\n        \"\"\"Removes an exception type from being handled during the reconnect logic.\n\n        Parameters\n        ------------\n        exc: Type[:class:`BaseException`]\n            The exception class to handle.\n\n        Returns\n        ---------\n        :class:`bool`\n            Whether it was successfully removed.\n        \"\"\"\n        old_length = len(self._valid_exception)\n        self._valid_exception = tuple(x for x in self._valid_exception if x is not exc)\n        return len(self._valid_exception) != old_length\n\n    def get_task(self):\n        \"\"\"Optional[:class:`asyncio.Task`]: Fetches the internal task or ``None`` if there isn't one running.\"\"\"\n        return self._task\n\n    def is_being_cancelled(self):\n        \"\"\":class:`bool`: Whether the task is being cancelled.\"\"\"\n        return self._is_being_cancelled\n\n    def failed(self):\n        \"\"\":class:`bool`: Whether the internal task has failed.\n\n        .. versionadded:: 1.2.0\n        \"\"\"\n        return self._has_failed\n\n    def before_loop(self, coro):\n        \"\"\"A decorator that registers a coroutine to be called before the loop starts running.\n\n        This is useful if you want to wait for some bot state before the loop starts,\n        such as :meth:`discord.Client.wait_until_ready`.\n\n        The coroutine must take no arguments (except ``self`` in a class context).\n\n        Parameters\n        ------------\n        coro: :ref:`coroutine <coroutine>`\n            The coroutine to register before the loop runs.\n\n        Raises\n        -------\n        TypeError\n            The function was not a coroutine.\n        \"\"\"\n\n        if not inspect.iscoroutinefunction(coro):\n            raise TypeError('Expected coroutine function, received {0.__name__!r}.'.format(type(coro)))\n\n        self._before_loop = coro\n        return coro\n\n    def after_loop(self, coro):\n        \"\"\"A decorator that register a coroutine to be called after the loop finished running.\n\n        The coroutine must take no arguments (except ``self`` in a class context).\n\n        .. note::\n\n            This coroutine is called even during cancellation. If it is desirable\n            to tell apart whether something was cancelled or not, check to see\n            whether :meth:`is_being_cancelled` is ``True`` or not.\n\n        Parameters\n        ------------\n        coro: :ref:`coroutine <coroutine>`\n            The coroutine to register after the loop finishes.\n\n        Raises\n        -------\n        TypeError\n            The function was not a coroutine.\n        \"\"\"\n\n        if not inspect.iscoroutinefunction(coro):\n            raise TypeError('Expected coroutine function, received {0.__name__!r}.'.format(type(coro)))\n\n        self._after_loop = coro\n        return coro\n\n    def change_interval(self, *, seconds=0, minutes=0, hours=0):\n        \"\"\"Changes the interval for the sleep time.\n\n        .. note::\n\n            This only applies on the next loop iteration. If it is desirable for the change of interval\n            to be applied right away, cancel the task with :meth:`cancel`.\n\n        Parameters\n        ------------\n        seconds: :class:`float`\n            The number of seconds between every iteration.\n        minutes: :class:`float`\n            The number of minutes between every iteration.\n        hours: :class:`float`\n            The number of hours between every iteration.\n\n        Raises\n        -------\n        ValueError\n            An invalid value was given.\n        \"\"\"\n\n        sleep = seconds + (minutes * 60.0) + (hours * 3600.0)\n        if sleep >= MAX_ASYNCIO_SECONDS:\n            fmt = 'Total number of seconds exceeds asyncio imposed limit of {0} seconds.'\n            raise ValueError(fmt.format(MAX_ASYNCIO_SECONDS))\n\n        if sleep < 0:\n            raise ValueError('Total number of seconds cannot be less than zero.')\n\n        self._sleep = sleep\n        self.seconds = seconds\n        self.hours = hours\n        self.minutes = minutes\n\ndef loop(*, seconds=0, minutes=0, hours=0, count=None, reconnect=True, loop=None):\n    \"\"\"A decorator that schedules a task in the background for you with\n    optional reconnect logic.\n\n    Parameters\n    ------------\n    seconds: :class:`float`\n        The number of seconds between every iteration.\n    minutes: :class:`float`\n        The number of minutes between every iteration.\n    hours: :class:`float`\n        The number of hours between every iteration.\n    count: Optional[:class:`int`]\n        The number of loops to do, ``None`` if it should be an\n        infinite loop.\n    reconnect: :class:`bool`\n        Whether to handle errors and restart the task\n        using an exponential back-off algorithm similar to the\n        one used in :meth:`discord.Client.connect`.\n    loop: :class:`asyncio.AbstractEventLoop`\n        The loop to use to register the task, if not given\n        defaults to :func:`asyncio.get_event_loop`.\n\n    Raises\n    --------\n    ValueError\n        An invalid value was given.\n    TypeError\n        The function was not a coroutine.\n\n    Returns\n    ---------\n    :class:`Loop`\n        The loop helper that handles the background task.\n    \"\"\"\n    def decorator(func):\n        return Loop(func, seconds=seconds, minutes=minutes, hours=hours,\n                          count=count, reconnect=reconnect, loop=loop)\n    return decorator\n", "path": "discord/ext/tasks/__init__.py"}]}