Repository: hikari-py/hikari
Pull request #1574 (2023-04-02 15:14:35)

Add more builder set methods
### Summary
The primary aim of this is to more consistently provide `set_custom_id`

### Checklist
<!-- Make sure to tick all the following boxes by putting an `x` in between (like this `[x]`) -->
- [ ] I have run `nox` and all the pipelines have passed.
- [ ] I have made unittests according to the code I have added/modified/deleted.

### Related issues
<!--
To mention an issue use `#issue-id` and to mention a merge request use `!merge-request-id`
To close/fix an issue use `Close #issue-id` or `Fix #issue-id` (depending on the merge request)
-->



Patch:
diff --git a/changes/1574.feature.md b/changes/1574.feature.md
new file mode 100644
index 00000000..96466240
--- /dev/null
+++ b/changes/1574.feature.md
@@ -0,0 +1,8 @@
+Added several set methods for required values to the builders:
+
+* `CommandBuilder.set_name`
+* `SlashCommandBuilder.set_description`
+* `InteractiveButtonBuilder.set_custom_id`
+* `SelectOptionBuilder.set_label`
+* `SelectOptionBuilder.set_value`
+* `SelectMenuBuilder.set_custom_id`
diff --git a/hikari/api/special_endpoints.py b/hikari/api/special_endpoints.py
index 446ea9a4..3007afe2 100644
--- a/hikari/api/special_endpoints.py
+++ b/hikari/api/special_endpoints.py
@@ -958,7 +958,7 @@ def set_title(self, title: str, /) -> Self:
 
     @abc.abstractmethod
     def set_custom_id(self, custom_id: str, /) -> Self:
-        """Set the developer set custom ID used for identifying interactions with this modal.
+        """Set the custom ID used for identifying interactions with this modal.
 
         Parameters
         ----------
@@ -1029,6 +1029,21 @@ def is_nsfw(self) -> undefined.UndefinedOr[bool]:
     def name_localizations(self) -> typing.Mapping[typing.Union[locales.Locale, str], str]:
         """Name localizations set for this command."""
 
+    @abc.abstractmethod
+    def set_name(self, name: str, /) -> Self:
+        """Set the name of this command.
+
+        Parameters
+        ----------
+        name : str
+            The name to set for this command.
+
+        Returns
+        -------
+        CommandBuilder
+            Object of this command builder to allow for chained calls.
+        """
+
     @abc.abstractmethod
     def set_id(self, id_: undefined.UndefinedOr[snowflakes.Snowflakeish], /) -> Self:
         """Set the ID of this command.
@@ -1177,6 +1192,26 @@ def description(self) -> str:
     def description_localizations(self) -> typing.Mapping[typing.Union[locales.Locale, str], str]:
         """Command's localised descriptions."""
 
+    @property
+    @abc.abstractmethod
+    def options(self) -> typing.Sequence[commands.CommandOption]:
+        """Sequence of up to 25 of the options set for this command."""
+
+    @abc.abstractmethod
+    def set_description(self, description: str, /) -> Self:
+        """Set the description for this command.
+
+        Parameters
+        ----------
+        description : str
+            The description to set for this command.
+
+        Returns
+        -------
+        SlashCommandBuilder
+            Object of this command builder.
+        """
+
     @abc.abstractmethod
     def set_description_localizations(
         self, description_localizations: typing.Mapping[typing.Union[locales.Locale, str], str], /
@@ -1190,15 +1225,10 @@ def set_description_localizations(
 
         Returns
         -------
-        CommandBuilder
+        SlashCommandBuilder
             Object of this command builder.
         """
 
-    @property
-    @abc.abstractmethod
-    def options(self) -> typing.Sequence[commands.CommandOption]:
-        """Sequence of up to 25 of the options set for this command."""
-
     @abc.abstractmethod
     def add_option(self, option: commands.CommandOption) -> Self:
         """Add an option to this command.
@@ -1213,7 +1243,7 @@ def add_option(self, option: commands.CommandOption) -> Self:
 
         Returns
         -------
-        CommandBuilder
+        SlashCommandBuilder
             Object of this command builder to allow for chained calls.
         """
 
@@ -1421,6 +1451,21 @@ class InteractiveButtonBuilder(ButtonBuilder, abc.ABC):
     def custom_id(self) -> str:
         """Developer set custom ID used for identifying interactions with this button."""
 
+    @abc.abstractmethod
+    def set_custom_id(self, custom_id: str, /) -> Self:
+        """Set the custom ID used for identifying this button.
+
+        Parameters
+        ----------
+        custom_id : str
+            Developer set custom ID used for identifying this button.
+
+        Returns
+        -------
+        InteractiveButtonBuilder
+            The builder object to enable chained calls.
+        """
+
 
 class SelectOptionBuilder(abc.ABC):
     """Builder class for select menu options."""
@@ -1452,6 +1497,38 @@ def emoji(self) -> typing.Union[snowflakes.Snowflakeish, emojis.Emoji, str, unde
     def is_default(self) -> bool:
         """Whether this option should be marked as selected by default."""
 
+    @abc.abstractmethod
+    def set_label(self, label: str, /) -> Self:
+        """Set the option's label.
+
+        Parameters
+        ----------
+        label : str
+            Label to set for this option. This can be up to 100 characters
+            long.
+
+        Returns
+        -------
+        SelectOptionBuilder
+            The builder object to enable chained calls.
+        """
+
+    @abc.abstractmethod
+    def set_value(self, value: str, /) -> Self:
+        """Set the option's value.
+
+        Parameters
+        ----------
+        value : str
+            Value to set for this option. This can be up to 100 characters
+            long.
+
+        Returns
+        -------
+        SelectOptionBuilder
+            The builder object to enable chained calls.
+        """
+
     @abc.abstractmethod
     def set_description(self, value: undefined.UndefinedOr[str], /) -> Self:
         """Set the option's description.
@@ -1552,6 +1629,21 @@ def max_values(self) -> int:
         less than or equal to 25.
         """
 
+    @abc.abstractmethod
+    def set_custom_id(self, custom_id: str, /) -> Self:
+        """Set the custom ID used for identifying this menu.
+
+        Parameters
+        ----------
+        custom_id : str
+            Developer set custom ID used for identifying this menu.
+
+        Returns
+        -------
+        SelectMenuBuilder
+            The builder object to enable chained calls.
+        """
+
     @abc.abstractmethod
     def set_is_disabled(self, state: bool, /) -> Self:
         """Set whether this option is disabled.
@@ -1777,7 +1869,7 @@ def set_style(self, style: typing.Union[components_.TextInputStyle, int], /) ->
 
     @abc.abstractmethod
     def set_custom_id(self, custom_id: str, /) -> Self:
-        """Set the developer set custom ID used for identifying this text input.
+        """Set the custom ID used for identifying this text input.
 
         Parameters
         ----------
diff --git a/hikari/impl/special_endpoints.py b/hikari/impl/special_endpoints.py
index e1df47f0..e7432242 100644
--- a/hikari/impl/special_endpoints.py
+++ b/hikari/impl/special_endpoints.py
@@ -1335,6 +1335,10 @@ def is_nsfw(self) -> undefined.UndefinedOr[bool]:
     def name(self) -> str:
         return self._name
 
+    def set_name(self, name: str, /) -> Self:
+        self._name = name
+        return self
+
     def set_id(self, id_: undefined.UndefinedOr[snowflakes.Snowflakeish], /) -> Self:
         self._id = snowflakes.Snowflake(id_) if id_ is not undefined.UNDEFINED else undefined.UNDEFINED
         return self
@@ -1399,10 +1403,6 @@ def description(self) -> str:
     def type(self) -> commands.CommandType:
         return commands.CommandType.SLASH
 
-    def add_option(self, option: commands.CommandOption) -> Self:
-        self._options.append(option)
-        return self
-
     @property
     def options(self) -> typing.Sequence[commands.CommandOption]:
         return self._options.copy()
@@ -1411,12 +1411,20 @@ def options(self) -> typing.Sequence[commands.CommandOption]:
     def description_localizations(self) -> typing.Mapping[typing.Union[locales.Locale, str], str]:
         return self._description_localizations
 
+    def set_description(self, description: str, /) -> Self:
+        self._description = description
+        return self
+
     def set_description_localizations(
         self, description_localizations: typing.Mapping[typing.Union[locales.Locale, str], str], /
     ) -> Self:
         self._description_localizations = description_localizations
         return self
 
+    def add_option(self, option: commands.CommandOption) -> Self:
+        self._options.append(option)
+        return self
+
     def build(self, entity_factory: entity_factory_.EntityFactory, /) -> typing.MutableMapping[str, typing.Any]:
         data = super().build(entity_factory)
         # Under this context we know this'll always be a JSONObjectBuilder but
@@ -1607,6 +1615,10 @@ class InteractiveButtonBuilder(_ButtonBuilder, special_endpoints.InteractiveButt
     def custom_id(self) -> str:
         return self._custom_id
 
+    def set_custom_id(self, custom_id: str, /) -> Self:
+        self._custom_id = custom_id
+        return self
+
 
 @attrs_extensions.with_copy
 @attrs.define(weakref_slot=False)
@@ -1648,6 +1660,14 @@ def emoji(self) -> typing.Union[snowflakes.Snowflakeish, emojis.Emoji, str, unde
     def is_default(self) -> bool:
         return self._is_default
 
+    def set_label(self, label: str, /) -> Self:
+        self._label = label
+        return self
+
+    def set_value(self, value: str, /) -> Self:
+        self._value = value
+        return self
+
     def set_description(self, value: undefined.UndefinedOr[str], /) -> Self:
         self._description = value
         return self
@@ -1716,6 +1736,10 @@ def min_values(self) -> int:
     def max_values(self) -> int:
         return self._max_values
 
+    def set_custom_id(self, custom_id: str, /) -> Self:
+        self._custom_id = custom_id
+        return self
+
     def set_is_disabled(self, state: bool, /) -> Self:
         self._is_disabled = state
         return self


Test patch:
diff --git a/tests/hikari/impl/test_special_endpoints.py b/tests/hikari/impl/test_special_endpoints.py
index 39f54b8c..24a7601f 100644
--- a/tests/hikari/impl/test_special_endpoints.py
+++ b/tests/hikari/impl/test_special_endpoints.py
@@ -1045,43 +1045,57 @@ def test_build(self):
         assert attachments == ()
 
 
-class TestSlashCommandBuilder:
-    def test_description_property(self):
-        builder = special_endpoints.SlashCommandBuilder("ok", "NO")
+class TestCommandBuilder:
+    @pytest.fixture()
+    def stub_command(self) -> typing.Type[special_endpoints.CommandBuilder]:
+        return hikari_test_helpers.mock_class_namespace(special_endpoints.CommandBuilder)
 
-        assert builder.description == "NO"
+    def test_name_property(self, stub_command):
+        builder = stub_command("NOOOOO").set_name("aaaaa")
 
-    def test_name_property(self):
-        builder = special_endpoints.SlashCommandBuilder("NOOOOO", "OKKKK")
+        assert builder.name == "aaaaa"
 
-        assert builder.name == "NOOOOO"
+    def test_id_property(self, stub_command):
+        builder = stub_command("OKSKDKSDK").set_id(3212123)
 
-    def test_options_property(self):
-        builder = special_endpoints.SlashCommandBuilder("OKSKDKSDK", "inmjfdsmjiooikjsa")
-        mock_option = object()
+        assert builder.id == 3212123
 
-        assert builder.options == []
+    def test_default_member_permissions(self, stub_command):
+        builder = stub_command("oksksksk").set_default_member_permissions(permissions.Permissions.ADMINISTRATOR)
 
-        builder.add_option(mock_option)
+        assert builder.default_member_permissions == permissions.Permissions.ADMINISTRATOR
 
-        assert builder.options == [mock_option]
+    def test_is_dm_enabled(self, stub_command):
+        builder = stub_command("oksksksk").set_is_dm_enabled(True)
 
-    def test_id_property(self):
-        builder = special_endpoints.SlashCommandBuilder("OKSKDKSDK", "inmjfdsmjiooikjsa").set_id(3212123)
+        assert builder.is_dm_enabled is True
 
-        assert builder.id == 3212123
+    def test_is_nsfw_property(self, stub_command):
+        builder = stub_command("oksksksk").set_is_nsfw(True)
 
-    def test_default_member_permissions(self):
-        builder = special_endpoints.SlashCommandBuilder("oksksksk", "kfdkodfokfd").set_default_member_permissions(
-            permissions.Permissions.ADMINISTRATOR
-        )
+        assert builder.is_nsfw is True
 
-        assert builder.default_member_permissions == permissions.Permissions.ADMINISTRATOR
+    def test_name_localizations_property(self, stub_command):
+        builder = stub_command("oksksksk").set_name_localizations({"aaa": "bbb", "ccc": "DDd"})
 
-    def test_is_dm_enabled(self):
-        builder = special_endpoints.SlashCommandBuilder("oksksksk", "kfdkodfokfd").set_is_dm_enabled(True)
+        assert builder.name_localizations == {"aaa": "bbb", "ccc": "DDd"}
 
-        assert builder.is_dm_enabled is True
+
+class TestSlashCommandBuilder:
+    def test_description_property(self):
+        builder = special_endpoints.SlashCommandBuilder("ok", "NO").set_description("meow")
+
+        assert builder.description == "meow"
+
+    def test_options_property(self):
+        builder = special_endpoints.SlashCommandBuilder("OKSKDKSDK", "inmjfdsmjiooikjsa")
+        mock_option = object()
+
+        assert builder.options == []
+
+        builder.add_option(mock_option)
+
+        assert builder.options == [mock_option]
 
     def test_build_with_optional_data(self):
         mock_entity_factory = mock.Mock()
@@ -1401,10 +1415,10 @@ def test_url_property(self):
 class TestInteractiveButtonBuilder:
     def test_custom_id_property(self):
         button = special_endpoints.InteractiveButtonBuilder(
-            style=components.ButtonStyle.DANGER, label="no u", custom_id="ooga booga", is_disabled=True
-        )
+            style=components.ButtonStyle.DANGER, custom_id="oogie"
+        ).set_custom_id("eeeeee")
 
-        assert button.custom_id == "ooga booga"
+        assert button.custom_id == "eeeeee"
 
 
 class TestSelectOptionBuilder:
@@ -1413,10 +1427,14 @@ def option(self):
         return special_endpoints.SelectOptionBuilder(label="ok", value="ok2")
 
     def test_label_property(self, option):
-        assert option.label == "ok"
+        option.set_label("new_label")
+
+        assert option.label == "new_label"
 
     def test_value_property(self, option):
-        assert option.value == "ok2"
+        option.set_value("aaaaaaaaaaaa")
+
+        assert option.value == "aaaaaaaaaaaa"
 
     def test_emoji_property(self, option):
         option._emoji = 123321
@@ -1499,7 +1517,9 @@ def test_type_property(self):
         assert menu.type == 123
 
     def test_custom_id_property(self, menu):
-        assert menu.custom_id == "o2o2o2"
+        menu.set_custom_id("ooooo")
+
+        assert menu.custom_id == "ooooo"
 
     def test_set_is_disabled(self, menu):
         assert menu.set_is_disabled(True) is menu