Skip to content

(Somehow) Allow autocomplete methods to accept arguments #2668

Open
@DefiDebauchery

Description

What is the feature request for?

The core library

The Problem

This stream of consciousness delves into parts of python I'm not fully familiar with, and is fairly contrived without testing, so apologies in advance for typos or incorrect concepts.

With autocompletes, I thought it might be useful to allow arguments to the autocomplete itself, which could generalize related searches with different parameters without cluttering the application with one-shot methods.

Let's assume the following basic example:

import discord
from discord.ext import commands

class Pets(commands.Cog):
	async def pet_autocomplete(self, ctx: discord.AutocompleteContext) -> list[discord.OptionChoice]:
        search_term = ctx.value.casefold()
        return [
            discord.OptionChoice(name=animal['name'], value=animal['id'])
            for animal in self.pet_datasource if search_term in animal['name'].casefold()
        ]

    @commands.slash_command(description='Get pet info')
    async def pet_by_name(
		self,
		ctx: discord.ApplicationContext,
		pet: discord.Option(str, "Target Pet", autocomplete=pet_autocomplete)
	) -> None:
		# ... This part doesn't matter

Now say we want to have commands that specifically only concern dogs (without adding a secondary option). Granted, we could always create a separate autocomplete method, referencing that in our new slash command:

	async def dog_autocomplete(self, ctx: discord.AutocompleteContext) -> list[OptionChoice]:
		search_term = ctx.value.casefold()
		return [
            discord.OptionChoice(name=animal['name'], value=animal['id'])
            for animal in self.pet_datasource if search_term in animal['name'].casefold()
			and animal['type'] == 'dog'
        ]

	@commands.slash_command(description="Who's a good dog?")
	async def vote_for_dog(
		self,
		ctx: discord.ApplicationContext,
		dog: discord.Option(str, "Dog's Name", autocomplete=dog_autocomplete)
	) -> None:
		# ...

And of course, we could move the logic of the autocomplete to a generalized search function and have the AC make the . But it would be even more cool for the autocomplete method itself to be more generic, taking in arguments from the autocomplete= arg in discord.Option. This would allow us to have something like

	async def pet_autocomplete(self, ctx: discord.AutocompleteContext, pet_type: str = None) -> list[OptionChoice]:
		search_term = ctx.value.casefold()
		return [
            discord.OptionChoice(name=animal['name'], value=animal['id'])
            for animal in self.pet_datasource if search_term in animal['name'].casefold()
			and (animal['type'] == pet_type if pet_type else True)
		]

The first hurdle is the autocomplete handler itself. It requires an AutocompleteContext, so of course we cannot execute the callback methods directly.

Two ideas I immediately had were functools.partial and lambdas. Neither of these work, and I suspect for the same reason. First, a visual on how these might look:

	@commands.slash_command(description="Who's a good dog?")
	async def vote_for_dog(
		self,
		ctx: discord.ApplicationContext,
		dog: discord.Option(str, "Dog's Name", autocomplete=functools.partial(dog_autocomplete, pet_type='dog')
	) -> None:
		# ...

	# -- or -- #

	@commands.slash_command(description="Who's a good dog?")
	async def vote_for_dog(
		self,
		ctx: discord.ApplicationContext,
		dog: discord.Option(str, "Dog's Name", autocomplete=lambda self, *args: self.dog_autocomplete(*args, pet_type='dog')
	) -> None:
		# ...

As an aside: The lambda syntax came after a few different iterations. I always thought it was interesting that my autocomplete argument was the bare def. And once I put it in the lambda, it lost all context and complained that dog_autocomplete was an unresolved reference. I also couldn't immediately reference self.dog_autocomplete because self was unresolved -- passing that into the lambda solved all the reference errors.

Anyway, both approaches yielded the same outcome: dog_autocomplete was never awaited

However, when I removed async from my autocomplete method(s), the lambda approach worked! Unfortunately, in my production context, this isn't viable, but is at least some bit of progress. There are workarounds to 'async lambdas', but I think there's a valid use case in having this Just Work within Pycord.

If anyone smarter than me could take a look, it'd simplify my classes quite a bit to have something like this available!

The Ideal Solution

Easily allow argument passing to autocomplete method references within Option()s, preferably without the complex syntax of lambdas.

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions