Skip to content

Commit

Permalink
fix(ui): don't require cls argument in select decorators to be posi…
Browse files Browse the repository at this point in the history
…tional (#1111)
  • Loading branch information
shiftinv authored Oct 29, 2023
1 parent f2e5886 commit 038a365
Show file tree
Hide file tree
Showing 9 changed files with 29 additions and 27 deletions.
1 change: 1 addition & 0 deletions changelog/1111.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Allow ``cls`` argument in select menu decorators (e.g. :func`ui.string_select`) to be specified by keyword instead of being positional-only.
2 changes: 1 addition & 1 deletion disnake/ui/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ def button(
----------
cls: Type[:class:`Button`]
The button subclass to create an instance of. If provided, the following parameters
described below do no apply. Instead, this decorator will accept the same keywords
described below do not apply. Instead, this decorator will accept the same keywords
as the passed cls does.
.. versionadded:: 2.6
Expand Down
6 changes: 2 additions & 4 deletions disnake/ui/select/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,7 @@ def channel_select(


def channel_select(
cls: Type[Object[S_co, P]] = ChannelSelect[Any],
/,
**kwargs: Any,
cls: Type[Object[S_co, P]] = ChannelSelect[Any], **kwargs: Any
) -> Callable[[ItemCallbackType[S_co]], DecoratedItem[S_co]]:
"""A decorator that attaches a channel select menu to a component.
Expand All @@ -187,7 +185,7 @@ def channel_select(
----------
cls: Type[:class:`ChannelSelect`]
The select subclass to create an instance of. If provided, the following parameters
described below do no apply. Instead, this decorator will accept the same keywords
described below do not apply. Instead, this decorator will accept the same keywords
as the passed cls does.
placeholder: Optional[:class:`str`]
The placeholder text that is shown if nothing is selected, if any.
Expand Down
6 changes: 2 additions & 4 deletions disnake/ui/select/mentionable.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,7 @@ def mentionable_select(


def mentionable_select(
cls: Type[Object[S_co, P]] = MentionableSelect[Any],
/,
**kwargs: Any,
cls: Type[Object[S_co, P]] = MentionableSelect[Any], **kwargs: Any
) -> Callable[[ItemCallbackType[S_co]], DecoratedItem[S_co]]:
"""A decorator that attaches a mentionable (user/member/role) select menu to a component.
Expand All @@ -163,7 +161,7 @@ def mentionable_select(
----------
cls: Type[:class:`MentionableSelect`]
The select subclass to create an instance of. If provided, the following parameters
described below do no apply. Instead, this decorator will accept the same keywords
described below do not apply. Instead, this decorator will accept the same keywords
as the passed cls does.
placeholder: Optional[:class:`str`]
The placeholder text that is shown if nothing is selected, if any.
Expand Down
6 changes: 2 additions & 4 deletions disnake/ui/select/role.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,7 @@ def role_select(


def role_select(
cls: Type[Object[S_co, P]] = RoleSelect[Any],
/,
**kwargs: Any,
cls: Type[Object[S_co, P]] = RoleSelect[Any], **kwargs: Any
) -> Callable[[ItemCallbackType[S_co]], DecoratedItem[S_co]]:
"""A decorator that attaches a role select menu to a component.
Expand All @@ -161,7 +159,7 @@ def role_select(
----------
cls: Type[:class:`RoleSelect`]
The select subclass to create an instance of. If provided, the following parameters
described below do no apply. Instead, this decorator will accept the same keywords
described below do not apply. Instead, this decorator will accept the same keywords
as the passed cls does.
placeholder: Optional[:class:`str`]
The placeholder text that is shown if nothing is selected, if any.
Expand Down
6 changes: 2 additions & 4 deletions disnake/ui/select/string.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,9 +268,7 @@ def string_select(


def string_select(
cls: Type[Object[S_co, P]] = StringSelect[Any],
/,
**kwargs: Any,
cls: Type[Object[S_co, P]] = StringSelect[Any], **kwargs: Any
) -> Callable[[ItemCallbackType[S_co]], DecoratedItem[S_co]]:
"""A decorator that attaches a string select menu to a component.
Expand All @@ -288,7 +286,7 @@ def string_select(
----------
cls: Type[:class:`StringSelect`]
The select subclass to create an instance of. If provided, the following parameters
described below do no apply. Instead, this decorator will accept the same keywords
described below do not apply. Instead, this decorator will accept the same keywords
as the passed cls does.
.. versionadded:: 2.6
Expand Down
6 changes: 2 additions & 4 deletions disnake/ui/select/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,7 @@ def user_select(


def user_select(
cls: Type[Object[S_co, P]] = UserSelect[Any],
/,
**kwargs: Any,
cls: Type[Object[S_co, P]] = UserSelect[Any], **kwargs: Any
) -> Callable[[ItemCallbackType[S_co]], DecoratedItem[S_co]]:
"""A decorator that attaches a user select menu to a component.
Expand All @@ -162,7 +160,7 @@ def user_select(
----------
cls: Type[:class:`UserSelect`]
The select subclass to create an instance of. If provided, the following parameters
described below do no apply. Instead, this decorator will accept the same keywords
described below do not apply. Instead, this decorator will accept the same keywords
as the passed cls does.
placeholder: Optional[:class:`str`]
The placeholder text that is shown if nothing is selected, if any.
Expand Down
2 changes: 1 addition & 1 deletion docs/api/ui.rst
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ TextInput
Functions
---------

.. autofunction:: button(cls=Button, *, style=ButtonStyle.secondary, label=None, disabled=False, custom_id=..., url=None, emoji=None, row=None)
.. autofunction:: button(cls=Button, *, custom_id=..., style=ButtonStyle.secondary, label=None, disabled=False, url=None, emoji=None, row=None)
:decorator:

.. autofunction:: string_select(cls=StringSelect, *, custom_id=..., placeholder=None, min_values=1, max_values=1, options=..., disabled=False, row=None)
Expand Down
21 changes: 16 additions & 5 deletions tests/ui/test_decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def test_default(self) -> None:
assert func.__discord_ui_model_type__ is ui.StringSelect
assert func.__discord_ui_model_kwargs__ == {"custom_id": "123"}

# from here on out we're only testing the button decorator,
# from here on out we're mostly only testing the button decorator,
# as @ui.string_select etc. works identically

@pytest.mark.parametrize("cls", [_CustomButton, _CustomButton[Any]])
Expand All @@ -64,7 +64,18 @@ def _test_typing_cls(self) -> None:
this_should_not_work="h", # type: ignore
)

@pytest.mark.parametrize("cls", [123, int, ui.StringSelect])
def test_cls_invalid(self, cls) -> None:
with pytest.raises(TypeError, match=r"cls argument must be"):
ui.button(cls=cls) # type: ignore
@pytest.mark.parametrize(
("decorator", "invalid_cls"),
[
(ui.button, ui.StringSelect),
(ui.string_select, ui.Button),
(ui.user_select, ui.Button),
(ui.role_select, ui.Button),
(ui.mentionable_select, ui.Button),
(ui.channel_select, ui.Button),
],
)
def test_cls_invalid(self, decorator, invalid_cls) -> None:
for cls in [123, int, invalid_cls]:
with pytest.raises(TypeError, match=r"cls argument must be"):
decorator(cls=cls)

0 comments on commit 038a365

Please sign in to comment.