From d12d41c71abeb7af3e3bcf8375c24857110b62f1 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Fri, 16 May 2025 13:01:20 +0300 Subject: [PATCH 01/10] fix: add support for Literal type in typing annotations for Discord choices --- discord/commands/core.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/discord/commands/core.py b/discord/commands/core.py index bee43341fd..386266b38f 100644 --- a/discord/commands/core.py +++ b/discord/commands/core.py @@ -73,9 +73,9 @@ from .options import Option, OptionChoice if sys.version_info >= (3, 11): - from typing import Annotated, get_args, get_origin + from typing import Annotated, Literal, get_args, get_origin else: - from typing_extensions import Annotated, get_args, get_origin + from typing_extensions import Annotated, Literal, get_args, get_origin __all__ = ( "_BaseCommand", @@ -805,6 +805,18 @@ def _parse_options(self, params, *, check_params: bool = True) -> list[Option]: option = p_obj.annotation if option == inspect.Parameter.empty: option = str + if get_origin(option) is Literal: + literal_values = get_args(option) + if not all(isinstance(v, (str, int, float)) for v in literal_values): + raise TypeError( + "Literal values must be str, int, or float for Discord choices." + ) + option = Option( + str, + choices=[ + OptionChoice(name=str(v), value=str(v)) for v in literal_values + ], + ) if self._is_typing_annotated(option): type_hint = get_args(option)[0] From 8df1da72146201d959f7d867f8e1018e659c1fe0 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Fri, 16 May 2025 13:05:16 +0300 Subject: [PATCH 02/10] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85d14e3309..94c157b189 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,8 @@ These changes are available on the `master` branch, but have not yet been releas ([#2714](https://github.com/Pycord-Development/pycord/pull/2714)) - Added the ability to pass a `datetime.time` object to `format_dt` ([#2747](https://github.com/Pycord-Development/pycord/pull/2747)) +- Added support for Literal[...] to define command choices. + ([#2781](https://github.com/Pycord-Development/pycord/pull/2781)) ### Fixed From 1a890428b4bfb11bcb323076255f1c58326dbd1a Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Fri, 16 May 2025 13:16:41 +0300 Subject: [PATCH 03/10] Update CHANGELOG.md Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ee992da65..dfe8709b2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,7 +54,7 @@ These changes are available on the `master` branch, but have not yet been releas - Added the ability to pass a `datetime.time` object to `format_dt` ([#2747](https://github.com/Pycord-Development/pycord/pull/2747)) - Added support for Literal[...] to define command choices. - ([#2781](https://github.com/Pycord-Development/pycord/pull/2781)) + ([#2782](https://github.com/Pycord-Development/pycord/pull/2782)) ### Fixed From fceba5fff5b4d80bf1ef3baaed5b1e3a19d54e72 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Fri, 23 May 2025 09:07:15 +0300 Subject: [PATCH 04/10] Update CHANGELOG.md Co-authored-by: JustaSqu1d <89910983+JustaSqu1d@users.noreply.github.com> Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e88602782c..bc82ef3cb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,7 +55,7 @@ These changes are available on the `master` branch, but have not yet been releas ([#2714](https://github.com/Pycord-Development/pycord/pull/2714)) - Added the ability to pass a `datetime.time` object to `format_dt` ([#2747](https://github.com/Pycord-Development/pycord/pull/2747)) -- Added support for Literal[...] to define command choices. +- Added support for `Literal[...]` to define command choices. ([#2782](https://github.com/Pycord-Development/pycord/pull/2782)) ### Fixed From 45ab8fcf60334c626acc6539a41806bf11baba08 Mon Sep 17 00:00:00 2001 From: Dorukyum <53639936+Dorukyum@users.noreply.github.com> Date: Fri, 6 Jun 2025 12:48:30 +0300 Subject: [PATCH 05/10] refactor: move condition to method --- discord/commands/core.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/discord/commands/core.py b/discord/commands/core.py index 6f4f761d9a..4a78fe514c 100644 --- a/discord/commands/core.py +++ b/discord/commands/core.py @@ -805,7 +805,8 @@ def _parse_options(self, params, *, check_params: bool = True) -> list[Option]: option = p_obj.annotation if option == inspect.Parameter.empty: option = str - if get_origin(option) is Literal: + + if self._is_typing_literal(option): literal_values = get_args(option) if not all(isinstance(v, (str, int, float)) for v in literal_values): raise TypeError( @@ -920,6 +921,9 @@ def _is_typing_union(self, annotation): def _is_typing_optional(self, annotation): return self._is_typing_union(annotation) and type(None) in annotation.__args__ # type: ignore + def _is_typing_literal(self, annotation): + return get_origin(annotation) is Literal + def _is_typing_annotated(self, annotation): return get_origin(annotation) is Annotated From c38ac13fafa63f017e76b6599d255620b0ad4d54 Mon Sep 17 00:00:00 2001 From: Dorukyum <53639936+Dorukyum@users.noreply.github.com> Date: Fri, 6 Jun 2025 12:53:34 +0300 Subject: [PATCH 06/10] docs: update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d1b21443ed..6bb26156f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,7 +55,7 @@ These changes are available on the `master` branch, but have not yet been releas ([#2714](https://github.com/Pycord-Development/pycord/pull/2714)) - Added the ability to pass a `datetime.time` object to `format_dt`. ([#2747](https://github.com/Pycord-Development/pycord/pull/2747)) -- Added support for `Literal[...]` to define command choices. +- Added support for type hinting slash command options with `typing.Annotated`. ([#2782](https://github.com/Pycord-Development/pycord/pull/2782)) - Added `discord.Interaction.created_at`. ([#2801](https://github.com/Pycord-Development/pycord/pull/2801)) From d749864b50ee348327c4a5b7063711e789f2bb0e Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Fri, 6 Jun 2025 15:30:03 +0300 Subject: [PATCH 07/10] fix: ensure all literal values are of the same type in SlashCommand options --- discord/commands/core.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/discord/commands/core.py b/discord/commands/core.py index 4a78fe514c..095e20ce1e 100644 --- a/discord/commands/core.py +++ b/discord/commands/core.py @@ -812,10 +812,15 @@ def _parse_options(self, params, *, check_params: bool = True) -> list[Option]: raise TypeError( "Literal values must be str, int, or float for Discord choices." ) + + value_type = type(literal_values[0]) + if not all(isinstance(v, value_type) for v in literal_values): + raise TypeError("All Literal values must be of the same type.") + option = Option( - str, + value_type, choices=[ - OptionChoice(name=str(v), value=str(v)) for v in literal_values + OptionChoice(name=str(v), value=v) for v in literal_values ], ) From edda069130cb5b37ad23865b3ca8c124ec889fd7 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sat, 28 Jun 2025 14:41:11 +0300 Subject: [PATCH 08/10] Update discord/commands/core.py Co-authored-by: Paillat Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/commands/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/commands/core.py b/discord/commands/core.py index 095e20ce1e..a1596361ae 100644 --- a/discord/commands/core.py +++ b/discord/commands/core.py @@ -810,7 +810,7 @@ def _parse_options(self, params, *, check_params: bool = True) -> list[Option]: literal_values = get_args(option) if not all(isinstance(v, (str, int, float)) for v in literal_values): raise TypeError( - "Literal values must be str, int, or float for Discord choices." + "Literal values for choices must be str, int, or float." ) value_type = type(literal_values[0]) From 08f6df4c8d86d3b6c1d7a8ba62ec4b3100629551 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sat, 28 Jun 2025 14:41:32 +0300 Subject: [PATCH 09/10] Update discord/commands/core.py Co-authored-by: Paillat Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/commands/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/commands/core.py b/discord/commands/core.py index a1596361ae..9d6ebd2c1f 100644 --- a/discord/commands/core.py +++ b/discord/commands/core.py @@ -815,7 +815,7 @@ def _parse_options(self, params, *, check_params: bool = True) -> list[Option]: value_type = type(literal_values[0]) if not all(isinstance(v, value_type) for v in literal_values): - raise TypeError("All Literal values must be of the same type.") + raise TypeError("All Literal values for choices must be of the same type.") option = Option( value_type, From dcfc7556528ef4bbac77fe54e529f2cfd690d32b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 28 Jun 2025 11:41:57 +0000 Subject: [PATCH 10/10] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/commands/core.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/discord/commands/core.py b/discord/commands/core.py index 9d6ebd2c1f..6400ed30f7 100644 --- a/discord/commands/core.py +++ b/discord/commands/core.py @@ -815,7 +815,9 @@ def _parse_options(self, params, *, check_params: bool = True) -> list[Option]: value_type = type(literal_values[0]) if not all(isinstance(v, value_type) for v in literal_values): - raise TypeError("All Literal values for choices must be of the same type.") + raise TypeError( + "All Literal values for choices must be of the same type." + ) option = Option( value_type,