diff --git a/discord/app_commands/commands.py b/discord/app_commands/commands.py index 11af21f8f..8fb916145 100644 --- a/discord/app_commands/commands.py +++ b/discord/app_commands/commands.py @@ -167,12 +167,20 @@ def validate_context_menu_name(name: str) -> str: def _validate_auto_complete_callback( - callback: AutocompleteCallback[GroupT, ChoiceT] + callback: AutocompleteCallback[GroupT, ChoiceT], + skip_binding: bool = False, ) -> AutocompleteCallback[GroupT, ChoiceT]: - requires_binding = is_inside_class(callback) - required_parameters = 2 + requires_binding + binding = getattr(callback, '__self__', None) + if binding is not None: + callback = callback.__func__ + + requires_binding = (binding is None and is_inside_class(callback)) or skip_binding + callback.requires_binding = requires_binding + callback.binding = binding + + required_parameters = 2 + requires_binding params = inspect.signature(callback).parameters if len(params) != required_parameters: raise TypeError('autocomplete callback requires either 2 or 3 parameters to be passed') @@ -581,8 +589,9 @@ class Command(Generic[GroupT, P, T]): raise CommandSignatureMismatch(self) if param.autocomplete.requires_binding: - if self.binding is not None: - choices = await param.autocomplete(self.binding, interaction, value) + binding = param.autocomplete.binding or self.binding + if binding is not None: + choices = await param.autocomplete(binding, interaction, value) else: raise TypeError('autocomplete parameter expected a bound self parameter but one was not provided') else: diff --git a/discord/app_commands/transformers.py b/discord/app_commands/transformers.py index ad78d5fab..de52bbbc6 100644 --- a/discord/app_commands/transformers.py +++ b/discord/app_commands/transformers.py @@ -61,6 +61,8 @@ __all__ = ( ) T = TypeVar('T') +FuncT = TypeVar('FuncT', bound=Callable[..., Any]) +ChoiceT = TypeVar('ChoiceT', str, int, float, Union[str, int, float]) NoneType = type(None) if TYPE_CHECKING: @@ -249,6 +251,35 @@ class Transformer: """ raise NotImplementedError('Derived classes need to implement this.') + @classmethod + async def autocomplete( + cls, interaction: Interaction, value: Union[int, float, str] + ) -> List[Choice[Union[int, float, str]]]: + """|coro| + + An autocomplete prompt handler to be automatically used by options using this transformer. + + .. note:: + + Autocomplete is only supported for options with a :meth:`~discord.app_commands.Transformer.type` + of :attr:`~discord.AppCommandOptionType.string`, :attr:`~discord.AppCommandOptionType.integer`, + or :attr:`~discord.AppCommandOptionType.number`. + + Parameters + ----------- + interaction: :class:`~discord.Interaction` + The autocomplete interaction being handled. + value: Union[:class:`str`, :class:`int`, :class:`float`] + The current value entered by the user. + + Returns + -------- + List[:class:`~discord.app_commands.Choice`] + A list of choices to be displayed to the user, a maximum of 25. + + """ + raise NotImplementedError('Derived classes can implement this.') + class _TransformMetadata: __discord_app_commands_transform__: ClassVar[bool] = True @@ -679,4 +710,9 @@ def annotation_to_parameter(annotation: Any, parameter: inspect.Parameter) -> Co if parameter.kind in (parameter.POSITIONAL_ONLY, parameter.VAR_KEYWORD, parameter.VAR_POSITIONAL): raise TypeError(f'unsupported parameter kind in callback: {parameter.kind!s}') + if inner.autocomplete is not Transformer.autocomplete: + from .commands import _validate_auto_complete_callback + + result.autocomplete = _validate_auto_complete_callback(inner.autocomplete, skip_binding=True) + return result