Use Unpack where it's possible

This commit is contained in:
Soheab
2025-08-15 11:36:36 +02:00
committed by GitHub
parent 13432591c6
commit 983a9b8f94
13 changed files with 383 additions and 103 deletions

View File

@ -64,7 +64,7 @@ from .cog import Cog
from .hybrid import hybrid_command, hybrid_group, HybridCommand, HybridGroup
if TYPE_CHECKING:
from typing_extensions import Self
from typing_extensions import Self, Unpack
import importlib.machinery
@ -80,12 +80,24 @@ if TYPE_CHECKING:
MaybeAwaitableFunc,
)
from .core import Command
from .hybrid import CommandCallback, ContextT, P
from .hybrid import CommandCallback, ContextT, P, _HybridCommandDecoratorKwargs, _HybridGroupDecoratorKwargs
from discord.client import _ClientOptions
from discord.shard import _AutoShardedClientOptions
_Prefix = Union[Iterable[str], str]
_PrefixCallable = MaybeAwaitableFunc[[BotT, Message], _Prefix]
PrefixType = Union[_Prefix, _PrefixCallable[BotT]]
class _BotOptions(_ClientOptions, total=False):
owner_id: int
owner_ids: Collection[int]
strip_after_prefix: bool
case_insensitive: bool
class _AutoShardedBotOptions(_AutoShardedClientOptions, _BotOptions):
...
__all__ = (
'when_mentioned',
'when_mentioned_or',
@ -169,7 +181,7 @@ class BotBase(GroupMixin[None]):
allowed_contexts: app_commands.AppCommandContext = MISSING,
allowed_installs: app_commands.AppInstallationType = MISSING,
intents: discord.Intents,
**options: Any,
**options: Unpack[_BotOptions],
) -> None:
super().__init__(intents=intents, **options)
self.command_prefix: PrefixType[BotT] = command_prefix # type: ignore
@ -281,7 +293,7 @@ class BotBase(GroupMixin[None]):
name: Union[str, app_commands.locale_str] = MISSING,
with_app_command: bool = True,
*args: Any,
**kwargs: Any,
**kwargs: Unpack[_HybridCommandDecoratorKwargs], # type: ignore # name, with_app_command
) -> Callable[[CommandCallback[Any, ContextT, P, T]], HybridCommand[Any, P, T]]:
"""A shortcut decorator that invokes :func:`~discord.ext.commands.hybrid_command` and adds it to
the internal command list via :meth:`add_command`.
@ -293,8 +305,8 @@ class BotBase(GroupMixin[None]):
"""
def decorator(func: CommandCallback[Any, ContextT, P, T]):
kwargs.setdefault('parent', self)
result = hybrid_command(name=name, *args, with_app_command=with_app_command, **kwargs)(func)
kwargs.setdefault('parent', self) # type: ignore # parent is not for the user to set
result = hybrid_command(name=name, *args, with_app_command=with_app_command, **kwargs)(func) # type: ignore # name, with_app_command
self.add_command(result)
return result
@ -305,7 +317,7 @@ class BotBase(GroupMixin[None]):
name: Union[str, app_commands.locale_str] = MISSING,
with_app_command: bool = True,
*args: Any,
**kwargs: Any,
**kwargs: Unpack[_HybridGroupDecoratorKwargs], # type: ignore # name, with_app_command
) -> Callable[[CommandCallback[Any, ContextT, P, T]], HybridGroup[Any, P, T]]:
"""A shortcut decorator that invokes :func:`~discord.ext.commands.hybrid_group` and adds it to
the internal command list via :meth:`add_command`.
@ -317,8 +329,8 @@ class BotBase(GroupMixin[None]):
"""
def decorator(func: CommandCallback[Any, ContextT, P, T]):
kwargs.setdefault('parent', self)
result = hybrid_group(name=name, *args, with_app_command=with_app_command, **kwargs)(func)
kwargs.setdefault('parent', self) # type: ignore # parent is not for the user to set
result = hybrid_group(name=name, *args, with_app_command=with_app_command, **kwargs)(func) # type: ignore # name, with_app_command
self.add_command(result)
return result
@ -1527,4 +1539,18 @@ class AutoShardedBot(BotBase, discord.AutoShardedClient):
.. versionadded:: 2.0
"""
pass
if TYPE_CHECKING:
def __init__(
self,
command_prefix: PrefixType[BotT],
*,
help_command: Optional[HelpCommand] = _default,
tree_cls: Type[app_commands.CommandTree[Any]] = app_commands.CommandTree,
description: Optional[str] = None,
allowed_contexts: app_commands.AppCommandContext = MISSING,
allowed_installs: app_commands.AppInstallationType = MISSING,
intents: discord.Intents,
**kwargs: Unpack[_AutoShardedBotOptions],
) -> None:
...

View File

@ -44,18 +44,30 @@ from typing import (
Tuple,
TypeVar,
Union,
TypedDict,
)
from ._types import _BaseCommand, BotT
if TYPE_CHECKING:
from typing_extensions import Self
from typing_extensions import Self, Unpack
from discord.abc import Snowflake
from discord._types import ClientT
from .bot import BotBase
from .context import Context
from .core import Command
from .core import Command, _CommandDecoratorKwargs
class _CogKwargs(TypedDict, total=False):
name: str
group_name: Union[str, app_commands.locale_str]
description: str
group_description: Union[str, app_commands.locale_str]
group_nsfw: bool
group_auto_locale_strings: bool
group_extras: Dict[Any, Any]
command_attrs: _CommandDecoratorKwargs
__all__ = (
'CogMeta',
@ -169,7 +181,7 @@ class CogMeta(type):
__cog_app_commands__: List[Union[app_commands.Group, app_commands.Command[Any, ..., Any]]]
__cog_listeners__: List[Tuple[str, str]]
def __new__(cls, *args: Any, **kwargs: Any) -> CogMeta:
def __new__(cls, *args: Any, **kwargs: Unpack[_CogKwargs]) -> CogMeta:
name, bases, attrs = args
if any(issubclass(base, app_commands.Group) for base in bases):
raise TypeError(

View File

@ -43,6 +43,7 @@ from typing import (
TypeVar,
Union,
overload,
TypedDict,
)
import re
@ -58,10 +59,39 @@ from .parameters import Parameter, Signature
from discord.app_commands.commands import NUMPY_DOCSTRING_ARG_REGEX
if TYPE_CHECKING:
from typing_extensions import Concatenate, ParamSpec, Self
from typing_extensions import Concatenate, ParamSpec, Self, Unpack
from ._types import BotT, Check, ContextT, Coro, CoroFunc, Error, Hook, UserCheck
from discord.permissions import _PermissionsKwargs
class _CommandDecoratorKwargs(TypedDict, total=False):
enabled: bool
help: str
brief: str
usage: str
rest_is_raw: bool
aliases: List[str]
description: str
hidden: bool
checks: List[UserCheck[Context[Any]]]
cooldown: CooldownMapping[Context[Any]]
max_concurrency: MaxConcurrency
require_var_positional: bool
cooldown_after_parsing: bool
ignore_extra: bool
extras: Dict[Any, Any]
class _CommandKwargs(_CommandDecoratorKwargs, total=False):
name: str
class _GroupDecoratorKwargs(_CommandDecoratorKwargs, total=False):
invoke_without_command: bool
case_insensitive: bool
class _GroupKwargs(_GroupDecoratorKwargs, total=False):
name: str
__all__ = (
'Command',
@ -393,7 +423,7 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
Callable[Concatenate[Context[Any], P], Coro[T]],
],
/,
**kwargs: Any,
**kwargs: Unpack[_CommandKwargs],
) -> None:
if not asyncio.iscoroutinefunction(func):
raise TypeError('Callback must be a coroutine.')
@ -556,7 +586,7 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
except ValueError:
pass
def update(self, **kwargs: Any) -> None:
def update(self, **kwargs: Unpack[_CommandKwargs]) -> None:
"""Updates :class:`Command` instance with updated attribute.
This works similarly to the :func:`~discord.ext.commands.command` decorator in terms
@ -1468,7 +1498,7 @@ class GroupMixin(Generic[CogT]):
self: GroupMixin[CogT],
name: str = ...,
*args: Any,
**kwargs: Any,
**kwargs: Unpack[_CommandDecoratorKwargs],
) -> Callable[
[
Union[
@ -1486,7 +1516,7 @@ class GroupMixin(Generic[CogT]):
name: str = ...,
cls: Type[CommandT] = ..., # type: ignore # previous overload handles case where cls is not set
*args: Any,
**kwargs: Any,
**kwargs: Unpack[_CommandDecoratorKwargs],
) -> Callable[
[
Union[
@ -1503,7 +1533,7 @@ class GroupMixin(Generic[CogT]):
name: str = MISSING,
cls: Type[Command[Any, ..., Any]] = MISSING,
*args: Any,
**kwargs: Any,
**kwargs: Unpack[_CommandDecoratorKwargs],
) -> Any:
"""A shortcut decorator that invokes :func:`~discord.ext.commands.command` and adds it to
the internal command list via :meth:`~.GroupMixin.add_command`.
@ -1515,8 +1545,7 @@ class GroupMixin(Generic[CogT]):
"""
def decorator(func):
kwargs.setdefault('parent', self)
kwargs.setdefault('parent', self) # type: ignore # the parent kwarg is not for users to set.
result = command(name=name, cls=cls, *args, **kwargs)(func)
self.add_command(result)
return result
@ -1528,7 +1557,7 @@ class GroupMixin(Generic[CogT]):
self: GroupMixin[CogT],
name: str = ...,
*args: Any,
**kwargs: Any,
**kwargs: Unpack[_GroupDecoratorKwargs],
) -> Callable[
[
Union[
@ -1546,7 +1575,7 @@ class GroupMixin(Generic[CogT]):
name: str = ...,
cls: Type[GroupT] = ..., # type: ignore # previous overload handles case where cls is not set
*args: Any,
**kwargs: Any,
**kwargs: Unpack[_GroupDecoratorKwargs],
) -> Callable[
[
Union[
@ -1563,7 +1592,7 @@ class GroupMixin(Generic[CogT]):
name: str = MISSING,
cls: Type[Group[Any, ..., Any]] = MISSING,
*args: Any,
**kwargs: Any,
**kwargs: Unpack[_GroupDecoratorKwargs],
) -> Any:
"""A shortcut decorator that invokes :func:`.group` and adds it to
the internal command list via :meth:`~.GroupMixin.add_command`.
@ -1575,7 +1604,7 @@ class GroupMixin(Generic[CogT]):
"""
def decorator(func):
kwargs.setdefault('parent', self)
kwargs.setdefault('parent', self) # type: ignore # the parent kwarg is not for users to set.
result = group(name=name, cls=cls, *args, **kwargs)(func)
self.add_command(result)
return result
@ -1606,7 +1635,7 @@ class Group(GroupMixin[CogT], Command[CogT, P, T]):
Defaults to ``False``.
"""
def __init__(self, *args: Any, **attrs: Any) -> None:
def __init__(self, *args: Any, **attrs: Unpack[_GroupKwargs]) -> None:
self.invoke_without_command: bool = attrs.pop('invoke_without_command', False)
super().__init__(*args, **attrs)
@ -1728,7 +1757,7 @@ if TYPE_CHECKING:
@overload
def command(
name: str = ...,
**attrs: Any,
**attrs: Unpack[_CommandDecoratorKwargs],
) -> _CommandDecorator:
...
@ -1737,7 +1766,7 @@ def command(
def command(
name: str = ...,
cls: Type[CommandT] = ..., # type: ignore # previous overload handles case where cls is not set
**attrs: Any,
**attrs: Unpack[_CommandDecoratorKwargs],
) -> Callable[
[
Union[
@ -1753,7 +1782,7 @@ def command(
def command(
name: str = MISSING,
cls: Type[Command[Any, ..., Any]] = MISSING,
**attrs: Any,
**attrs: Unpack[_CommandDecoratorKwargs],
) -> Any:
"""A decorator that transforms a function into a :class:`.Command`
or if called with :func:`.group`, :class:`.Group`.
@ -1798,7 +1827,7 @@ def command(
@overload
def group(
name: str = ...,
**attrs: Any,
**attrs: Unpack[_GroupDecoratorKwargs],
) -> _GroupDecorator:
...
@ -1807,7 +1836,7 @@ def group(
def group(
name: str = ...,
cls: Type[GroupT] = ..., # type: ignore # previous overload handles case where cls is not set
**attrs: Any,
**attrs: Unpack[_GroupDecoratorKwargs],
) -> Callable[
[
Union[
@ -1823,7 +1852,7 @@ def group(
def group(
name: str = MISSING,
cls: Type[Group[Any, ..., Any]] = MISSING,
**attrs: Any,
**attrs: Unpack[_GroupDecoratorKwargs],
) -> Any:
"""A decorator that transforms a function into a :class:`.Group`.
@ -2165,7 +2194,7 @@ def bot_has_any_role(*items: int) -> Callable[[T], T]:
return check(predicate)
def has_permissions(**perms: bool) -> Check[Any]:
def has_permissions(**perms: Unpack[_PermissionsKwargs]) -> Check[Any]:
"""A :func:`.check` that is added that checks if the member has all of
the permissions necessary.
@ -2212,7 +2241,7 @@ def has_permissions(**perms: bool) -> Check[Any]:
return check(predicate)
def bot_has_permissions(**perms: bool) -> Check[Any]:
def bot_has_permissions(**perms: Unpack[_PermissionsKwargs]) -> Check[Any]:
"""Similar to :func:`.has_permissions` except checks if the bot itself has
the permissions listed.
@ -2237,7 +2266,7 @@ def bot_has_permissions(**perms: bool) -> Check[Any]:
return check(predicate)
def has_guild_permissions(**perms: bool) -> Check[Any]:
def has_guild_permissions(**perms: Unpack[_PermissionsKwargs]) -> Check[Any]:
"""Similar to :func:`.has_permissions`, but operates on guild wide
permissions instead of the current channel permissions.
@ -2266,7 +2295,7 @@ def has_guild_permissions(**perms: bool) -> Check[Any]:
return check(predicate)
def bot_has_guild_permissions(**perms: bool) -> Check[Any]:
def bot_has_guild_permissions(**perms: Unpack[_PermissionsKwargs]) -> Check[Any]:
"""Similar to :func:`.has_guild_permissions`, but checks the bot
members guild permissions.

View File

@ -42,6 +42,7 @@ from typing import (
Iterable,
Sequence,
Mapping,
TypedDict,
)
import discord.utils
@ -50,7 +51,7 @@ from .core import Group, Command, get_signature_parameters
from .errors import CommandError
if TYPE_CHECKING:
from typing_extensions import Self
from typing_extensions import Self, Unpack
import discord.abc
@ -58,6 +59,7 @@ if TYPE_CHECKING:
from .context import Context
from .cog import Cog
from .parameters import Parameter
from .core import _CommandKwargs
from ._types import (
UserCheck,
@ -65,6 +67,30 @@ if TYPE_CHECKING:
_Bot,
)
class _HelpCommandOptions(TypedDict, total=False):
show_hidden: bool
verify_checks: bool
command_attrs: _CommandKwargs
class _BaseHelpCommandOptions(_HelpCommandOptions, total=False):
sort_commands: bool
dm_help: bool
dm_help_threshold: int
no_category: str
paginator: Paginator
commands_heading: str
class _DefaultHelpCommandOptions(_BaseHelpCommandOptions, total=False):
width: int
indent: int
arguments_heading: str
default_argument_description: str
show_parameter_descriptions: bool
class _MinimalHelpCommandOptions(_BaseHelpCommandOptions, total=False):
aliases_heading: str
__all__ = (
'Paginator',
'HelpCommand',
@ -224,7 +250,7 @@ def _not_overridden(f: FuncT) -> FuncT:
class _HelpCommandImpl(Command):
def __init__(self, inject: HelpCommand, *args: Any, **kwargs: Any) -> None:
def __init__(self, inject: HelpCommand, *args: Any, **kwargs: Unpack[_CommandKwargs]) -> None:
super().__init__(inject.command_callback, *args, **kwargs)
self._original: HelpCommand = inject
self._injected: HelpCommand = inject
@ -299,7 +325,7 @@ class _HelpCommandImpl(Command):
def update(self, **kwargs: Any) -> None:
cog = self.cog
self.__init__(self._original, **dict(self.__original_kwargs__, **kwargs))
self.__init__(self._original, **dict(self.__original_kwargs__, **kwargs)) # type: ignore
self.cog = cog
@ -366,10 +392,9 @@ class HelpCommand:
self.__original_args__ = deepcopy(args)
return self
def __init__(self, **options: Any) -> None:
def __init__(self, **options: Unpack[_HelpCommandOptions]) -> None:
self.show_hidden: bool = options.pop('show_hidden', False)
self.verify_checks: bool = options.pop('verify_checks', True)
self.command_attrs: Dict[str, Any]
self.command_attrs = attrs = options.pop('command_attrs', {})
attrs.setdefault('name', 'help')
attrs.setdefault('help', 'Shows this message')
@ -1041,7 +1066,7 @@ class DefaultHelpCommand(HelpCommand):
The paginator used to paginate the help command output.
"""
def __init__(self, **options: Any) -> None:
def __init__(self, **options: Unpack[_DefaultHelpCommandOptions]) -> None:
self.width: int = options.pop('width', 80)
self.indent: int = options.pop('indent', 2)
self.sort_commands: bool = options.pop('sort_commands', True)
@ -1051,11 +1076,13 @@ class DefaultHelpCommand(HelpCommand):
self.commands_heading: str = options.pop('commands_heading', 'Commands:')
self.default_argument_description: str = options.pop('default_argument_description', 'No description given')
self.no_category: str = options.pop('no_category', 'No Category')
self.paginator: Paginator = options.pop('paginator', None)
self.show_parameter_descriptions: bool = options.pop('show_parameter_descriptions', True)
if self.paginator is None:
paginator = options.pop('paginator', None)
if paginator is None:
self.paginator: Paginator = Paginator()
else:
self.paginator: Paginator = paginator
super().__init__(**options)
@ -1334,17 +1361,19 @@ class MinimalHelpCommand(HelpCommand):
The paginator used to paginate the help command output.
"""
def __init__(self, **options: Any) -> None:
def __init__(self, **options: Unpack[_MinimalHelpCommandOptions]) -> None:
self.sort_commands: bool = options.pop('sort_commands', True)
self.commands_heading: str = options.pop('commands_heading', 'Commands')
self.dm_help: bool = options.pop('dm_help', False)
self.dm_help_threshold: int = options.pop('dm_help_threshold', 1000)
self.aliases_heading: str = options.pop('aliases_heading', 'Aliases:')
self.no_category: str = options.pop('no_category', 'No Category')
self.paginator: Paginator = options.pop('paginator', None)
if self.paginator is None:
paginator = options.pop('paginator', None)
if paginator is None:
self.paginator: Paginator = Paginator(suffix=None, prefix=None)
else:
self.paginator: Paginator = paginator
super().__init__(**options)

View File

@ -24,19 +24,7 @@ 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, List, Tuple, Type, TypeVar, Union, Optional
import discord
import inspect
@ -51,7 +39,7 @@ from .cog import Cog
from .view import StringView
if TYPE_CHECKING:
from typing_extensions import Self, ParamSpec, Concatenate
from typing_extensions import Self, ParamSpec, Concatenate, Unpack
from ._types import ContextT, Coro, BotT
from .bot import Bot
from .context import Context
@ -60,6 +48,29 @@ if TYPE_CHECKING:
AutocompleteCallback,
ChoiceT,
)
from .core import _CommandKwargs
class _HybridCommandKwargs(_CommandKwargs, total=False):
guild_ids: list[int]
guild_only: bool
default_permissions: bool
nsfw: bool
with_app_command: bool
class _HybridCommandDecoratorKwargs(_HybridCommandKwargs, total=False):
description: Union[str, app_commands.locale_str]
class _HybridGroupKwargs(_HybridCommandDecoratorKwargs, total=False):
with_app_command: bool
guild_ids: list[int]
guild_only: bool
default_permissions: bool
nsfw: bool
description: str
class _HybridGroupDecoratorKwargs(_HybridGroupKwargs, total=False):
description: Union[str, app_commands.locale_str]
fallback: Union[str, app_commands.locale_str]
__all__ = (
@ -501,7 +512,7 @@ class HybridCommand(Command[CogT, P, T]):
*,
name: Union[str, app_commands.locale_str] = MISSING,
description: Union[str, app_commands.locale_str] = MISSING,
**kwargs: Any,
**kwargs: Unpack[_HybridCommandKwargs], # type: ignore # name, description
) -> None:
name, name_locale = (name.message, name) if isinstance(name, app_commands.locale_str) else (name, None)
if name is not MISSING:
@ -621,7 +632,7 @@ class HybridGroup(Group[CogT, P, T]):
name: Union[str, app_commands.locale_str] = MISSING,
description: Union[str, app_commands.locale_str] = MISSING,
fallback: Optional[Union[str, app_commands.locale_str]] = None,
**attrs: Any,
**attrs: Unpack[_HybridGroupKwargs], # type: ignore # name, description
) -> None:
name, name_locale = (name.message, name) if isinstance(name, app_commands.locale_str) else (name, None)
if name is not MISSING:
@ -825,7 +836,7 @@ class HybridGroup(Group[CogT, P, T]):
name: Union[str, app_commands.locale_str] = MISSING,
*args: Any,
with_app_command: bool = True,
**kwargs: Any,
**kwargs: Unpack[_HybridCommandDecoratorKwargs], # type: ignore # name, with_app_command
) -> Callable[[CommandCallback[CogT, ContextT, P2, U]], HybridCommand[CogT, P2, U]]:
"""A shortcut decorator that invokes :func:`~discord.ext.commands.hybrid_command` and adds it to
the internal command list via :meth:`add_command`.
@ -837,8 +848,8 @@ class HybridGroup(Group[CogT, P, T]):
"""
def decorator(func: CommandCallback[CogT, ContextT, P2, U]):
kwargs.setdefault('parent', self)
result = hybrid_command(name=name, *args, with_app_command=with_app_command, **kwargs)(func)
kwargs.setdefault('parent', self) # type: ignore # parent is not for users to set
result = hybrid_command(name=name, *args, with_app_command=with_app_command, **kwargs)(func) # type: ignore # name, with_app_command
self.add_command(result)
return result
@ -849,7 +860,7 @@ class HybridGroup(Group[CogT, P, T]):
name: Union[str, app_commands.locale_str] = MISSING,
*args: Any,
with_app_command: bool = True,
**kwargs: Any,
**kwargs: Unpack[_HybridGroupDecoratorKwargs], # type: ignore # name, with_app_command
) -> Callable[[CommandCallback[CogT, ContextT, P2, U]], HybridGroup[CogT, P2, U]]:
"""A shortcut decorator that invokes :func:`~discord.ext.commands.hybrid_group` and adds it to
the internal command list via :meth:`~.GroupMixin.add_command`.
@ -861,8 +872,8 @@ class HybridGroup(Group[CogT, P, T]):
"""
def decorator(func: CommandCallback[CogT, ContextT, P2, U]):
kwargs.setdefault('parent', self)
result = hybrid_group(name=name, *args, with_app_command=with_app_command, **kwargs)(func)
kwargs.setdefault('parent', self) # type: ignore # parent is not for users to set
result = hybrid_group(name=name, *args, with_app_command=with_app_command, **kwargs)(func) # type: ignore # name, with_app_command
self.add_command(result)
return result
@ -873,7 +884,7 @@ def hybrid_command(
name: Union[str, app_commands.locale_str] = MISSING,
*,
with_app_command: bool = True,
**attrs: Any,
**attrs: Unpack[_HybridCommandDecoratorKwargs], # type: ignore # name, with_app_command
) -> Callable[[CommandCallback[CogT, ContextT, P, T]], HybridCommand[CogT, P, T]]:
r"""A decorator that transforms a function into a :class:`.HybridCommand`.
@ -916,7 +927,7 @@ def hybrid_command(
if isinstance(func, Command):
raise TypeError('Callback is already a 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
return HybridCommand(func, name=name, with_app_command=with_app_command, **attrs) # type: ignore # name, with_app_command
return decorator
@ -925,7 +936,7 @@ def hybrid_group(
name: Union[str, app_commands.locale_str] = MISSING,
*,
with_app_command: bool = True,
**attrs: Any,
**attrs: Unpack[_HybridGroupDecoratorKwargs], # type: ignore # name, with_app_command
) -> Callable[[CommandCallback[CogT, ContextT, P, T]], HybridGroup[CogT, P, T]]:
"""A decorator that transforms a function into a :class:`.HybridGroup`.
@ -949,6 +960,6 @@ def hybrid_group(
def decorator(func: CommandCallback[CogT, ContextT, P, T]) -> HybridGroup[CogT, P, T]:
if isinstance(func, Command):
raise TypeError('Callback is already a command.')
return HybridGroup(func, name=name, with_app_command=with_app_command, **attrs)
return HybridGroup(func, name=name, with_app_command=with_app_command, **attrs) # type: ignore # name, with_app_command
return decorator