Skip to content

Commit ac7cfdb

Browse files
feat: preliminary support for python 3.14 (#1337)
Co-authored-by: vi <8530778+shiftinv@users.noreply.github.com>
1 parent 287c210 commit ac7cfdb

File tree

21 files changed

+103
-107
lines changed

21 files changed

+103
-107
lines changed

.github/workflows/lint-test.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ jobs:
110110
- lint
111111
strategy:
112112
matrix:
113+
# TODO: add 3.14 once we switch to uv
113114
python-version: ${{ fromJson(needs.python-versions.outputs.pyright) }}
114115
experimental: [false]
115116
fail-fast: false
@@ -241,6 +242,7 @@ jobs:
241242
strategy:
242243
matrix:
243244
os: ["windows-latest", "ubuntu-latest", "macos-latest"]
245+
# TODO: add 3.14 once we switch to uv
244246
python-version: ${{ fromJson(needs.python-versions.outputs.tests) }}
245247
experimental: [false]
246248
fail-fast: true

changelog/1337.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add preliminary support for Python 3.14.

disnake/client.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import sys
99
import traceback
1010
import types
11-
import warnings
1211
from datetime import datetime, timedelta
1312
from errno import ECONNRESET
1413
from typing import (
@@ -240,8 +239,8 @@ class Client:
240239
Allow disabling the message cache and change the default size to ``1000``.
241240
loop: Optional[:class:`asyncio.AbstractEventLoop`]
242241
The :class:`asyncio.AbstractEventLoop` to use for asynchronous operations.
243-
Defaults to ``None``, in which case the default event loop is used via
244-
:func:`asyncio.get_event_loop()`.
242+
Defaults to ``None``, in which case the current event loop is
243+
used, or a new loop is created if there is none.
245244
asyncio_debug: :class:`bool`
246245
Whether to enable asyncio debugging when the client starts.
247246
Defaults to False.
@@ -407,9 +406,7 @@ def __init__(
407406
self.ws: DiscordWebSocket = None # type: ignore
408407

409408
if loop is None:
410-
with warnings.catch_warnings():
411-
warnings.simplefilter("ignore", DeprecationWarning)
412-
self.loop: asyncio.AbstractEventLoop = asyncio.get_event_loop()
409+
self.loop: asyncio.AbstractEventLoop = utils.get_event_loop()
413410
else:
414411
self.loop: asyncio.AbstractEventLoop = loop
415412

@@ -840,7 +837,7 @@ async def another_message(message): pass
840837
else (name if isinstance(name, str) else f"on_{name.value}")
841838
)
842839

843-
if not asyncio.iscoroutinefunction(func):
840+
if not utils.iscoroutinefunction(func):
844841
raise TypeError("Listeners must be coroutines")
845842

846843
if name_ in self.extra_events:
@@ -1866,7 +1863,7 @@ async def on_ready():
18661863
TypeError
18671864
The coroutine passed is not actually a coroutine.
18681865
"""
1869-
if not asyncio.iscoroutinefunction(coro):
1866+
if not utils.iscoroutinefunction(coro):
18701867
raise TypeError("event registered must be a coroutine function")
18711868

18721869
setattr(self, coro.__name__, coro)

disnake/ext/commands/base_core.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,13 @@
2424
from disnake.enums import ApplicationCommandType
2525
from disnake.flags import ApplicationInstallTypes, InteractionContextTypes
2626
from disnake.permissions import Permissions
27-
from disnake.utils import _generated, _overload_with_permissions, async_all, maybe_coroutine
27+
from disnake.utils import (
28+
_generated,
29+
_overload_with_permissions,
30+
async_all,
31+
iscoroutinefunction,
32+
maybe_coroutine,
33+
)
2834

2935
from .cooldowns import BucketType, CooldownMapping, MaxConcurrency
3036
from .errors import CheckFailure, CommandError, CommandInvokeError, CommandOnCooldown
@@ -481,7 +487,7 @@ def error(self, coro: ErrorT) -> ErrorT:
481487
TypeError
482488
The coroutine passed is not actually a coroutine.
483489
"""
484-
if not asyncio.iscoroutinefunction(coro):
490+
if not iscoroutinefunction(coro):
485491
raise TypeError("The error handler must be a coroutine.")
486492

487493
self.on_error: Error = coro
@@ -597,7 +603,7 @@ def before_invoke(self, coro: HookT) -> HookT:
597603
TypeError
598604
The coroutine passed is not actually a coroutine.
599605
"""
600-
if not asyncio.iscoroutinefunction(coro):
606+
if not iscoroutinefunction(coro):
601607
raise TypeError("The pre-invoke hook must be a coroutine.")
602608

603609
self._before_invoke = coro
@@ -620,7 +626,7 @@ def after_invoke(self, coro: HookT) -> HookT:
620626
TypeError
621627
The coroutine passed is not actually a coroutine.
622628
"""
623-
if not asyncio.iscoroutinefunction(coro):
629+
if not iscoroutinefunction(coro):
624630
raise TypeError("The post-invoke hook must be a coroutine.")
625631

626632
self._after_invoke = coro

disnake/ext/commands/bot_base.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
from __future__ import annotations
44

5-
import asyncio
65
import collections
76
import collections.abc
87
import inspect
@@ -13,6 +12,7 @@
1312
from typing import TYPE_CHECKING, Any, Callable, Iterable, List, Optional, Type, TypeVar, Union
1413

1514
import disnake
15+
from disnake.utils import iscoroutinefunction
1616

1717
from . import errors
1818
from .common_bot_base import CommonBotBase
@@ -355,7 +355,7 @@ def before_invoke(self, coro: CFT) -> CFT:
355355
TypeError
356356
The coroutine passed is not actually a coroutine.
357357
"""
358-
if not asyncio.iscoroutinefunction(coro):
358+
if not iscoroutinefunction(coro):
359359
raise TypeError("The pre-invoke hook must be a coroutine.")
360360

361361
self._before_invoke = coro
@@ -390,7 +390,7 @@ def after_invoke(self, coro: CFT) -> CFT:
390390
TypeError
391391
The coroutine passed is not actually a coroutine.
392392
"""
393-
if not asyncio.iscoroutinefunction(coro):
393+
if not iscoroutinefunction(coro):
394394
raise TypeError("The post-invoke hook must be a coroutine.")
395395

396396
self._after_invoke = coro

disnake/ext/commands/cog.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
from __future__ import annotations
44

5-
import asyncio
65
import inspect
76
import logging
87
from typing import (
@@ -193,7 +192,7 @@ def __new__(cls: Type[CogMeta], *args: Any, **kwargs: Any) -> CogMeta:
193192
if elem.startswith(("cog_", "bot_")):
194193
raise TypeError(no_bot_cog.format(base, elem))
195194
app_commands[elem] = value
196-
elif asyncio.iscoroutinefunction(value):
195+
elif disnake.utils.iscoroutinefunction(value):
197196
if hasattr(value, "__cog_listener__"):
198197
if elem.startswith(("cog_", "bot_")):
199198
raise TypeError(no_bot_cog.format(base, elem))
@@ -419,7 +418,7 @@ def decorator(func: FuncT) -> FuncT:
419418
actual = func
420419
if isinstance(actual, staticmethod):
421420
actual = actual.__func__
422-
if not asyncio.iscoroutinefunction(actual):
421+
if not disnake.utils.iscoroutinefunction(actual):
423422
raise TypeError("Listener function must be a coroutine function.")
424423
actual.__cog_listener__ = True
425424
to_assign = (

disnake/ext/commands/core.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
_generated,
3232
_overload_with_permissions,
3333
get_signature_parameters,
34+
iscoroutinefunction,
3435
unwrap_function,
3536
)
3637

@@ -278,7 +279,7 @@ def __init__(
278279
func: CommandCallback[CogT, ContextT, P, T],
279280
**kwargs: Any,
280281
) -> None:
281-
if not asyncio.iscoroutinefunction(func):
282+
if not iscoroutinefunction(func):
282283
raise TypeError("Callback must be a coroutine.")
283284

284285
name = kwargs.get("name") or func.__name__
@@ -508,9 +509,8 @@ async def dispatch_error(self, ctx: Context, error: Exception) -> None:
508509
stop_propagation = await wrapped(ctx, error)
509510
# User has an option to cancel the global error handler by returning True
510511
finally:
511-
if stop_propagation:
512-
return # noqa: B012
513-
ctx.bot.dispatch("command_error", ctx, error)
512+
if not stop_propagation:
513+
ctx.bot.dispatch("command_error", ctx, error)
514514

515515
async def transform(self, ctx: Context, param: inspect.Parameter) -> Any:
516516
required = param.default is param.empty
@@ -886,7 +886,7 @@ def error(self, coro: ErrorT) -> ErrorT:
886886
TypeError
887887
The coroutine passed is not actually a coroutine.
888888
"""
889-
if not asyncio.iscoroutinefunction(coro):
889+
if not iscoroutinefunction(coro):
890890
raise TypeError("The error handler must be a coroutine.")
891891

892892
self.on_error: Error = coro
@@ -922,7 +922,7 @@ def before_invoke(self, coro: HookT) -> HookT:
922922
TypeError
923923
The coroutine passed is not actually a coroutine.
924924
"""
925-
if not asyncio.iscoroutinefunction(coro):
925+
if not iscoroutinefunction(coro):
926926
raise TypeError("The pre-invoke hook must be a coroutine.")
927927

928928
self._before_invoke = coro
@@ -949,7 +949,7 @@ def after_invoke(self, coro: HookT) -> HookT:
949949
TypeError
950950
The coroutine passed is not actually a coroutine.
951951
"""
952-
if not asyncio.iscoroutinefunction(coro):
952+
if not iscoroutinefunction(coro):
953953
raise TypeError("The post-invoke hook must be a coroutine.")
954954

955955
self._after_invoke = coro
@@ -1702,7 +1702,7 @@ def decorator(func: Union[Command, CoroFunc]) -> Union[Command, CoroFunc]:
17021702

17031703
return func
17041704

1705-
if asyncio.iscoroutinefunction(predicate):
1705+
if iscoroutinefunction(predicate):
17061706
decorator.predicate = predicate
17071707
else:
17081708

disnake/ext/commands/ctx_menus_core.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22

33
from __future__ import annotations
44

5-
import asyncio
65
from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Sequence, Tuple, Union
76

87
from disnake.app_commands import MessageCommand, UserCommand
98
from disnake.flags import ApplicationInstallTypes, InteractionContextTypes
109
from disnake.i18n import Localized
1110
from disnake.permissions import Permissions
11+
from disnake.utils import iscoroutinefunction
1212

1313
from .base_core import InvokableApplicationCommand, _get_overridden_method
1414
from .errors import CommandError
@@ -124,9 +124,8 @@ async def _call_external_error_handlers(
124124
stop_propagation = await local(inter, error)
125125
# User has an option to cancel the global error handler by returning True
126126
finally:
127-
if stop_propagation:
128-
return # noqa: B012
129-
inter.bot.dispatch("user_command_error", inter, error)
127+
if not stop_propagation:
128+
inter.bot.dispatch("user_command_error", inter, error)
130129

131130
async def __call__(
132131
self,
@@ -236,9 +235,8 @@ async def _call_external_error_handlers(
236235
stop_propagation = await local(inter, error)
237236
# User has an option to cancel the global error handler by returning True
238237
finally:
239-
if stop_propagation:
240-
return # noqa: B012
241-
inter.bot.dispatch("message_command_error", inter, error)
238+
if not stop_propagation:
239+
inter.bot.dispatch("message_command_error", inter, error)
242240

243241
async def __call__(
244242
self,
@@ -337,7 +335,7 @@ def user_command(
337335
def decorator(
338336
func: InteractionCommandCallback[CogT, UserCommandInteraction, P],
339337
) -> InvokableUserCommand:
340-
if not asyncio.iscoroutinefunction(func):
338+
if not iscoroutinefunction(func):
341339
raise TypeError(f"<{func.__qualname__}> must be a coroutine function")
342340
if hasattr(func, "__command_flag__"):
343341
raise TypeError("Callback is already a command.")
@@ -445,7 +443,7 @@ def message_command(
445443
def decorator(
446444
func: InteractionCommandCallback[CogT, MessageCommandInteraction, P],
447445
) -> InvokableMessageCommand:
448-
if not asyncio.iscoroutinefunction(func):
446+
if not iscoroutinefunction(func):
449447
raise TypeError(f"<{func.__qualname__}> must be a coroutine function")
450448
if hasattr(func, "__command_flag__"):
451449
raise TypeError("Callback is already a command.")

disnake/ext/commands/interaction_bot_base.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from disnake.custom_warnings import SyncWarning
3030
from disnake.enums import ApplicationCommandType
3131
from disnake.flags import ApplicationInstallTypes, InteractionContextTypes
32-
from disnake.utils import warn_deprecated
32+
from disnake.utils import iscoroutinefunction, warn_deprecated
3333

3434
from . import errors
3535
from .base_core import InvokableApplicationCommand
@@ -1300,7 +1300,7 @@ def before_slash_command_invoke(self, coro: CFT) -> CFT:
13001300
"""Similar to :meth:`Bot.before_invoke` but for slash commands,
13011301
and it takes an :class:`.ApplicationCommandInteraction` as its only parameter.
13021302
"""
1303-
if not asyncio.iscoroutinefunction(coro):
1303+
if not iscoroutinefunction(coro):
13041304
raise TypeError("The pre-invoke hook must be a coroutine.")
13051305

13061306
self._before_slash_command_invoke = coro
@@ -1310,39 +1310,39 @@ def after_slash_command_invoke(self, coro: CFT) -> CFT:
13101310
"""Similar to :meth:`Bot.after_invoke` but for slash commands,
13111311
and it takes an :class:`.ApplicationCommandInteraction` as its only parameter.
13121312
"""
1313-
if not asyncio.iscoroutinefunction(coro):
1313+
if not iscoroutinefunction(coro):
13141314
raise TypeError("The post-invoke hook must be a coroutine.")
13151315

13161316
self._after_slash_command_invoke = coro
13171317
return coro
13181318

13191319
def before_user_command_invoke(self, coro: CFT) -> CFT:
13201320
"""Similar to :meth:`Bot.before_slash_command_invoke` but for user commands."""
1321-
if not asyncio.iscoroutinefunction(coro):
1321+
if not iscoroutinefunction(coro):
13221322
raise TypeError("The pre-invoke hook must be a coroutine.")
13231323

13241324
self._before_user_command_invoke = coro
13251325
return coro
13261326

13271327
def after_user_command_invoke(self, coro: CFT) -> CFT:
13281328
"""Similar to :meth:`Bot.after_slash_command_invoke` but for user commands."""
1329-
if not asyncio.iscoroutinefunction(coro):
1329+
if not iscoroutinefunction(coro):
13301330
raise TypeError("The post-invoke hook must be a coroutine.")
13311331

13321332
self._after_user_command_invoke = coro
13331333
return coro
13341334

13351335
def before_message_command_invoke(self, coro: CFT) -> CFT:
13361336
"""Similar to :meth:`Bot.before_slash_command_invoke` but for message commands."""
1337-
if not asyncio.iscoroutinefunction(coro):
1337+
if not iscoroutinefunction(coro):
13381338
raise TypeError("The pre-invoke hook must be a coroutine.")
13391339

13401340
self._before_message_command_invoke = coro
13411341
return coro
13421342

13431343
def after_message_command_invoke(self, coro: CFT) -> CFT:
13441344
"""Similar to :meth:`Bot.after_slash_command_invoke` but for message commands."""
1345-
if not asyncio.iscoroutinefunction(coro):
1345+
if not iscoroutinefunction(coro):
13461346
raise TypeError("The post-invoke hook must be a coroutine.")
13471347

13481348
self._after_message_command_invoke = coro

disnake/ext/commands/slash_core.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -677,9 +677,8 @@ async def _call_external_error_handlers(
677677
stop_propagation = await local(inter, error)
678678
# User has an option to cancel the global error handler by returning True
679679
finally:
680-
if stop_propagation:
681-
return # noqa: B012
682-
inter.bot.dispatch("slash_command_error", inter, error)
680+
if not stop_propagation:
681+
inter.bot.dispatch("slash_command_error", inter, error)
683682

684683
async def _call_autocompleter(
685684
self, param: str, inter: ApplicationCommandInteraction, user_input: str
@@ -871,7 +870,7 @@ def slash_command(
871870
"""
872871

873872
def decorator(func: CommandCallback) -> InvokableSlashCommand:
874-
if not asyncio.iscoroutinefunction(func):
873+
if not utils.iscoroutinefunction(func):
875874
raise TypeError(f"<{func.__qualname__}> must be a coroutine function")
876875
if hasattr(func, "__command_flag__"):
877876
raise TypeError("Callback is already a command.")

0 commit comments

Comments
 (0)