From 47c4be9def9f38f1f14401b2ec8ba07ecc6190ab Mon Sep 17 00:00:00 2001 From: Paillat Date: Thu, 12 Dec 2024 23:08:19 +0100 Subject: [PATCH 01/17] :sparkles: Allow for `functools.partials` and such as autocomplete --- discord/commands/core.py | 4 ++-- discord/commands/options.py | 24 +++++++++++++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/discord/commands/core.py b/discord/commands/core.py index 6dd1b0d636..846e030efc 100644 --- a/discord/commands/core.py +++ b/discord/commands/core.py @@ -1095,13 +1095,13 @@ async def invoke_autocomplete_callback(self, ctx: AutocompleteContext): ctx.value = op.get("value") ctx.options = values - if len(inspect.signature(option.autocomplete).parameters) == 2: + if option.autocomplete._is_instance_method: instance = getattr(option.autocomplete, "__self__", ctx.cog) result = option.autocomplete(instance, ctx) else: result = option.autocomplete(ctx) - if asyncio.iscoroutinefunction(option.autocomplete): + if inspect.isawaitable(result): result = await result choices = [ diff --git a/discord/commands/options.py b/discord/commands/options.py index 4b35a080d9..5fe4db2798 100644 --- a/discord/commands/options.py +++ b/discord/commands/options.py @@ -27,7 +27,7 @@ import inspect import logging from enum import Enum -from typing import TYPE_CHECKING, Literal, Optional, Type, Union +from typing import TYPE_CHECKING, Any, Callable, Literal, Optional, Type, Union from ..abc import GuildChannel, Mentionable from ..channel import ( @@ -272,6 +272,7 @@ def __init__( ) self.default = kwargs.pop("default", None) + self._autocomplete: Callable[[Any], Any, Any] | None = None self.autocomplete = kwargs.pop("autocomplete", None) if len(enum_choices) > 25: self.choices: list[OptionChoice] = [] @@ -390,6 +391,27 @@ def to_dict(self) -> dict: def __repr__(self): return f"" + @property + def autocomplete(self) -> Callable[[Any], Any, Any] | None: + return self._autocomplete + + @autocomplete.setter + def autocomplete(self, value: Callable[[Any], Any, Any] | None) -> None: + self._autocomplete = value + # this is done here so it does not have to be computed every time the autocomplete is invoked + if self._autocomplete is not None: + self._autocomplete._is_instance_method = ( + sum( + 1 + for param in inspect.signature( + self.autocomplete + ).parameters.values() + if param.default == param.empty + and param.kind not in (param.VAR_POSITIONAL, param.VAR_KEYWORD) + ) + == 2 + ) + class OptionChoice: """ From 53036a4c61b9106fc710edf7b9fe8d82522bdee8 Mon Sep 17 00:00:00 2001 From: Paillat Date: Fri, 13 Dec 2024 08:57:56 +0100 Subject: [PATCH 02/17] :memo: CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bda9a0ecc..4bb501bf50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,9 @@ These changes are available on the `master` branch, but have not yet been releas `Permissions.use_external_sounds` and `Permissions.view_creator_monetization_analytics`. ([#2620](https://github.com/Pycord-Development/pycord/pull/2620)) +- Added the ability to use functions with any number of optional arguments, and + functions returning an awaitable as `Option.autocomplete` + ([#2669](https://github.com/Pycord-Development/pycord/pull/2669)). ### Fixed From 7308d08f2bf35fd3f3f3737741dee76444bc1d18 Mon Sep 17 00:00:00 2001 From: Paillat Date: Fri, 13 Dec 2024 09:35:07 +0100 Subject: [PATCH 03/17] :label: Better typing --- discord/commands/options.py | 44 +++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/discord/commands/options.py b/discord/commands/options.py index 5fe4db2798..013e05b190 100644 --- a/discord/commands/options.py +++ b/discord/commands/options.py @@ -27,7 +27,7 @@ import inspect import logging from enum import Enum -from typing import TYPE_CHECKING, Any, Callable, Literal, Optional, Type, Union +from typing import TYPE_CHECKING, Iterable, Literal, Optional, Type, Union from ..abc import GuildChannel, Mentionable from ..channel import ( @@ -39,7 +39,7 @@ Thread, VoiceChannel, ) -from ..commands import ApplicationContext +from ..commands import ApplicationContext, AutocompleteContext from ..enums import ChannelType from ..enums import Enum as DiscordEnum from ..enums import SlashCommandOptionType @@ -111,6 +111,11 @@ def __init__(self, thread_type: Literal["public", "private", "news"]): self._type = type_map[thread_type] +AutocompleteReturnType = Union[ + Iterable["OptionChoice"], Iterable[str], Iterable[int], Iterable[float] +] + + class Option: """Represents a selectable option for a slash command. @@ -146,15 +151,6 @@ class Option: max_length: Optional[:class:`int`] The maximum length of the string that can be entered. Must be between 1 and 6000 (inclusive). Only applies to Options with an :attr:`input_type` of :class:`str`. - autocomplete: Optional[Callable[[:class:`.AutocompleteContext`], Awaitable[Union[Iterable[:class:`.OptionChoice`], Iterable[:class:`str`], Iterable[:class:`int`], Iterable[:class:`float`]]]]] - The autocomplete handler for the option. Accepts a callable (sync or async) - that takes a single argument of :class:`AutocompleteContext`. - The callable must return an iterable of :class:`str` or :class:`OptionChoice`. - Alternatively, :func:`discord.utils.basic_autocomplete` may be used in place of the callable. - - .. note:: - - Does not validate the input value against the autocomplete results. channel_types: list[:class:`discord.ChannelType`] | None A list of channel types that can be selected in this option. Only applies to Options with an :attr:`input_type` of :class:`discord.SlashCommandOptionType.channel`. @@ -272,7 +268,7 @@ def __init__( ) self.default = kwargs.pop("default", None) - self._autocomplete: Callable[[Any], Any, Any] | None = None + self._autocomplete = None self.autocomplete = kwargs.pop("autocomplete", None) if len(enum_choices) > 25: self.choices: list[OptionChoice] = [] @@ -392,11 +388,31 @@ def __repr__(self): return f"" @property - def autocomplete(self) -> Callable[[Any], Any, Any] | None: + def autocomplete(self): return self._autocomplete @autocomplete.setter - def autocomplete(self, value: Callable[[Any], Any, Any] | None) -> None: + def autocomplete(self, value) -> None: + """ + The autocomplete handler for the option. Accepts a callable (sync or async) + that takes a single required argument of :class:`AutocompleteContext`. + The callable must return an iterable of :class:`str` or :class:`OptionChoice`. + Alternatively, :func:`discord.utils.basic_autocomplete` may be used in place of the callable. + + Parameters + ---------- + value: Union[ + Callable[[Self, AutocompleteContext, Any], AutocompleteReturnType], + Callable[[AutocompleteContext, Any], AutocompleteReturnType], + Callable[[Self, AutocompleteContext, Any], Awaitable[AutocompleteReturnType]], + Callable[[AutocompleteContext, Any], Awaitable[AutocompleteReturnType]], + ] + + .. versionchanged:: 2.7 + + .. note:: + Does not validate the input value against the autocomplete results. + """ self._autocomplete = value # this is done here so it does not have to be computed every time the autocomplete is invoked if self._autocomplete is not None: From ebb190418b08ec8737e76a4389d3c6ec14bcf19f Mon Sep 17 00:00:00 2001 From: Paillat Date: Fri, 13 Dec 2024 09:43:18 +0100 Subject: [PATCH 04/17] :truck: Add partial autocomplete example --- .../slash_partial_autocomplete.py | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 examples/app_commands/slash_partial_autocomplete.py diff --git a/examples/app_commands/slash_partial_autocomplete.py b/examples/app_commands/slash_partial_autocomplete.py new file mode 100644 index 0000000000..79be4a9bf9 --- /dev/null +++ b/examples/app_commands/slash_partial_autocomplete.py @@ -0,0 +1,55 @@ +from functools import partial +from os import getenv + +from dotenv import load_dotenv + +import discord +from discord.ext import commands + +load_dotenv() + +bot = discord.Bot() + +fruits = ["Apple", "Banana", "Orange"] +vegetables = ["Carrot", "Lettuce", "Potato"] + + +async def food_autocomplete( + ctx: discord.AutocompleteContext, food_type: str +) -> list[discord.OptionChoice]: + items = fruits if food_type == "fruit" else vegetables + return [ + discord.OptionChoice(name=item) + for item in items + if ctx.value.lower() in item.lower() + ] + + +class FoodCog(commands.Cog): + @commands.slash_command(name="fruit") + async def get_fruit( + self, + ctx: discord.ApplicationContext, + choice: discord.Option( + str, + "Pick a fruit", + autocomplete=partial(food_autocomplete, food_type="fruit"), + ), + ): + await ctx.respond(f"You picked: {choice}") + + @commands.slash_command(name="vegetable") + async def get_vegetable( + self, + ctx: discord.ApplicationContext, + choice: discord.Option( + str, + "Pick a vegetable", + autocomplete=partial(food_autocomplete, food_type="vegetable"), + ), + ): + await ctx.respond(f"You picked: {choice}") + + +bot.add_cog(FoodCog()) +bot.run(getenv("TOKEN")) From 9121251c8f70084f51e249bb5a4da1b6663d3193 Mon Sep 17 00:00:00 2001 From: Paillat Date: Sat, 14 Dec 2024 15:54:50 +0100 Subject: [PATCH 05/17] :adhesive_bandage: Make CI pass --- .../slash_partial_autocomplete.py | 30 ++++++++----------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/examples/app_commands/slash_partial_autocomplete.py b/examples/app_commands/slash_partial_autocomplete.py index 79be4a9bf9..f21499e006 100644 --- a/examples/app_commands/slash_partial_autocomplete.py +++ b/examples/app_commands/slash_partial_autocomplete.py @@ -27,27 +27,21 @@ async def food_autocomplete( class FoodCog(commands.Cog): @commands.slash_command(name="fruit") - async def get_fruit( - self, - ctx: discord.ApplicationContext, - choice: discord.Option( - str, - "Pick a fruit", - autocomplete=partial(food_autocomplete, food_type="fruit"), - ), - ): + @discord.option( + "choice", + "Pick a fruit", + autocomplete=partial(food_autocomplete, food_type="fruit"), + ) + async def get_fruit(self, ctx: discord.ApplicationContext, choice: str): await ctx.respond(f"You picked: {choice}") @commands.slash_command(name="vegetable") - async def get_vegetable( - self, - ctx: discord.ApplicationContext, - choice: discord.Option( - str, - "Pick a vegetable", - autocomplete=partial(food_autocomplete, food_type="vegetable"), - ), - ): + @discord.option( + "choice", + "Pick a vegetable", + autocomplete=partial(food_autocomplete, food_type="vegetable"), + ) + async def get_vegetable(self, ctx: discord.ApplicationContext, choice: str): await ctx.respond(f"You picked: {choice}") From c2b401124763c8bbf39f07a2c7594c19ad19995b Mon Sep 17 00:00:00 2001 From: Paillat Date: Sat, 14 Dec 2024 15:56:05 +0100 Subject: [PATCH 06/17] :memo: Move docstring to getter --- discord/commands/options.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/discord/commands/options.py b/discord/commands/options.py index 013e05b190..ea0bf3e209 100644 --- a/discord/commands/options.py +++ b/discord/commands/options.py @@ -389,23 +389,20 @@ def __repr__(self): @property def autocomplete(self): - return self._autocomplete - - @autocomplete.setter - def autocomplete(self, value) -> None: """ The autocomplete handler for the option. Accepts a callable (sync or async) that takes a single required argument of :class:`AutocompleteContext`. The callable must return an iterable of :class:`str` or :class:`OptionChoice`. Alternatively, :func:`discord.utils.basic_autocomplete` may be used in place of the callable. - Parameters - ---------- - value: Union[ + Returns + ------- + Union[ Callable[[Self, AutocompleteContext, Any], AutocompleteReturnType], Callable[[AutocompleteContext, Any], AutocompleteReturnType], Callable[[Self, AutocompleteContext, Any], Awaitable[AutocompleteReturnType]], Callable[[AutocompleteContext, Any], Awaitable[AutocompleteReturnType]], + None ] .. versionchanged:: 2.7 @@ -413,6 +410,10 @@ def autocomplete(self, value) -> None: .. note:: Does not validate the input value against the autocomplete results. """ + return self._autocomplete + + @autocomplete.setter + def autocomplete(self, value) -> None: self._autocomplete = value # this is done here so it does not have to be computed every time the autocomplete is invoked if self._autocomplete is not None: From 1dddf13747208d23d66bd12c50610a348c6b541d Mon Sep 17 00:00:00 2001 From: Paillat Date: Sat, 14 Dec 2024 16:20:13 +0100 Subject: [PATCH 07/17] :label: Boring typing stuff --- discord/commands/options.py | 51 ++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/discord/commands/options.py b/discord/commands/options.py index ea0bf3e209..8b4649a4a5 100644 --- a/discord/commands/options.py +++ b/discord/commands/options.py @@ -26,8 +26,9 @@ import inspect import logging +from collections.abc import Awaitable, Callable, Iterable from enum import Enum -from typing import TYPE_CHECKING, Iterable, Literal, Optional, Type, Union +from typing import TYPE_CHECKING, Any, Literal, Optional, Type, TypeVar, Union from ..abc import GuildChannel, Mentionable from ..channel import ( @@ -46,6 +47,7 @@ from ..utils import MISSING, basic_autocomplete if TYPE_CHECKING: + from ..cog import Cog from ..ext.commands import Converter from ..member import Member from ..message import Attachment @@ -71,6 +73,25 @@ Type[DiscordEnum], ] + AutocompleteReturnType = Union[ + Iterable["OptionChoice"], Iterable[str], Iterable[int], Iterable[float] + ] + T = TypeVar("T", bound=AutocompleteReturnType) + MaybeAwaitable = Union[T, Awaitable[T]] + AutocompleteFunction = Union[ + Callable[[AutocompleteContext], MaybeAwaitable[AutocompleteReturnType]], + Callable[[Cog, AutocompleteContext], MaybeAwaitable[AutocompleteReturnType]], + Callable[ + [AutocompleteContext, Any], # pyright: ignore [reportExplicitAny] + MaybeAwaitable[AutocompleteReturnType], + ], + Callable[ + [Cog, AutocompleteContext, Any], # pyright: ignore [reportExplicitAny] + MaybeAwaitable[AutocompleteReturnType], + ], + ] + + __all__ = ( "ThreadOption", "Option", @@ -111,11 +132,6 @@ def __init__(self, thread_type: Literal["public", "private", "news"]): self._type = type_map[thread_type] -AutocompleteReturnType = Union[ - Iterable["OptionChoice"], Iterable[str], Iterable[int], Iterable[float] -] - - class Option: """Represents a selectable option for a slash command. @@ -268,7 +284,7 @@ def __init__( ) self.default = kwargs.pop("default", None) - self._autocomplete = None + self._autocomplete: AutocompleteFunction | None = None self.autocomplete = kwargs.pop("autocomplete", None) if len(enum_choices) > 25: self.choices: list[OptionChoice] = [] @@ -388,22 +404,17 @@ def __repr__(self): return f"" @property - def autocomplete(self): + def autocomplete(self) -> AutocompleteFunction | None: """ The autocomplete handler for the option. Accepts a callable (sync or async) - that takes a single required argument of :class:`AutocompleteContext`. + that takes a single required argument of :class:`AutocompleteContext` or two arguments + of :class:`discord.Cog` (being the command's cog) and :class:`AutocompleteContext`. The callable must return an iterable of :class:`str` or :class:`OptionChoice`. Alternatively, :func:`discord.utils.basic_autocomplete` may be used in place of the callable. Returns ------- - Union[ - Callable[[Self, AutocompleteContext, Any], AutocompleteReturnType], - Callable[[AutocompleteContext, Any], AutocompleteReturnType], - Callable[[Self, AutocompleteContext, Any], Awaitable[AutocompleteReturnType]], - Callable[[AutocompleteContext, Any], Awaitable[AutocompleteReturnType]], - None - ] + Optional[AutocompleteFunction] .. versionchanged:: 2.7 @@ -413,17 +424,17 @@ def autocomplete(self): return self._autocomplete @autocomplete.setter - def autocomplete(self, value) -> None: + def autocomplete(self, value: AutocompleteFunction | None) -> None: self._autocomplete = value # this is done here so it does not have to be computed every time the autocomplete is invoked if self._autocomplete is not None: - self._autocomplete._is_instance_method = ( + self._autocomplete._is_instance_method = ( # pyright: ignore [reportFunctionMemberAccess] sum( 1 for param in inspect.signature( - self.autocomplete + self._autocomplete ).parameters.values() - if param.default == param.empty + if param.default == param.empty # pyright: ignore[reportAny] and param.kind not in (param.VAR_POSITIONAL, param.VAR_KEYWORD) ) == 2 From 1f0b69703dd959ecd0a263656bfdd9a1f3ca112b Mon Sep 17 00:00:00 2001 From: Paillat Date: Wed, 18 Dec 2024 17:35:18 +0100 Subject: [PATCH 08/17] :pencil2: Fix writing --- examples/app_commands/slash_partial_autocomplete.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/app_commands/slash_partial_autocomplete.py b/examples/app_commands/slash_partial_autocomplete.py index f21499e006..ca6cd1eab9 100644 --- a/examples/app_commands/slash_partial_autocomplete.py +++ b/examples/app_commands/slash_partial_autocomplete.py @@ -33,7 +33,7 @@ class FoodCog(commands.Cog): autocomplete=partial(food_autocomplete, food_type="fruit"), ) async def get_fruit(self, ctx: discord.ApplicationContext, choice: str): - await ctx.respond(f"You picked: {choice}") + await ctx.respond(f'You picked "{choice}"') @commands.slash_command(name="vegetable") @discord.option( @@ -42,7 +42,7 @@ async def get_fruit(self, ctx: discord.ApplicationContext, choice: str): autocomplete=partial(food_autocomplete, food_type="vegetable"), ) async def get_vegetable(self, ctx: discord.ApplicationContext, choice: str): - await ctx.respond(f"You picked: {choice}") + await ctx.respond(f'You picked "{choice}"') bot.add_cog(FoodCog()) From 8dd4b4feed460d5282147d1a82c6c56566e3f08d Mon Sep 17 00:00:00 2001 From: Paillat Date: Wed, 18 Dec 2024 17:54:49 +0100 Subject: [PATCH 09/17] chore: :alien: Update base max filesize to `10` Mb (#2671) * :alien: Update base max filesize to `10` Mb * :memo: CHANGELOG.md --- CHANGELOG.md | 2 ++ discord/guild.py | 10 +++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 861f66ec0f..3c8d779160 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -77,6 +77,8 @@ These changes are available on the `master` branch, but have not yet been releas - Replaced audioop (deprecated module) implementation of `PCMVolumeTransformer.read` method with a pure Python equivalent. ([#2176](https://github.com/Pycord-Development/pycord/pull/2176)) +- Updated `Guild.filesize_limit` to 10 Mb instead of 25 Mb following Discord's API + changes. ([#2671](https://github.com/Pycord-Development/pycord/pull/2671)) ### Deprecated diff --git a/discord/guild.py b/discord/guild.py index b1e937d07b..337abd31c0 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -289,11 +289,11 @@ class Guild(Hashable): ) _PREMIUM_GUILD_LIMITS: ClassVar[dict[int | None, _GuildLimit]] = { - None: _GuildLimit(emoji=50, stickers=5, bitrate=96e3, filesize=26214400), - 0: _GuildLimit(emoji=50, stickers=5, bitrate=96e3, filesize=26214400), - 1: _GuildLimit(emoji=100, stickers=15, bitrate=128e3, filesize=26214400), - 2: _GuildLimit(emoji=150, stickers=30, bitrate=256e3, filesize=52428800), - 3: _GuildLimit(emoji=250, stickers=60, bitrate=384e3, filesize=104857600), + None: _GuildLimit(emoji=50, stickers=5, bitrate=96e3, filesize=10_485_760), + 0: _GuildLimit(emoji=50, stickers=5, bitrate=96e3, filesize=10_485_760), + 1: _GuildLimit(emoji=100, stickers=15, bitrate=128e3, filesize=10_485_760), + 2: _GuildLimit(emoji=150, stickers=30, bitrate=256e3, filesize=52_428_800), + 3: _GuildLimit(emoji=250, stickers=60, bitrate=384e3, filesize=104_857_600), } def __init__(self, *, data: GuildPayload, state: ConnectionState): From 33c5f21eaa601a3f495a4c4a7613e0563c9b7f6e Mon Sep 17 00:00:00 2001 From: Paillat Date: Thu, 26 Dec 2024 23:08:37 +0100 Subject: [PATCH 10/17] :memo: Requested changes --- CHANGELOG.md | 4 ++-- discord/commands/options.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3735324562..9fbb5acbbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,8 +37,8 @@ These changes are available on the `master` branch, but have not yet been releas `Permissions.view_creator_monetization_analytics`. ([#2620](https://github.com/Pycord-Development/pycord/pull/2620)) - Added the ability to use functions with any number of optional arguments, and - functions returning an awaitable as `Option.autocomplete` - ([#2669](https://github.com/Pycord-Development/pycord/pull/2669)). + functions returning an awaitable as `Option.autocomplete`. + ([#2669](https://github.com/Pycord-Development/pycord/pull/2669)) ### Fixed diff --git a/discord/commands/options.py b/discord/commands/options.py index 8b4649a4a5..2d640e45a0 100644 --- a/discord/commands/options.py +++ b/discord/commands/options.py @@ -414,7 +414,7 @@ def autocomplete(self) -> AutocompleteFunction | None: Returns ------- - Optional[AutocompleteFunction] + Optional[AutocompleteFunction] .. versionchanged:: 2.7 From 3e71fc98993942e7f3706ed582d4a4a0c859a93f Mon Sep 17 00:00:00 2001 From: Paillat Date: Sat, 28 Dec 2024 11:02:34 +0100 Subject: [PATCH 11/17] :memo: Grammar --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 077af33b41..00b2446d79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,8 +36,8 @@ These changes are available on the `master` branch, but have not yet been releas `Permissions.use_external_sounds`, and `Permissions.view_creator_monetization_analytics`. ([#2620](https://github.com/Pycord-Development/pycord/pull/2620)) -- Added the ability to use functions with any number of optional arguments, and - functions returning an awaitable as `Option.autocomplete`. +- Added the ability to use functions with any number of optional arguments and functions + returning an awaitable as `Option.autocomplete`. ([#2669](https://github.com/Pycord-Development/pycord/pull/2669)) ### Fixed From 4eeb9fde260ba8e6ea8c2666ec03687edfea2fec Mon Sep 17 00:00:00 2001 From: Paillat-dev Date: Sun, 16 Feb 2025 14:55:55 +0100 Subject: [PATCH 12/17] :recycle: Merge the 2 examples --- examples/app_commands/slash_autocomplete.py | 37 ++++++++++++++ .../slash_partial_autocomplete.py | 49 ------------------- 2 files changed, 37 insertions(+), 49 deletions(-) delete mode 100644 examples/app_commands/slash_partial_autocomplete.py diff --git a/examples/app_commands/slash_autocomplete.py b/examples/app_commands/slash_autocomplete.py index 5d888732de..4213bfc5fc 100644 --- a/examples/app_commands/slash_autocomplete.py +++ b/examples/app_commands/slash_autocomplete.py @@ -1,3 +1,5 @@ +from functools import partial + import discord from discord.commands import option @@ -196,4 +198,39 @@ async def autocomplete_basic_example( await ctx.respond(f"You picked {color} as your color, and {animal} as your animal!") +FRUITS = ["Apple", "Banana", "Orange"] +VEGETABLES = ["Carrot", "Lettuce", "Potato"] + + +async def food_autocomplete( + ctx: discord.AutocompleteContext, food_type: str +) -> list[discord.OptionChoice]: + items = FRUITS if food_type == "fruit" else VEGETABLES + return [ + discord.OptionChoice(name=item) + for item in items + if ctx.value.lower() in item.lower() + ] + + +@bot.slash_command(name="fruit") +@option( + "choice", + "Pick a fruit", + autocomplete=partial(food_autocomplete, food_type="fruit"), +) +async def get_fruit(self, ctx: discord.ApplicationContext, choice: str): + await ctx.respond(f'You picked "{choice}"') + + +@bot.slash_command(name="vegetable") +@option( + "choice", + "Pick a vegetable", + autocomplete=partial(food_autocomplete, food_type="vegetable"), +) +async def get_vegetable(self, ctx: discord.ApplicationContext, choice: str): + await ctx.respond(f'You picked "{choice}"') + + bot.run("TOKEN") diff --git a/examples/app_commands/slash_partial_autocomplete.py b/examples/app_commands/slash_partial_autocomplete.py deleted file mode 100644 index ca6cd1eab9..0000000000 --- a/examples/app_commands/slash_partial_autocomplete.py +++ /dev/null @@ -1,49 +0,0 @@ -from functools import partial -from os import getenv - -from dotenv import load_dotenv - -import discord -from discord.ext import commands - -load_dotenv() - -bot = discord.Bot() - -fruits = ["Apple", "Banana", "Orange"] -vegetables = ["Carrot", "Lettuce", "Potato"] - - -async def food_autocomplete( - ctx: discord.AutocompleteContext, food_type: str -) -> list[discord.OptionChoice]: - items = fruits if food_type == "fruit" else vegetables - return [ - discord.OptionChoice(name=item) - for item in items - if ctx.value.lower() in item.lower() - ] - - -class FoodCog(commands.Cog): - @commands.slash_command(name="fruit") - @discord.option( - "choice", - "Pick a fruit", - autocomplete=partial(food_autocomplete, food_type="fruit"), - ) - async def get_fruit(self, ctx: discord.ApplicationContext, choice: str): - await ctx.respond(f'You picked "{choice}"') - - @commands.slash_command(name="vegetable") - @discord.option( - "choice", - "Pick a vegetable", - autocomplete=partial(food_autocomplete, food_type="vegetable"), - ) - async def get_vegetable(self, ctx: discord.ApplicationContext, choice: str): - await ctx.respond(f'You picked "{choice}"') - - -bot.add_cog(FoodCog()) -bot.run(getenv("TOKEN")) From d8323e6ec8b4be29962e606ceeaafb1441e9e6ac Mon Sep 17 00:00:00 2001 From: Paillat Date: Sat, 2 Aug 2025 18:12:28 +0200 Subject: [PATCH 13/17] :coffin: remove conflicting autocomplete attribute from `Option` --- discord/commands/options.py | 1 - 1 file changed, 1 deletion(-) diff --git a/discord/commands/options.py b/discord/commands/options.py index 82de4291d6..da469229fd 100644 --- a/discord/commands/options.py +++ b/discord/commands/options.py @@ -289,7 +289,6 @@ def __init__( self.default = kwargs.pop("default", None) self._autocomplete: AutocompleteFunction | None = None - self.autocomplete = kwargs.pop("autocomplete", None) if len(enum_choices) > 25: self.choices: list[OptionChoice] = [] for e in enum_choices: From 09be0b315599668e66a620b593c421f3b780133b Mon Sep 17 00:00:00 2001 From: Paillat-dev Date: Fri, 5 Sep 2025 13:36:32 +0200 Subject: [PATCH 14/17] :bug: Fix missing setting autocomplete --- discord/commands/options.py | 1 + 1 file changed, 1 insertion(+) diff --git a/discord/commands/options.py b/discord/commands/options.py index 7b10e0eb63..013c2c5c91 100644 --- a/discord/commands/options.py +++ b/discord/commands/options.py @@ -291,6 +291,7 @@ def __init__( self.default = kwargs.pop("default", None) self._autocomplete: AutocompleteFunction | None = None + self.autocomplete = kwargs.pop("autocomplete", None) if len(enum_choices) > 25: self.choices: list[OptionChoice] = [] for e in enum_choices: From 68b565a1188246d0855f33c2852af2b5e2d6e3ea Mon Sep 17 00:00:00 2001 From: Paillat Date: Tue, 16 Sep 2025 14:24:36 +0200 Subject: [PATCH 15/17] Copilot Signed-off-by: Paillat --- examples/app_commands/slash_autocomplete.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/app_commands/slash_autocomplete.py b/examples/app_commands/slash_autocomplete.py index 4213bfc5fc..9e952546d2 100644 --- a/examples/app_commands/slash_autocomplete.py +++ b/examples/app_commands/slash_autocomplete.py @@ -219,7 +219,7 @@ async def food_autocomplete( "Pick a fruit", autocomplete=partial(food_autocomplete, food_type="fruit"), ) -async def get_fruit(self, ctx: discord.ApplicationContext, choice: str): +async def get_fruit(ctx: discord.ApplicationContext, choice: str): await ctx.respond(f'You picked "{choice}"') @@ -229,7 +229,7 @@ async def get_fruit(self, ctx: discord.ApplicationContext, choice: str): "Pick a vegetable", autocomplete=partial(food_autocomplete, food_type="vegetable"), ) -async def get_vegetable(self, ctx: discord.ApplicationContext, choice: str): +async def get_vegetable(ctx: discord.ApplicationContext, choice: str): await ctx.respond(f'You picked "{choice}"') From 4758dc4e7ad1273627cd98fd24ba8b6999f75485 Mon Sep 17 00:00:00 2001 From: Paillat Date: Tue, 16 Sep 2025 14:26:51 +0200 Subject: [PATCH 16/17] :memo: CHANGELOG.md Signed-off-by: Paillat --- CHANGELOG.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47c96a707a..4b9d26c6db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,9 @@ These changes are available on the `master` branch, but have not yet been releas - Adds pre-typed and pre-constructed with select_type `ui.Select` aliases for the different select types: `ui.StringSelect`, `ui.UserSelect`, `ui.RoleSelect`, `ui.MentionableSelect`, and `ui.ChannelSelect`. +- Added the ability to use functions with any number of optional arguments and functions + returning an awaitable as `Option.autocomplete`. + ([#2669](https://github.com/Pycord-Development/pycord/pull/2914)) ### Changed @@ -45,6 +48,8 @@ These changes are available on the `master` branch, but have not yet been releas ([#2924](https://github.com/Pycord-Development/pycord/pull/2924)) - Fixed OPUS Decode Error when recording audio. ([#2925](https://github.com/Pycord-Development/pycord/pull/2925)) +- Fixed autocomplete crashing when using an async static method + ([#2669](https://github.com/Pycord-Development/pycord/pull/2914)) ### Removed @@ -100,9 +105,6 @@ These changes are available on the `master` branch, but have not yet been releas ([#2564](https://github.com/Pycord-Development/pycord/pull/2564)) - Added `Message.forward_to`, `Message.snapshots`, and other related attributes. ([#2598](https://github.com/Pycord-Development/pycord/pull/2598)) -- Added the ability to use functions with any number of optional arguments and functions - returning an awaitable as `Option.autocomplete`. - ([#2669](https://github.com/Pycord-Development/pycord/pull/2669)) - Add missing `Guild` feature flags and `Guild.edit` parameters. ([#2672](https://github.com/Pycord-Development/pycord/pull/2672)) - Added the ability to change the API's base URL with `Route.API_BASE_URL`. From a53eadfa28e791f2f036303db606f25b2e1d04a7 Mon Sep 17 00:00:00 2001 From: Paillat Date: Tue, 16 Sep 2025 14:28:59 +0200 Subject: [PATCH 17/17] Update CHANGELOG.md Signed-off-by: Paillat --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b9d26c6db..ed7ab88154 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,7 +48,7 @@ These changes are available on the `master` branch, but have not yet been releas ([#2924](https://github.com/Pycord-Development/pycord/pull/2924)) - Fixed OPUS Decode Error when recording audio. ([#2925](https://github.com/Pycord-Development/pycord/pull/2925)) -- Fixed autocomplete crashing when using an async static method +- Fixed autocomplete crashing when using an async static method. ([#2669](https://github.com/Pycord-Development/pycord/pull/2914)) ### Removed