From 968a1f366fa1913c621bc866aedde068b86e9654 Mon Sep 17 00:00:00 2001 From: Rapptz Date: Thu, 24 Mar 2022 23:00:50 -0400 Subject: [PATCH] Change View callback order to (self, interaction, item) This is more consistent with the rest of the library which always has the interaction as the first parameter. This has been done before in the command extension as well, the first parameter is always either self or the context. --- discord/ui/button.py | 4 ++-- discord/ui/item.py | 2 +- discord/ui/select.py | 4 ++-- discord/ui/view.py | 16 ++++++++++++++-- examples/views/confirm.py | 4 ++-- examples/views/counter.py | 2 +- examples/views/ephemeral.py | 4 ++-- examples/views/persistent.py | 6 +++--- 8 files changed, 27 insertions(+), 15 deletions(-) diff --git a/discord/ui/button.py b/discord/ui/button.py index 457a632c6..1f99a2dc8 100644 --- a/discord/ui/button.py +++ b/discord/ui/button.py @@ -240,8 +240,8 @@ def button( """A decorator that attaches a button to a component. The function being decorated should have three parameters, ``self`` representing - the :class:`discord.ui.View`, the :class:`discord.ui.Button` being pressed and - the :class:`discord.Interaction` you receive. + the :class:`discord.ui.View`, the :class:`discord.Interaction` you receive and + the :class:`discord.ui.Button` being pressed. .. note:: diff --git a/discord/ui/item.py b/discord/ui/item.py index a80875a12..45f27fe20 100644 --- a/discord/ui/item.py +++ b/discord/ui/item.py @@ -41,7 +41,7 @@ if TYPE_CHECKING: I = TypeVar('I', bound='Item') V = TypeVar('V', bound='View', covariant=True) -ItemCallbackType = Callable[[V, I, Interaction], Coroutine[Any, Any, Any]] +ItemCallbackType = Callable[[V, Interaction, I], Coroutine[Any, Any, Any]] class Item(Generic[V]): diff --git a/discord/ui/select.py b/discord/ui/select.py index 87de5450a..a592356b5 100644 --- a/discord/ui/select.py +++ b/discord/ui/select.py @@ -305,8 +305,8 @@ def select( """A decorator that attaches a select menu to a component. The function being decorated should have three parameters, ``self`` representing - the :class:`discord.ui.View`, the :class:`discord.ui.Select` being pressed and - the :class:`discord.Interaction` you receive. + the :class:`discord.ui.View`, the :class:`discord.Interaction` you receive and + the :class:`discord.ui.Select` being used. In order to get the selected items that the user has chosen within the callback use :attr:`Select.values`. diff --git a/discord/ui/view.py b/discord/ui/view.py index 4c3e9d886..c0c4296d6 100644 --- a/discord/ui/view.py +++ b/discord/ui/view.py @@ -23,7 +23,7 @@ DEALINGS IN THE SOFTWARE. """ from __future__ import annotations -from typing import Any, Callable, ClassVar, Dict, Iterator, List, Optional, Sequence, TYPE_CHECKING, Tuple +from typing import Any, Callable, ClassVar, Coroutine, Dict, Iterator, List, Optional, Sequence, TYPE_CHECKING, Tuple from functools import partial from itertools import groupby @@ -127,6 +127,18 @@ class _ViewWeights: self.weights = [0, 0, 0, 0, 0] +class _ViewCallback: + __slots__ = ('view', 'callback', 'item') + + def __init__(self, callback: ItemCallbackType[Any, Any], view: View, item: Item[View]) -> None: + self.callback: ItemCallbackType[Any, Any] = callback + self.view: View = view + self.item: Item[View] = item + + def __call__(self, interaction: Interaction) -> Coroutine[Any, Any, Any]: + return self.callback(self.view, interaction, self.item) + + class View: """Represents a UI view. @@ -169,7 +181,7 @@ class View: children = [] for func in self.__view_children_items__: item: Item = func.__discord_ui_model_type__(**func.__discord_ui_model_kwargs__) - item.callback = partial(func, self, item) # type: ignore + item.callback = _ViewCallback(func, self, item) item._view = self setattr(self, func.__name__, item) children.append(item) diff --git a/examples/views/confirm.py b/examples/views/confirm.py index cc1164800..09c31ef0f 100644 --- a/examples/views/confirm.py +++ b/examples/views/confirm.py @@ -27,14 +27,14 @@ class Confirm(discord.ui.View): # stop the View from listening to more input. # We also send the user an ephemeral message that we're confirming their choice. @discord.ui.button(label='Confirm', style=discord.ButtonStyle.green) - async def confirm(self, button: discord.ui.Button, interaction: discord.Interaction): + async def confirm(self, interaction: discord.Interaction, button: discord.ui.Button): await interaction.response.send_message('Confirming', ephemeral=True) self.value = True self.stop() # This one is similar to the confirmation button except sets the inner value to `False` @discord.ui.button(label='Cancel', style=discord.ButtonStyle.grey) - async def cancel(self, button: discord.ui.Button, interaction: discord.Interaction): + async def cancel(self, interaction: discord.Interaction, button: discord.ui.Button): await interaction.response.send_message('Cancelling', ephemeral=True) self.value = False self.stop() diff --git a/examples/views/counter.py b/examples/views/counter.py index 9771611ce..df5b3bc9c 100644 --- a/examples/views/counter.py +++ b/examples/views/counter.py @@ -25,7 +25,7 @@ class Counter(discord.ui.View): # When it hits 5, the counter button is disabled and it turns green. # note: The name of the function does not matter to the library @discord.ui.button(label='0', style=discord.ButtonStyle.red) - async def count(self, button: discord.ui.Button, interaction: discord.Interaction): + async def count(self, interaction: discord.Interaction, button: discord.ui.Button): number = int(button.label) if button.label else 0 if number + 1 >= 5: button.style = discord.ButtonStyle.green diff --git a/examples/views/ephemeral.py b/examples/views/ephemeral.py index e0c197c6b..ad1320db9 100644 --- a/examples/views/ephemeral.py +++ b/examples/views/ephemeral.py @@ -23,7 +23,7 @@ class Counter(discord.ui.View): # When it hits 5, the counter button is disabled and it turns green. # note: The name of the function does not matter to the library @discord.ui.button(label='0', style=discord.ButtonStyle.red) - async def count(self, button: discord.ui.Button, interaction: discord.Interaction): + async def count(self, interaction: discord.Interaction, button: discord.ui.Button): number = int(button.label) if button.label else 0 if number + 1 >= 5: button.style = discord.ButtonStyle.green @@ -38,7 +38,7 @@ class EphemeralCounter(discord.ui.View): # When this button is pressed, it will respond with a Counter view that will # give the button presser their own personal button they can press 5 times. @discord.ui.button(label='Click', style=discord.ButtonStyle.blurple) - async def receive(self, button: discord.ui.Button, interaction: discord.Interaction): + async def receive(self, interaction: discord.Interaction, button: discord.ui.Button): # ephemeral=True makes the message hidden from everyone except the button presser await interaction.response.send_message('Enjoy!', view=Counter(), ephemeral=True) diff --git a/examples/views/persistent.py b/examples/views/persistent.py index 1194f5d24..1c410abd2 100644 --- a/examples/views/persistent.py +++ b/examples/views/persistent.py @@ -17,15 +17,15 @@ class PersistentView(discord.ui.View): super().__init__(timeout=None) @discord.ui.button(label='Green', style=discord.ButtonStyle.green, custom_id='persistent_view:green') - async def green(self, button: discord.ui.Button, interaction: discord.Interaction): + async def green(self, interaction: discord.Interaction, button: discord.ui.Button): await interaction.response.send_message('This is green.', ephemeral=True) @discord.ui.button(label='Red', style=discord.ButtonStyle.red, custom_id='persistent_view:red') - async def red(self, button: discord.ui.Button, interaction: discord.Interaction): + async def red(self, interaction: discord.Interaction, button: discord.ui.Button): await interaction.response.send_message('This is red.', ephemeral=True) @discord.ui.button(label='Grey', style=discord.ButtonStyle.grey, custom_id='persistent_view:grey') - async def grey(self, button: discord.ui.Button, interaction: discord.Interaction): + async def grey(self, interaction: discord.Interaction, button: discord.ui.Button): await interaction.response.send_message('This is grey.', ephemeral=True)