1414 Literal ,
1515 Protocol ,
1616 TypeAlias ,
17+ TypedDict ,
1718 TypeVar ,
1819 Union ,
1920 cast ,
5960if TYPE_CHECKING :
6061 from typing import Concatenate
6162
62- from typing_extensions import ParamSpec , Self
63+ from typing_extensions import ParamSpec , Self , Unpack
6364
6465 from disnake .message import Message
6566
6667 from ._types import AppCheck , Check , Coro , CoroFunc , Error , Hook
6768
69+ class _CommandArgs (TypedDict , total = False ):
70+ enabled : bool
71+ help : str | None
72+ brief : str | None
73+ usage : str | None
74+ rest_is_raw : bool
75+ aliases : list [str ] | tuple [str , ...]
76+ extras : dict [str , Any ]
77+ description : str
78+ hidden : bool
79+ checks : list [Check ]
80+ cooldown : CooldownMapping | None
81+ max_concurrency : MaxConcurrency | None
82+ require_var_positional : bool
83+ ignore_extra : bool
84+ cooldown_after_parsing : bool
85+ parent : GroupMixin [Any ] | None
86+
87+ class _GroupArgs (_CommandArgs , total = False ):
88+ invoke_without_command : bool
89+ case_insensitive : bool
90+
6891
6992__all__ = (
7093 "Command" ,
@@ -201,7 +224,7 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
201224 The short help text for the command.
202225 usage: :class:`str` | :data:`None`
203226 A replacement for arguments in the default help text.
204- aliases: :class:`list`\[:class:`str`] | :class:`tuple`\[:class:`str`]
227+ aliases: :class:`list`\[:class:`str`] | :class:`tuple`\[:class:`str`, ... ]
205228 The list of aliases the command can be invoked under.
206229 enabled: :class:`bool`
207230 Whether the command is currently enabled.
@@ -277,13 +300,15 @@ def __new__(cls, *args: Any, **kwargs: Any) -> Self:
277300 def __init__ (
278301 self ,
279302 func : CommandCallback [CogT , ContextT , P , T ],
280- ** kwargs : Any ,
303+ * ,
304+ name : str | None = None ,
305+ ** kwargs : Unpack [_CommandArgs ],
281306 ) -> None :
282307 if not inspect .iscoroutinefunction (func ):
283308 msg = "Callback must be a coroutine function."
284309 raise TypeError (msg )
285310
286- name = kwargs . get ( " name" ) or func .__name__
311+ name = name or func .__name__
287312 if not isinstance (name , str ):
288313 msg = "Name of a command must be a string."
289314 raise TypeError (msg )
@@ -305,7 +330,7 @@ def __init__(
305330 self .brief : str | None = kwargs .get ("brief" )
306331 self .usage : str | None = kwargs .get ("usage" )
307332 self .rest_is_raw : bool = kwargs .get ("rest_is_raw" , False )
308- self .aliases : list [str ] | tuple [str ] = kwargs .get ("aliases" , [])
333+ self .aliases : list [str ] | tuple [str , ... ] = kwargs .get ("aliases" , [])
309334 self .extras : dict [str , Any ] = kwargs .get ("extras" , {})
310335
311336 if not isinstance (self .aliases , (list , tuple )):
@@ -351,7 +376,7 @@ def __init__(
351376
352377 # bandaid for the fact that sometimes parent can be the bot instance
353378 parent = kwargs .get ("parent" )
354- self .parent : GroupMixin | None = parent if isinstance (parent , _BaseCommand ) else None # pyright: ignore[reportAttributeAccessIssue]
379+ self .parent : GroupMixin | None = parent if isinstance (parent , _BaseCommand ) else None
355380
356381 self ._before_invoke : Hook | None = None
357382 try :
@@ -433,6 +458,8 @@ def remove_check(self, func: Check) -> None:
433458 except ValueError :
434459 pass
435460
461+ # n.b. this continues to use `**kwargs: Any` instead of _CommandArgs,
462+ # since custom command subclasses may accept additional parameters
436463 def update (self , ** kwargs : Any ) -> None :
437464 """Updates :class:`Command` instance with updated attribute.
438465
@@ -1283,18 +1310,16 @@ def get_command(self, name: str) -> Command[CogT, Any, Any] | None:
12831310 @overload
12841311 def command (
12851312 self ,
1286- name : str ,
1287- cls : type [CommandT ],
1288- * args : Any ,
1289- ** kwargs : Any ,
1290- ) -> Callable [[CommandCallback [CogT , ContextT , P , T ]], CommandT ]: ...
1313+ name : str = ...,
1314+ ** attrs : Unpack [_CommandArgs ],
1315+ ) -> Callable [[CommandCallback [CogT , ContextT , P , T ]], Command [CogT , P , T ]]: ...
12911316
12921317 @overload
12931318 def command (
12941319 self ,
1295- name : str = ...,
1296- * args : Any ,
1320+ name : str ,
12971321 cls : type [CommandT ],
1322+ * args : Any ,
12981323 ** kwargs : Any ,
12991324 ) -> Callable [[CommandCallback [CogT , ContextT , P , T ]], CommandT ]: ...
13001325
@@ -1303,8 +1328,9 @@ def command(
13031328 self ,
13041329 name : str = ...,
13051330 * args : Any ,
1331+ cls : type [CommandT ],
13061332 ** kwargs : Any ,
1307- ) -> Callable [[CommandCallback [CogT , ContextT , P , T ]], Command [ CogT , P , T ] ]: ...
1333+ ) -> Callable [[CommandCallback [CogT , ContextT , P , T ]], CommandT ]: ...
13081334
13091335 def command (
13101336 self ,
@@ -1333,18 +1359,16 @@ def decorator(func: CommandCallback[CogT, ContextT, P, T]) -> Command[Any, Any,
13331359 @overload
13341360 def group (
13351361 self ,
1336- name : str ,
1337- cls : type [GroupT ],
1338- * args : Any ,
1339- ** kwargs : Any ,
1340- ) -> Callable [[CommandCallback [CogT , ContextT , P , T ]], GroupT ]: ...
1362+ name : str = ...,
1363+ ** attrs : Unpack [_GroupArgs ],
1364+ ) -> Callable [[CommandCallback [CogT , ContextT , P , T ]], Group [CogT , P , T ]]: ...
13411365
13421366 @overload
13431367 def group (
13441368 self ,
1345- name : str = ...,
1346- * args : Any ,
1369+ name : str ,
13471370 cls : type [GroupT ],
1371+ * args : Any ,
13481372 ** kwargs : Any ,
13491373 ) -> Callable [[CommandCallback [CogT , ContextT , P , T ]], GroupT ]: ...
13501374
@@ -1353,8 +1377,9 @@ def group(
13531377 self ,
13541378 name : str = ...,
13551379 * args : Any ,
1380+ cls : type [GroupT ],
13561381 ** kwargs : Any ,
1357- ) -> Callable [[CommandCallback [CogT , ContextT , P , T ]], Group [ CogT , P , T ] ]: ...
1382+ ) -> Callable [[CommandCallback [CogT , ContextT , P , T ]], GroupT ]: ...
13581383
13591384 def group (
13601385 self ,
@@ -1404,9 +1429,9 @@ class Group(GroupMixin[CogT], Command[CogT, P, T]):
14041429 Defaults to ``False``.
14051430 """
14061431
1407- def __init__ (self , * args : Any , ** attrs : Any ) -> None :
1432+ def __init__ (self , * args : Any , name : str | None = None , ** attrs : Unpack [ _GroupArgs ] ) -> None :
14081433 self .invoke_without_command : bool = attrs .pop ("invoke_without_command" , False )
1409- super ().__init__ (* args , ** attrs )
1434+ super ().__init__ (* args , name = name , ** attrs )
14101435
14111436 def copy (self : GroupT ) -> GroupT :
14121437 """Creates a copy of this :class:`Group`.
@@ -1522,15 +1547,17 @@ def __call__(
15221547 ) -> Group [CogT , P , T ]: ...
15231548
15241549
1525- # Small explanation regarding these overloads:
1526- # The overloads with the `cls` parameter need to be first,
1527- # as the other overload would otherwise match first even if `cls` is given.
1528- # To prevent the overloads with `cls` from matching everything, the parameter
1529- # cannot have a default value, which in turn means it has to be split into two
1530- # overloads, one with a positional `cls` parameter and one with a kwarg parameter,
1531- # as `name` should still be optional.
1550+ @overload
1551+ def command (
1552+ name : str = ...,
1553+ ** attrs : Unpack [_CommandArgs ],
1554+ ) -> CommandDecorator : ...
15321555
15331556
1557+ # Typing **attrs correctly here is not possible with current ParamSpec/Concatenate features,
1558+ # as it does not support adding kw-only arguments.
1559+ # This overload is split into two, since `cls` cannot have a default without implicitly
1560+ # becoming a fallback when the previous overload didn't match (due to typo'd parameters, etc.)
15341561@overload
15351562def command (
15361563 name : str ,
@@ -1548,19 +1575,12 @@ def command(
15481575) -> Callable [[CommandCallback [CogT , ContextT , P , T ]], CommandT ]: ...
15491576
15501577
1551- @overload
1552- def command (
1553- name : str = ...,
1554- ** attrs : Any ,
1555- ) -> CommandDecorator : ...
1556-
1557-
15581578def command (
15591579 name : str = MISSING ,
15601580 cls : type [Command [Any , Any , Any ]] = MISSING ,
15611581 ** attrs : Any ,
15621582) -> Any :
1563- """A decorator that transforms a function into a :class:`.Command`
1583+ r """A decorator that transforms a function into a :class:`.Command`
15641584 or if called with :func:`.group`, :class:`.Group`.
15651585
15661586 By default the ``help`` attribute is received automatically from the
@@ -1580,7 +1600,7 @@ def command(
15801600 cls
15811601 The class to construct with. By default this is :class:`.Command`.
15821602 You usually do not change this.
1583- attrs
1603+ \*\* attrs
15841604 Keyword arguments to pass into the construction of the class denoted
15851605 by ``cls``.
15861606
@@ -1603,16 +1623,14 @@ def decorator(func: CommandCallback[CogT, ContextT, P, T]) -> Command[Any, Any,
16031623
16041624@overload
16051625def group (
1606- name : str ,
1607- cls : type [GroupT ],
1608- ** attrs : Any ,
1609- ) -> Callable [[CommandCallback [CogT , ContextT , P , T ]], GroupT ]: ...
1626+ name : str = ...,
1627+ ** attrs : Unpack [_GroupArgs ],
1628+ ) -> GroupDecorator : ...
16101629
16111630
16121631@overload
16131632def group (
1614- name : str = ...,
1615- * ,
1633+ name : str ,
16161634 cls : type [GroupT ],
16171635 ** attrs : Any ,
16181636) -> Callable [[CommandCallback [CogT , ContextT , P , T ]], GroupT ]: ...
@@ -1621,8 +1639,10 @@ def group(
16211639@overload
16221640def group (
16231641 name : str = ...,
1642+ * ,
1643+ cls : type [GroupT ],
16241644 ** attrs : Any ,
1625- ) -> GroupDecorator : ...
1645+ ) -> Callable [[ CommandCallback [ CogT , ContextT , P , T ]], GroupT ] : ...
16261646
16271647
16281648def group (
0 commit comments