Skip to content
This repository was archived by the owner on Aug 16, 2024. It is now read-only.

Commit a508c0a

Browse files
committed
refactor!: use Annotated for command options
1 parent 901a9cd commit a508c0a

File tree

3 files changed

+64
-44
lines changed

3 files changed

+64
-44
lines changed

examples/interactions/commands/application_commands.py

+27-22
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Annotated
2+
13
import pycord
24

35
# start the bot with no intents, and with default config
@@ -22,28 +24,31 @@ async def favorite(
2224
# The name of this option,
2325
# can be set to anything but
2426
# try to keep it short
25-
anime: str = pycord.Option(
26-
# The description for this option,
27-
# this is a longer version of name displaying
28-
# more detail and technicalities
29-
description='Your favorite Anime Show',
30-
# this just sets it so the user cannot proceed without
31-
# entering this option
32-
required=True,
33-
# enables autocomplete on Discord's side
34-
autocomplete=True,
35-
# these are the choices the user can pick.
36-
# the first value is the name, which is what
37-
# the user will see. The second is the value, which is what
38-
# you, or the bot, will see.
39-
choices=[
40-
pycord.CommandChoice('Attack on Titan'),
41-
pycord.CommandChoice("JoJo's Bizzare Adventure"),
42-
pycord.CommandChoice('Cowboy Bebop'),
43-
pycord.CommandChoice('Hunter x Hunter'),
44-
pycord.CommandChoice('Spy x Family'),
45-
],
46-
),
27+
anime: Annotated[
28+
str,
29+
pycord.Option(
30+
# The description for this option,
31+
# this is a longer version of name displaying
32+
# more detail and technicalities
33+
description='Your favorite Anime Show',
34+
# this just sets it so the user cannot proceed without
35+
# entering this option
36+
required=True,
37+
# enables autocomplete on Discord's side
38+
autocomplete=True,
39+
# these are the choices the user can pick.
40+
# the first value is the name, which is what
41+
# the user will see. The second is the value, which is what
42+
# you, or the bot, will see.
43+
choices=[
44+
pycord.CommandChoice('Attack on Titan'),
45+
pycord.CommandChoice("JoJo's Bizzare Adventure"),
46+
pycord.CommandChoice('Cowboy Bebop'),
47+
pycord.CommandChoice('Hunter x Hunter'),
48+
pycord.CommandChoice('Spy x Family'),
49+
],
50+
),
51+
],
4752
):
4853
"""Pick which one is your favorite anime"""
4954

pycord/commands/application/command.py

+19-20
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222

2323
import asyncio
2424
from copy import copy
25-
from typing import TYPE_CHECKING, Any, Sequence, Union
25+
from inspect import isclass
26+
from typing import TYPE_CHECKING, Any, Sequence, Union, get_origin
2627

2728
from ...channel import Channel, identify_channel
2829
from ...enums import ApplicationCommandOptionType, ApplicationCommandType
@@ -31,17 +32,17 @@
3132
from ...media import Attachment
3233
from ...member import Member
3334
from ...message import Message
35+
from ...missing import MISSING, MissingEnum
3436
from ...role import Role
3537
from ...snowflake import Snowflake
3638
from ...types import AsyncFunc
3739
from ...types.interaction import ApplicationCommandData
38-
from ...missing import MISSING, MissingEnum
3940
from ...user import User
40-
from ...utils import get_arg_defaults, remove_undefined
41+
from ...utils import get_arg_defaults, get_args, remove_undefined
4142
from ..command import Command
4243
from ..group import Group
43-
from .errors import ApplicationCommandException
4444
from .context import Context
45+
from .errors import ApplicationCommandException
4546

4647
if TYPE_CHECKING:
4748
from ...state import State
@@ -195,6 +196,8 @@ def __init__(
195196
else:
196197
if choices:
197198
self.choices = [choice._to_dict() for choice in choices]
199+
else:
200+
self.choices = MISSING
198201
if options is MISSING:
199202
self.options = []
200203
else:
@@ -537,28 +540,24 @@ def _parse_arguments(self) -> None:
537540
continue
538541
elif v[1] is Interaction or v[1] is Context:
539542
continue
540-
elif not isinstance(v[0], Option) and v[0] is not None:
543+
544+
args = get_args(v[1])
545+
546+
if not isinstance(args[1], Option):
541547
raise ApplicationCommandException(
542-
f'Options may only be of type Option, not {v[0]}'
548+
f'Options may only be of type Option, not {args[1]}'
543549
)
544550

545-
if v[0]:
546-
v[0]._param = name
547-
v[0].type = _OPTION_BIND[v[1]].value
551+
option: Option = args[1]
548552

549-
if not v[0].name:
550-
v[0].name = name
553+
option._param = name
554+
option.type = _OPTION_BIND[args[0]].value
551555

552-
self.options.append(v[0])
553-
self._options_dict[v[0].name] = v[0]
554-
else:
555-
if v[1] is None:
556-
raise
556+
if not option.name:
557+
option.name = name
557558

558-
option = Option(name=name, type=v[1])
559-
option._param = name
560-
self.options.append(option)
561-
self._options_dict[name] = option
559+
self.options.append(option)
560+
self._options_dict[option.name] = option
562561

563562
for option in self.options:
564563
self._options.append(option.to_dict())

pycord/utils.py

+18-2
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@
2525
import warnings
2626
from collections.abc import Iterator, Sequence
2727
from itertools import accumulate
28-
from typing import Any, AsyncGenerator, Callable, Type, TypeVar
28+
from typing import Annotated, Any, AsyncGenerator, Callable, Type, TypeVar, get_origin
2929

3030
from aiohttp import ClientResponse
3131

3232
from .file import File
33-
from .types import AsyncFunc
3433
from .missing import MISSING
34+
from .types import AsyncFunc
3535

3636
try:
3737
import msgspec
@@ -270,3 +270,19 @@ def to_datauri(f: File) -> str:
270270
b64 = base64.b64encode(b)
271271

272272
return f'data:{m};base64,{b64}'
273+
274+
275+
def get_args(annotation: Any) -> tuple[Any, ...]:
276+
if get_origin(annotation) is not Annotated:
277+
raise ValueError(
278+
f'Argument annotation {annotation} must originate from typing.Annotated'
279+
)
280+
281+
anns = annotation.__args__ + annotation.__metadata__
282+
283+
if len(anns) != 2:
284+
raise ValueError(
285+
f'Annotation {annotation} must only have two arguments subsequently'
286+
)
287+
288+
return anns

0 commit comments

Comments
 (0)