|
28 | 28 | import logging |
29 | 29 | import sys |
30 | 30 | import types |
| 31 | +from collections.abc import Awaitable, Callable, Iterable |
31 | 32 | from enum import Enum |
32 | 33 | from typing import ( |
33 | 34 | TYPE_CHECKING, |
| 35 | + Any, |
34 | 36 | Literal, |
35 | 37 | Optional, |
36 | 38 | Type, |
| 39 | + TypeVar, |
37 | 40 | Union, |
38 | 41 | get_args, |
39 | 42 | ) |
|
54 | 57 | Thread, |
55 | 58 | VoiceChannel, |
56 | 59 | ) |
57 | | -from ..commands import ApplicationContext |
| 60 | +from ..commands import ApplicationContext, AutocompleteContext |
58 | 61 | from ..enums import ChannelType |
59 | 62 | from ..enums import Enum as DiscordEnum |
60 | 63 | from ..enums import SlashCommandOptionType |
61 | 64 | from ..utils import MISSING, basic_autocomplete |
62 | 65 |
|
63 | 66 | if TYPE_CHECKING: |
| 67 | + from ..cog import Cog |
64 | 68 | from ..ext.commands import Converter |
65 | 69 | from ..member import Member |
66 | 70 | from ..message import Attachment |
|
86 | 90 | Type[DiscordEnum], |
87 | 91 | ] |
88 | 92 |
|
| 93 | + AutocompleteReturnType = Union[ |
| 94 | + Iterable["OptionChoice"], Iterable[str], Iterable[int], Iterable[float] |
| 95 | + ] |
| 96 | + T = TypeVar("T", bound=AutocompleteReturnType) |
| 97 | + MaybeAwaitable = Union[T, Awaitable[T]] |
| 98 | + AutocompleteFunction = Union[ |
| 99 | + Callable[[AutocompleteContext], MaybeAwaitable[AutocompleteReturnType]], |
| 100 | + Callable[[Cog, AutocompleteContext], MaybeAwaitable[AutocompleteReturnType]], |
| 101 | + Callable[ |
| 102 | + [AutocompleteContext, Any], # pyright: ignore [reportExplicitAny] |
| 103 | + MaybeAwaitable[AutocompleteReturnType], |
| 104 | + ], |
| 105 | + Callable[ |
| 106 | + [Cog, AutocompleteContext, Any], # pyright: ignore [reportExplicitAny] |
| 107 | + MaybeAwaitable[AutocompleteReturnType], |
| 108 | + ], |
| 109 | + ] |
| 110 | + |
| 111 | + |
89 | 112 | __all__ = ( |
90 | 113 | "ThreadOption", |
91 | 114 | "Option", |
@@ -162,15 +185,6 @@ class Option: |
162 | 185 | max_length: Optional[:class:`int`] |
163 | 186 | The maximum length of the string that can be entered. Must be between 1 and 6000 (inclusive). |
164 | 187 | Only applies to Options with an :attr:`input_type` of :class:`str`. |
165 | | - autocomplete: Optional[Callable[[:class:`.AutocompleteContext`], Awaitable[Union[Iterable[:class:`.OptionChoice`], Iterable[:class:`str`], Iterable[:class:`int`], Iterable[:class:`float`]]]]] |
166 | | - The autocomplete handler for the option. Accepts a callable (sync or async) |
167 | | - that takes a single argument of :class:`AutocompleteContext`. |
168 | | - The callable must return an iterable of :class:`str` or :class:`OptionChoice`. |
169 | | - Alternatively, :func:`discord.utils.basic_autocomplete` may be used in place of the callable. |
170 | | -
|
171 | | - .. note:: |
172 | | -
|
173 | | - Does not validate the input value against the autocomplete results. |
174 | 188 | channel_types: list[:class:`discord.ChannelType`] | None |
175 | 189 | A list of channel types that can be selected in this option. |
176 | 190 | Only applies to Options with an :attr:`input_type` of :class:`discord.SlashCommandOptionType.channel`. |
@@ -292,6 +306,7 @@ def __init__( |
292 | 306 | ) |
293 | 307 | self.default = kwargs.pop("default", None) |
294 | 308 |
|
| 309 | + self._autocomplete: AutocompleteFunction | None = None |
295 | 310 | self.autocomplete = kwargs.pop("autocomplete", None) |
296 | 311 | if len(enum_choices) > 25: |
297 | 312 | self.choices: list[OptionChoice] = [] |
@@ -443,6 +458,43 @@ def to_dict(self) -> dict: |
443 | 458 | def __repr__(self): |
444 | 459 | return f"<discord.commands.{self.__class__.__name__} name={self.name}>" |
445 | 460 |
|
| 461 | + @property |
| 462 | + def autocomplete(self) -> AutocompleteFunction | None: |
| 463 | + """ |
| 464 | + The autocomplete handler for the option. Accepts a callable (sync or async) |
| 465 | + that takes a single required argument of :class:`AutocompleteContext` or two arguments |
| 466 | + of :class:`discord.Cog` (being the command's cog) and :class:`AutocompleteContext`. |
| 467 | + The callable must return an iterable of :class:`str` or :class:`OptionChoice`. |
| 468 | + Alternatively, :func:`discord.utils.basic_autocomplete` may be used in place of the callable. |
| 469 | +
|
| 470 | + Returns |
| 471 | + ------- |
| 472 | + Optional[AutocompleteFunction] |
| 473 | +
|
| 474 | + .. versionchanged:: 2.7 |
| 475 | +
|
| 476 | + .. note:: |
| 477 | + Does not validate the input value against the autocomplete results. |
| 478 | + """ |
| 479 | + return self._autocomplete |
| 480 | + |
| 481 | + @autocomplete.setter |
| 482 | + def autocomplete(self, value: AutocompleteFunction | None) -> None: |
| 483 | + self._autocomplete = value |
| 484 | + # this is done here so it does not have to be computed every time the autocomplete is invoked |
| 485 | + if self._autocomplete is not None: |
| 486 | + self._autocomplete._is_instance_method = ( # pyright: ignore [reportFunctionMemberAccess] |
| 487 | + sum( |
| 488 | + 1 |
| 489 | + for param in inspect.signature( |
| 490 | + self._autocomplete |
| 491 | + ).parameters.values() |
| 492 | + if param.default == param.empty # pyright: ignore[reportAny] |
| 493 | + and param.kind not in (param.VAR_POSITIONAL, param.VAR_KEYWORD) |
| 494 | + ) |
| 495 | + == 2 |
| 496 | + ) |
| 497 | + |
446 | 498 |
|
447 | 499 | class OptionChoice: |
448 | 500 | """ |
|
0 commit comments