Skip to content

Commit

Permalink
fix(appcmd): use covariant collection types for choices (#1136)
Browse files Browse the repository at this point in the history
  • Loading branch information
shiftinv authored Nov 25, 2023
1 parent cc5db41 commit 594c12a
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 18 deletions.
1 change: 1 addition & 0 deletions changelog/1136.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Update ``choices`` type in app commands to accept any :class:`~py:typing.Sequence` or :class:`~py:typing.Mapping`, instead of the more constrained :class:`list`/:class:`dict` types.
25 changes: 14 additions & 11 deletions disnake/app_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import math
import re
from abc import ABC
from typing import TYPE_CHECKING, ClassVar, Dict, List, Mapping, Optional, Tuple, Union
from typing import TYPE_CHECKING, ClassVar, List, Mapping, Optional, Sequence, Tuple, Union

from .enums import (
ApplicationCommandPermissionType,
Expand Down Expand Up @@ -37,10 +37,10 @@
)

Choices = Union[
List["OptionChoice"],
List[ApplicationCommandOptionChoiceValue],
Dict[str, ApplicationCommandOptionChoiceValue],
List[Localized[str]],
Sequence["OptionChoice"],
Sequence[ApplicationCommandOptionChoiceValue],
Mapping[str, ApplicationCommandOptionChoiceValue],
Sequence[Localized[str]],
]

APIApplicationCommand = Union["APIUserCommand", "APIMessageCommand", "APISlashCommand"]
Expand Down Expand Up @@ -179,8 +179,8 @@ class Option:
The option type, e.g. :class:`OptionType.user`.
required: :class:`bool`
Whether this option is required.
choices: Union[List[:class:`OptionChoice`], List[Union[:class:`str`, :class:`int`]], Dict[:class:`str`, Union[:class:`str`, :class:`int`]]]
The list of option choices.
choices: Union[Sequence[:class:`OptionChoice`], Sequence[Union[:class:`str`, :class:`int`, :class:`float`]], Mapping[:class:`str`, Union[:class:`str`, :class:`int`, :class:`float`]]]
The pre-defined choices for this option.
options: List[:class:`Option`]
The list of sub options. Normally you don't have to specify it directly,
instead consider using ``@main_cmd.sub_command`` or ``@main_cmd.sub_command_group`` decorators.
Expand Down Expand Up @@ -214,7 +214,7 @@ class Option:
required: :class:`bool`
Whether this option is required.
choices: List[:class:`OptionChoice`]
The list of option choices.
The list of pre-defined choices.
options: List[:class:`Option`]
The list of sub options. Normally you don't have to specify it directly,
instead consider using ``@main_cmd.sub_command`` or ``@main_cmd.sub_command_group`` decorators.
Expand Down Expand Up @@ -304,6 +304,9 @@ def __init__(
if autocomplete:
raise TypeError("can not specify both choices and autocomplete args")

if isinstance(choices, str): # str matches `Sequence[str]`, but isn't meant to be used
raise TypeError("choices argument should be a list/sequence or dict, not str")

if isinstance(choices, Mapping):
self.choices = [OptionChoice(name, value) for name, value in choices.items()]
else:
Expand Down Expand Up @@ -370,7 +373,7 @@ def from_dict(cls, data: ApplicationCommandOptionPayload) -> Option:
def add_choice(
self,
name: LocalizedRequired,
value: Union[str, int],
value: ApplicationCommandOptionChoiceValue,
) -> None:
"""Adds an OptionChoice to the list of current choices,
parameters are the same as for :class:`OptionChoice`.
Expand All @@ -388,7 +391,7 @@ def add_option(
description: LocalizedOptional = None,
type: Optional[OptionType] = None,
required: bool = False,
choices: Optional[List[OptionChoice]] = None,
choices: Optional[Choices] = None,
options: Optional[list] = None,
channel_types: Optional[List[ChannelType]] = None,
autocomplete: bool = False,
Expand Down Expand Up @@ -884,7 +887,7 @@ def add_option(
description: LocalizedOptional = None,
type: Optional[OptionType] = None,
required: bool = False,
choices: Optional[List[OptionChoice]] = None,
choices: Optional[Choices] = None,
options: Optional[list] = None,
channel_types: Optional[List[ChannelType]] = None,
autocomplete: bool = False,
Expand Down
11 changes: 6 additions & 5 deletions disnake/ext/commands/params.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import asyncio
import collections.abc
import copy
import inspect
import itertools
import math
Expand Down Expand Up @@ -451,8 +452,8 @@ class ParamInfo:
.. versionchanged:: 2.5
Added support for localizations.
choices: Union[List[:class:`.OptionChoice`], List[Union[:class:`str`, :class:`int`]], Dict[:class:`str`, Union[:class:`str`, :class:`int`]]]
The list of choices of this slash command option.
choices: Union[Sequence[:class:`.OptionChoice`], Sequence[Union[:class:`str`, :class:`int`, :class:`float`]], Mapping[:class:`str`, Union[:class:`str`, :class:`int`, :class:`float`]]]
The pre-defined choices for this option.
ge: :class:`float`
The lowest allowed value for this option.
le: :class:`float`
Expand Down Expand Up @@ -554,7 +555,7 @@ def copy(self) -> Self:
ins.converter = self.converter
ins.convert_default = self.convert_default
ins.autocomplete = self.autocomplete
ins.choices = self.choices.copy()
ins.choices = copy.copy(self.choices)
ins.type = self.type
ins.channel_types = self.channel_types.copy()
ins.max_value = self.max_value
Expand Down Expand Up @@ -1155,8 +1156,8 @@ def Param(
.. versionchanged:: 2.5
Added support for localizations.
choices: Union[List[:class:`.OptionChoice`], List[Union[:class:`str`, :class:`int`]], Dict[:class:`str`, Union[:class:`str`, :class:`int`]]]
A list of choices for this option.
choices: Union[Sequence[:class:`.OptionChoice`], Sequence[Union[:class:`str`, :class:`int`, :class:`float`]], Mapping[:class:`str`, Union[:class:`str`, :class:`int`, :class:`float`]]]
The pre-defined choices for this slash command option.
converter: Callable[[:class:`.ApplicationCommandInteraction`, Any], Any]
A function that will convert the original input to a desired format.
Kwarg aliases: ``conv``.
Expand Down
7 changes: 5 additions & 2 deletions disnake/interactions/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1260,8 +1260,8 @@ async def autocomplete(self, *, choices: Choices) -> None:
Parameters
----------
choices: Union[List[:class:`OptionChoice`], List[Union[:class:`str`, :class:`int`]], Dict[:class:`str`, Union[:class:`str`, :class:`int`]]]
The list of choices to suggest.
choices: Union[Sequence[:class:`OptionChoice`], Sequence[Union[:class:`str`, :class:`int`, :class:`float`]], Mapping[:class:`str`, Union[:class:`str`, :class:`int`, :class:`float`]]]
The choices to suggest.
Raises
------
Expand All @@ -1277,6 +1277,9 @@ async def autocomplete(self, *, choices: Choices) -> None:
if isinstance(choices, Mapping):
choices_data = [{"name": n, "value": v} for n, v in choices.items()]
else:
if isinstance(choices, str): # str matches `Sequence[str]`, but isn't meant to be used
raise TypeError("choices argument should be a list/sequence or dict, not str")

choices_data = []
value: ApplicationCommandOptionChoicePayload
i18n = self._parent.client.i18n
Expand Down

0 comments on commit 594c12a

Please sign in to comment.