From 05b56e02f745e7f12123552aa965b54b1950b77e Mon Sep 17 00:00:00 2001 From: Steve C Date: Wed, 8 Jul 2026 16:25:18 -0400 Subject: [PATCH] Fix command/group call overloads with custom contexts --- discord/ext/commands/core.py | 114 +++++++++++++++++---------------- discord/ext/commands/hybrid.py | 68 +++++++++++++++++--- 2 files changed, 119 insertions(+), 63 deletions(-) diff --git a/discord/ext/commands/core.py b/discord/ext/commands/core.py index 4adaf1fb1..bbccb640c 100644 --- a/discord/ext/commands/core.py +++ b/discord/ext/commands/core.py @@ -1504,15 +1504,7 @@ class GroupMixin(Generic[CogT]): name: str = ..., *args: Any, **kwargs: Unpack[_CommandDecoratorKwargs], - ) -> Callable[ - [ - Union[ - Callable[Concatenate[CogT, ContextT, P], Coro[T]], - Callable[Concatenate[ContextT, P], Coro[T]], - ] - ], - Command[CogT, P, T], - ]: ... + ) -> _CogCommandDecorator[CogT]: ... @overload def command( @@ -1521,15 +1513,7 @@ class GroupMixin(Generic[CogT]): cls: Type[CommandT] = ..., # type: ignore # previous overload handles case where cls is not set *args: Any, **kwargs: Unpack[_CommandDecoratorKwargs], - ) -> Callable[ - [ - Union[ - Callable[Concatenate[CogT, ContextT, P], Coro[T]], - Callable[Concatenate[ContextT, P], Coro[T]], - ] - ], - CommandT, - ]: ... + ) -> _CogCommandDecoratorWithCls[CogT, CommandT]: ... def command( self, @@ -1561,15 +1545,7 @@ class GroupMixin(Generic[CogT]): name: str = ..., *args: Any, **kwargs: Unpack[_GroupDecoratorKwargs], - ) -> Callable[ - [ - Union[ - Callable[Concatenate[CogT, ContextT, P], Coro[T]], - Callable[Concatenate[ContextT, P], Coro[T]], - ] - ], - Group[CogT, P, T], - ]: ... + ) -> _CogGroupDecorator[CogT]: ... @overload def group( @@ -1578,15 +1554,7 @@ class GroupMixin(Generic[CogT]): cls: Type[GroupT] = ..., # type: ignore # previous overload handles case where cls is not set *args: Any, **kwargs: Unpack[_GroupDecoratorKwargs], - ) -> Callable[ - [ - Union[ - Callable[Concatenate[CogT, ContextT, P], Coro[T]], - Callable[Concatenate[ContextT, P], Coro[T]], - ] - ], - GroupT, - ]: ... + ) -> _CogGroupDecoratorWithCls[CogT, GroupT]: ... def group( self, @@ -1748,6 +1716,60 @@ if TYPE_CHECKING: def __call__(self, func: Callable[..., Coro[T]], /) -> Any: ... + class _CogCommandDecorator(Generic[CogT]): + @overload + def __call__(self, func: Callable[Concatenate[CogT, ContextT, P], Coro[T]], /) -> Command[CogT, P, T]: ... + + @overload + def __call__(self, func: Callable[Concatenate[ContextT, P], Coro[T]], /) -> Command[CogT, P, T]: ... + + def __call__(self, func: Callable[..., Coro[T]], /) -> Any: ... + + class _CogGroupDecorator(Generic[CogT]): + @overload + def __call__(self, func: Callable[Concatenate[CogT, ContextT, P], Coro[T]], /) -> Group[CogT, P, T]: ... + + @overload + def __call__(self, func: Callable[Concatenate[ContextT, P], Coro[T]], /) -> Group[CogT, P, T]: ... + + def __call__(self, func: Callable[..., Coro[T]], /) -> Any: ... + + class _CommandDecoratorWithCls(Generic[CommandT]): + @overload + def __call__(self, func: Callable[Concatenate[CogT, ContextT, P], Coro[T]], /) -> CommandT: ... + + @overload + def __call__(self, func: Callable[Concatenate[ContextT, P], Coro[T]], /) -> CommandT: ... + + def __call__(self, func: Callable[..., Coro[T]], /) -> Any: ... + + class _GroupDecoratorWithCls(Generic[GroupT]): + @overload + def __call__(self, func: Callable[Concatenate[CogT, ContextT, P], Coro[T]], /) -> GroupT: ... + + @overload + def __call__(self, func: Callable[Concatenate[ContextT, P], Coro[T]], /) -> GroupT: ... + + def __call__(self, func: Callable[..., Coro[T]], /) -> Any: ... + + class _CogCommandDecoratorWithCls(Generic[CogT, CommandT]): + @overload + def __call__(self, func: Callable[Concatenate[CogT, ContextT, P], Coro[T]], /) -> CommandT: ... + + @overload + def __call__(self, func: Callable[Concatenate[ContextT, P], Coro[T]], /) -> CommandT: ... + + def __call__(self, func: Callable[..., Coro[T]], /) -> Any: ... + + class _CogGroupDecoratorWithCls(Generic[CogT, GroupT]): + @overload + def __call__(self, func: Callable[Concatenate[CogT, ContextT, P], Coro[T]], /) -> GroupT: ... + + @overload + def __call__(self, func: Callable[Concatenate[ContextT, P], Coro[T]], /) -> GroupT: ... + + def __call__(self, func: Callable[..., Coro[T]], /) -> Any: ... + @overload def command( @@ -1761,15 +1783,7 @@ def command( name: str = ..., cls: Type[CommandT] = ..., # type: ignore # previous overload handles case where cls is not set **attrs: Unpack[_CommandDecoratorKwargs], -) -> Callable[ - [ - Union[ - Callable[Concatenate[ContextT, P], Coro[Any]], - Callable[Concatenate[CogT, ContextT, P], Coro[Any]], # type: ignore # CogT is used here to allow covariance - ] - ], - CommandT, -]: ... +) -> _CommandDecoratorWithCls[CommandT]: ... def command( @@ -1829,15 +1843,7 @@ def group( name: str = ..., cls: Type[GroupT] = ..., # type: ignore # previous overload handles case where cls is not set **attrs: Unpack[_GroupDecoratorKwargs], -) -> Callable[ - [ - Union[ - Callable[Concatenate[CogT, ContextT, P], Coro[Any]], # type: ignore # CogT is used here to allow covariance - Callable[Concatenate[ContextT, P], Coro[Any]], - ] - ], - GroupT, -]: ... +) -> _GroupDecoratorWithCls[GroupT]: ... def group( diff --git a/discord/ext/commands/hybrid.py b/discord/ext/commands/hybrid.py index e7366c6e8..6655efbb2 100644 --- a/discord/ext/commands/hybrid.py +++ b/discord/ext/commands/hybrid.py @@ -24,7 +24,21 @@ DEALINGS IN THE SOFTWARE. from __future__ import annotations -from typing import TYPE_CHECKING, Any, Callable, ClassVar, Dict, List, Tuple, Type, TypeVar, Union, Optional +from typing import ( + TYPE_CHECKING, + Any, + Callable, + ClassVar, + Dict, + Generic, + List, + Tuple, + Type, + TypeVar, + Union, + Optional, + overload, +) import discord import inspect @@ -98,6 +112,42 @@ if TYPE_CHECKING: Callable[Concatenate[CogT, ContextT, P], Coro[T]], Callable[Concatenate[ContextT, P], Coro[T]], ] + + class _HybridCommandDecorator: + @overload + def __call__(self, func: Callable[Concatenate[CogT, ContextT, P], Coro[T]], /) -> HybridCommand[CogT, P, T]: ... + + @overload + def __call__(self, func: Callable[Concatenate[ContextT, P], Coro[T]], /) -> HybridCommand[None, P, T]: ... # type: ignore + + def __call__(self, func: Callable[..., Coro[T]], /) -> Any: ... + + class _HybridGroupDecorator: + @overload + def __call__(self, func: Callable[Concatenate[CogT, ContextT, P], Coro[T]], /) -> HybridGroup[CogT, P, T]: ... + + @overload + def __call__(self, func: Callable[Concatenate[ContextT, P], Coro[T]], /) -> HybridGroup[None, P, T]: ... # type: ignore + + def __call__(self, func: Callable[..., Coro[T]], /) -> Any: ... + + class _CogHybridCommandDecorator(Generic[CogT]): + @overload + def __call__(self, func: Callable[Concatenate[CogT, ContextT, P], Coro[T]], /) -> HybridCommand[CogT, P, T]: ... + + @overload + def __call__(self, func: Callable[Concatenate[ContextT, P], Coro[T]], /) -> HybridCommand[CogT, P, T]: ... + + def __call__(self, func: Callable[..., Coro[T]], /) -> Any: ... + + class _CogHybridGroupDecorator(Generic[CogT]): + @overload + def __call__(self, func: Callable[Concatenate[CogT, ContextT, P], Coro[T]], /) -> HybridGroup[CogT, P, T]: ... + + @overload + def __call__(self, func: Callable[Concatenate[ContextT, P], Coro[T]], /) -> HybridGroup[CogT, P, T]: ... + + def __call__(self, func: Callable[..., Coro[T]], /) -> Any: ... else: P = TypeVar('P') P2 = TypeVar('P2') @@ -847,7 +897,7 @@ class HybridGroup(Group[CogT, P, T]): *args: Any, with_app_command: bool = True, **kwargs: Unpack[_HybridCommandDecoratorKwargs], # type: ignore # name, with_app_command - ) -> Callable[[CommandCallback[CogT, ContextT, P2, U]], HybridCommand[CogT, P2, U]]: + ) -> _CogHybridCommandDecorator[CogT]: """A shortcut decorator that invokes :func:`~discord.ext.commands.hybrid_command` and adds it to the internal command list via :meth:`add_command`. @@ -863,7 +913,7 @@ class HybridGroup(Group[CogT, P, T]): self.add_command(result) return result - return decorator + return decorator # type: ignore # _CogHybridCommandDecorator only exists under TYPE_CHECKING def group( self, @@ -871,7 +921,7 @@ class HybridGroup(Group[CogT, P, T]): *args: Any, with_app_command: bool = True, **kwargs: Unpack[_HybridGroupDecoratorKwargs], # type: ignore # name, with_app_command - ) -> Callable[[CommandCallback[CogT, ContextT, P2, U]], HybridGroup[CogT, P2, U]]: + ) -> _CogHybridGroupDecorator[CogT]: """A shortcut decorator that invokes :func:`~discord.ext.commands.hybrid_group` and adds it to the internal command list via :meth:`~.GroupMixin.add_command`. @@ -887,7 +937,7 @@ class HybridGroup(Group[CogT, P, T]): self.add_command(result) return result - return decorator + return decorator # type: ignore # _CogHybridGroupDecorator only exists under TYPE_CHECKING def hybrid_command( @@ -895,7 +945,7 @@ def hybrid_command( *, with_app_command: bool = True, **attrs: Unpack[_HybridCommandDecoratorKwargs], # type: ignore # name, with_app_command -) -> Callable[[CommandCallback[CogT, ContextT, P, T]], HybridCommand[CogT, P, T]]: +) -> _HybridCommandDecorator: r"""A decorator that transforms a function into a :class:`.HybridCommand`. A hybrid command is one that functions both as a regular :class:`.Command` @@ -939,7 +989,7 @@ def hybrid_command( # Pyright does not allow Command[Any] to be assigned to Command[CogT] despite it being okay here return HybridCommand(func, name=name, with_app_command=with_app_command, **attrs) # type: ignore # name, with_app_command - return decorator + return decorator # type: ignore # _HybridCommandDecorator only exists under TYPE_CHECKING def hybrid_group( @@ -947,7 +997,7 @@ def hybrid_group( *, with_app_command: bool = True, **attrs: Unpack[_HybridGroupDecoratorKwargs], # type: ignore # name, with_app_command -) -> Callable[[CommandCallback[CogT, ContextT, P, T]], HybridGroup[CogT, P, T]]: +) -> _HybridGroupDecorator: """A decorator that transforms a function into a :class:`.HybridGroup`. This is similar to the :func:`~discord.ext.commands.group` decorator except it creates @@ -972,4 +1022,4 @@ def hybrid_group( raise TypeError('Callback is already a command.') return HybridGroup(func, name=name, with_app_command=with_app_command, **attrs) # type: ignore # name, with_app_command - return decorator + return decorator # type: ignore # _HybridGroupDecorator only exists under TYPE_CHECKING