Skip to content

Commit 1ec7128

Browse files
authored
feat: add ability to use different arg name than option name (#1493)
* feat: add ability to use different arg name than option name * feat: raise better error if arg name doesnt exist * fix: use ValueError instead of TypeError TypeError seems to be suppressed
1 parent 0b467bd commit 1ec7128

File tree

3 files changed

+62
-15
lines changed

3 files changed

+62
-15
lines changed

interactions/ext/hybrid_commands/hybrid_slash.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ def slash_to_prefixed(cmd: HybridSlashCommand) -> _HybridToPrefixedCommand: # n
351351
# there isn't much we can do here
352352
raise ValueError("Autocomplete is unsupported in hybrid commands.")
353353

354-
name = str(option.name)
354+
name = option.argument_name or str(option.name)
355355
annotation = inspect.Parameter.empty
356356
default = inspect.Parameter.empty
357357
kind = inspect.Parameter.POSITIONAL_ONLY if cmd._uses_arg else inspect.Parameter.POSITIONAL_OR_KEYWORD

interactions/models/internal/annotations/slash.py

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ def slash_str_option(
3535
choices: List[Union["SlashCommandChoice", dict]] | None = None,
3636
min_length: Optional[int] = None,
3737
max_length: Optional[int] = None,
38+
name: Optional[str] = None,
3839
) -> Type[str]:
3940
"""
4041
Annotates an argument as a string type slash command option.
@@ -46,10 +47,11 @@ def slash_str_option(
4647
choices: The choices allowed by this command
4748
min_length: The minimum length of text a user can input.
4849
max_length: The maximum length of text a user can input.
50+
name: The name of the option. Defaults to the name of the argument
4951
5052
"""
5153
return SlashCommandOption(
52-
name="placeholder",
54+
name=name,
5355
description=description,
5456
required=required,
5557
autocomplete=autocomplete,
@@ -67,6 +69,7 @@ def slash_float_option(
6769
choices: List[Union["SlashCommandChoice", dict]] | None = None,
6870
min_value: Optional[float] = None,
6971
max_value: Optional[float] = None,
72+
name: Optional[str] = None,
7073
) -> Type[float]:
7174
"""
7275
Annotates an argument as a float type slash command option.
@@ -78,10 +81,11 @@ def slash_float_option(
7881
choices: The choices allowed by this command
7982
min_value: The minimum number allowed
8083
max_value: The maximum number allowed
84+
name: The name of the option. Defaults to the name of the argument
8185
8286
"""
8387
return SlashCommandOption(
84-
name="placeholder",
88+
name=name,
8589
description=description,
8690
required=required,
8791
autocomplete=autocomplete,
@@ -99,6 +103,7 @@ def slash_int_option(
99103
choices: List[Union["SlashCommandChoice", dict]] | None = None,
100104
min_value: Optional[float] = None,
101105
max_value: Optional[float] = None,
106+
name: Optional[str] = None,
102107
) -> Type[int]:
103108
"""
104109
Annotates an argument as a integer type slash command option.
@@ -110,10 +115,11 @@ def slash_int_option(
110115
choices: The choices allowed by this command
111116
min_value: The minimum number allowed
112117
max_value: The maximum number allowed
118+
name: The name of the option. Defaults to the name of the argument
113119
114120
"""
115121
return SlashCommandOption(
116-
name="placeholder",
122+
name=name,
117123
description=description,
118124
required=required,
119125
autocomplete=autocomplete,
@@ -127,17 +133,19 @@ def slash_int_option(
127133
def slash_bool_option(
128134
description: str,
129135
required: bool = False,
136+
name: Optional[str] = None,
130137
) -> Type[bool]:
131138
"""
132139
Annotates an argument as a boolean type slash command option.
133140
134141
Args:
135142
description: The description of your option
136143
required: Is this option required?
144+
name: The name of the option. Defaults to the name of the argument
137145
138146
"""
139147
return SlashCommandOption(
140-
name="placeholder",
148+
name=name,
141149
description=description,
142150
required=required,
143151
type=models.OptionType.BOOLEAN,
@@ -148,6 +156,7 @@ def slash_user_option(
148156
description: str,
149157
required: bool = False,
150158
autocomplete: bool = False,
159+
name: Optional[str] = None,
151160
) -> Type[Union["User", "Member"]]:
152161
"""
153162
Annotates an argument as a user type slash command option.
@@ -156,10 +165,11 @@ def slash_user_option(
156165
description: The description of your option
157166
required: Is this option required?
158167
autocomplete: Use autocomplete for this option
168+
name: The name of the option. Defaults to the name of the argument
159169
160170
"""
161171
return SlashCommandOption(
162-
name="placeholder",
172+
name=name,
163173
description=description,
164174
required=required,
165175
autocomplete=autocomplete,
@@ -173,6 +183,7 @@ def slash_channel_option(
173183
autocomplete: bool = False,
174184
choices: List[Union["SlashCommandChoice", dict]] | None = None,
175185
channel_types: Optional[list[Union["ChannelType", int]]] = None,
186+
name: Optional[str] = None,
176187
) -> Type["BaseChannel"]:
177188
"""
178189
Annotates an argument as a channel type slash command option.
@@ -183,10 +194,11 @@ def slash_channel_option(
183194
autocomplete: Use autocomplete for this option
184195
choices: The choices allowed by this command
185196
channel_types: The types of channel allowed by this option
197+
name: The name of the option. Defaults to the name of the argument
186198
187199
"""
188200
return SlashCommandOption(
189-
name="placeholder",
201+
name=name,
190202
description=description,
191203
required=required,
192204
autocomplete=autocomplete,
@@ -201,6 +213,7 @@ def slash_role_option(
201213
required: bool = False,
202214
autocomplete: bool = False,
203215
choices: List[Union["SlashCommandChoice", dict]] | None = None,
216+
name: Optional[str] = None,
204217
) -> Type["Role"]:
205218
"""
206219
Annotates an argument as a role type slash command option.
@@ -210,10 +223,11 @@ def slash_role_option(
210223
required: Is this option required?
211224
autocomplete: Use autocomplete for this option
212225
choices: The choices allowed by this command
226+
name: The name of the option. Defaults to the name of the argument
213227
214228
"""
215229
return SlashCommandOption(
216-
name="placeholder",
230+
name=name,
217231
description=description,
218232
required=required,
219233
autocomplete=autocomplete,
@@ -227,6 +241,7 @@ def slash_mentionable_option(
227241
required: bool = False,
228242
autocomplete: bool = False,
229243
choices: List[Union["SlashCommandChoice", dict]] | None = None,
244+
name: Optional[str] = None,
230245
) -> Type[Union["Role", "BaseChannel", "User", "Member"]]:
231246
"""
232247
Annotates an argument as a mentionable type slash command option.
@@ -236,10 +251,11 @@ def slash_mentionable_option(
236251
required: Is this option required?
237252
autocomplete: Use autocomplete for this option
238253
choices: The choices allowed by this command
254+
name: The name of the option. Defaults to the name of the argument
239255
240256
"""
241257
return SlashCommandOption(
242-
name="placeholder",
258+
name=name,
243259
description=description,
244260
required=required,
245261
autocomplete=autocomplete,
@@ -251,17 +267,19 @@ def slash_mentionable_option(
251267
def slash_attachment_option(
252268
description: str,
253269
required: bool = False,
270+
name: Optional[str] = None,
254271
) -> Type["Attachment"]:
255272
"""
256273
Annotates an argument as an attachment type slash command option.
257274
258275
Args:
259276
description: The description of your option
260277
required: Is this option required?
278+
name: The name of the option. Defaults to the name of the argument
261279
262280
"""
263281
return SlashCommandOption(
264-
name="placeholder",
282+
name=name,
265283
description=description,
266284
required=required,
267285
type=models.OptionType.ATTACHMENT,

interactions/models/internal/application_commands.py

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@ class SlashCommandOption(DictSerializationMixin):
400400
max_value: The maximum value permitted. The option needs to be an integer or float
401401
min_length: The minimum length of text a user can input. The option needs to be a string
402402
max_length: The maximum length of text a user can input. The option needs to be a string
403+
argument_name: The name of the argument to be used in the function. If not given, assumed to be the same as the name of the option
403404
404405
"""
405406

@@ -418,6 +419,7 @@ class SlashCommandOption(DictSerializationMixin):
418419
max_value: Optional[float] = attrs.field(repr=False, default=None)
419420
min_length: Optional[int] = attrs.field(repr=False, default=None)
420421
max_length: Optional[int] = attrs.field(repr=False, default=None)
422+
argument_name: Optional[str] = attrs.field(repr=False, default=None)
421423

422424
@type.validator
423425
def _type_validator(self, attribute: str, value: int) -> None:
@@ -488,6 +490,7 @@ def _max_length_validator(self, attribute: str, value: Optional[int]) -> None:
488490

489491
def as_dict(self) -> dict:
490492
data = attrs.asdict(self)
493+
data.pop("argument_name", None)
491494
data["name"] = str(self.name)
492495
data["description"] = str(self.description)
493496
data["choices"] = [
@@ -506,6 +509,11 @@ class SlashCommandParameter:
506509
kind: inspect._ParameterKind = attrs.field()
507510
default: typing.Any = attrs.field(default=MISSING)
508511
converter: typing.Optional[typing.Callable] = attrs.field(default=None)
512+
_option_name: typing.Optional[str] = attrs.field(default=None)
513+
514+
@property
515+
def option_name(self) -> str:
516+
return self._option_name or self.name
509517

510518

511519
def _get_option_from_annotated(annotated: Annotated) -> SlashCommandOption | None:
@@ -601,10 +609,14 @@ def _add_option_from_anno_method(self, name: str, option: SlashCommandOption) ->
601609
if not self.options:
602610
self.options = []
603611

604-
option.name = name
612+
if option.name is None:
613+
option.name = name
614+
else:
615+
option.argument_name = name
616+
605617
self.options.append(option)
606618

607-
def _parse_parameters(self) -> None:
619+
def _parse_parameters(self) -> None: # noqa: C901
608620
"""
609621
Parses the parameters that this command has into a form i.py can use.
610622
@@ -665,6 +677,20 @@ def _parse_parameters(self) -> None:
665677

666678
self.parameters[param.name] = our_param
667679

680+
if self.options:
681+
for option in self.options:
682+
maybe_argument_name = (
683+
option.argument_name if isinstance(option, SlashCommandOption) else option.get("argument_name")
684+
)
685+
if maybe_argument_name:
686+
name = option.name if isinstance(option, SlashCommandOption) else option["name"]
687+
try:
688+
self.parameters[maybe_argument_name]._option_name = str(name)
689+
except KeyError:
690+
raise ValueError(
691+
f'Argument name "{maybe_argument_name}" for "{name}" does not match any parameter in {self.resolved_name}\'s function.'
692+
) from None
693+
668694
def to_dict(self) -> dict:
669695
data = super().to_dict()
670696

@@ -780,8 +806,8 @@ async def call_callback(self, callback: typing.Callable, ctx: "InteractionContex
780806
new_args = []
781807
new_kwargs = {}
782808

783-
for name, param in self.parameters.items():
784-
value = kwargs_copy.pop(name, MISSING)
809+
for param in self.parameters.values():
810+
value = kwargs_copy.pop(param.option_name, MISSING)
785811
if value is MISSING:
786812
continue
787813

@@ -791,7 +817,7 @@ async def call_callback(self, callback: typing.Callable, ctx: "InteractionContex
791817
if param.kind == inspect.Parameter.POSITIONAL_ONLY:
792818
new_args.append(value)
793819
else:
794-
new_kwargs[name] = value
820+
new_kwargs[param.name] = value
795821

796822
# i do want to address one thing: what happens if you have both *args and **kwargs
797823
# in your argument?
@@ -1196,6 +1222,7 @@ def slash_option(
11961222
max_value: Optional[float] = None,
11971223
min_length: Optional[int] = None,
11981224
max_length: Optional[int] = None,
1225+
argument_name: Optional[str] = None,
11991226
) -> Callable[[SlashCommandT], SlashCommandT]:
12001227
r"""
12011228
A decorator to add an option to a slash command.
@@ -1212,6 +1239,7 @@ def slash_option(
12121239
max_value: The maximum value permitted. The option needs to be an integer or float
12131240
min_length: The minimum length of text a user can input. The option needs to be a string
12141241
max_length: The maximum length of text a user can input. The option needs to be a string
1242+
argument_name: The name of the argument to be used in the function. If not given, assumed to be the same as the name of the option
12151243
"""
12161244

12171245
def wrapper(func: SlashCommandT) -> SlashCommandT:
@@ -1230,6 +1258,7 @@ def wrapper(func: SlashCommandT) -> SlashCommandT:
12301258
max_value=max_value,
12311259
min_length=min_length,
12321260
max_length=max_length,
1261+
argument_name=argument_name,
12331262
)
12341263
if not hasattr(func, "options"):
12351264
func.options = []

0 commit comments

Comments
 (0)