Repository: hikari-py/hikari
Pull request #920 (2021-11-26 18:47:36)

Add min_value and max_value to CommandOption
### Summary
Add min_value and max_value to CommandOption. 

Reference: https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure

### Checklist
<!-- Make sure to tick all the following boxes by putting an `x` in between (like this `[x]`) -->
- [x] I have run `nox` and all the pipelines have passed. (it's taking so long 😭)
- [x] I have made unittests according to the code I have added/modified/deleted.


thesadru: I think this is ready for a re-review.

Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4a8440af..af4cdeb4 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -20,7 +20,7 @@ jobs:
         uses: actions/checkout@v2.4.0
 
       - name: Setup python
-        uses: actions/setup-python@v2.3.0
+        uses: actions/setup-python@v2.3.1
         with:
           python-version: ${{ matrix.python-version }}
 
@@ -60,7 +60,7 @@ jobs:
         uses: actions/checkout@v2.4.0
 
       - name: Setup python
-        uses: actions/setup-python@v2.3.0
+        uses: actions/setup-python@v2.3.1
         with:
           python-version: 3.8
 
@@ -90,7 +90,7 @@ jobs:
         uses: actions/checkout@v2.4.0
 
       - name: Setup python
-        uses: actions/setup-python@v2.3.0
+        uses: actions/setup-python@v2.3.1
         with:
           python-version: 3.8
 
@@ -117,7 +117,7 @@ jobs:
         uses: actions/checkout@v2.4.0
 
       - name: Setup python
-        uses: actions/setup-python@v2.3.0
+        uses: actions/setup-python@v2.3.1
         with:
           python-version: 3.8
 
@@ -134,12 +134,12 @@ jobs:
         uses: actions/checkout@v2.4.0
 
       - name: Setup python
-        uses: actions/setup-python@v2.3.0
+        uses: actions/setup-python@v2.3.1
         with:
           python-version: 3.8
 
       - name: Setup node
-        uses: actions/setup-node@v2.4.1
+        uses: actions/setup-node@v2.5.0
 
       - name: Build pages
         run: |
diff --git a/.github/workflows/fragments-check.yml b/.github/workflows/fragments-check.yml
index 8a738c9a..d2de736b 100644
--- a/.github/workflows/fragments-check.yml
+++ b/.github/workflows/fragments-check.yml
@@ -20,7 +20,7 @@ jobs:
           fetch-depth: 0
 
       - name: Setup python
-        uses: actions/setup-python@v2.3.0
+        uses: actions/setup-python@v2.3.1
         with:
           python-version: 3.8
 
diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml
index 8850e317..b9c708f4 100644
--- a/.github/workflows/prepare-release.yml
+++ b/.github/workflows/prepare-release.yml
@@ -16,7 +16,7 @@ jobs:
         uses: actions/checkout@v2.4.0
 
       - name: Setup python
-        uses: actions/setup-python@v2.3.0
+        uses: actions/setup-python@v2.3.1
         with:
           python-version: 3.8
 
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index bce5bf18..4ec622b3 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -15,7 +15,7 @@ jobs:
           token: ${{ secrets.PAT_TOKEN }}
 
       - name: Setup python
-        uses: actions/setup-python@v2.3.0
+        uses: actions/setup-python@v2.3.1
         with:
           python-version: 3.8
 
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 755487f8..83e1d675 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,7 +22,7 @@ Deprecation
 -----------
 
 - `edit_my_nick` rest method. ([#827](https://github.com/hikari-py/hikari/issues/827))
-- EventStream is now a sync context menu not async. ([#864](https://github.com/hikari-py/hikari/issues/864))
+- EventStream is now a sync context manager, not async. ([#864](https://github.com/hikari-py/hikari/issues/864))
 
 
 Features
diff --git a/changes/920.feature.md b/changes/920.feature.md
new file mode 100644
index 00000000..f2ec79a7
--- /dev/null
+++ b/changes/920.feature.md
@@ -0,0 +1,1 @@
+Add min_value and max_value to `CommandOption`
diff --git a/changes/928.bugfix.md b/changes/928.bugfix.md
new file mode 100644
index 00000000..e8a1817a
--- /dev/null
+++ b/changes/928.bugfix.md
@@ -0,0 +1,1 @@
+Fix reposition_roles using the wrong route.
diff --git a/dev-requirements.txt b/dev-requirements.txt
index 182f0724..c8e78883 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -9,10 +9,10 @@ mock==4.0.3
 pytest==6.2.5
 pytest-asyncio==0.16.0
 pytest-cov==3.0.0
-pytest-randomly==3.10.2
+pytest-randomly==3.10.3
 
 # Coverage testing.
-coverage==6.1.2
+coverage==6.2
 
 # Other stuff
 async-timeout==4.0.1  # Used for timeouts in some test cases.
@@ -40,7 +40,7 @@ safety~=1.10.3
 # FORMATTING #
 #############
 
-black==21.11b1
+black==21.12b0
 isort==5.10.1
 
 ##################
diff --git a/hikari/commands.py b/hikari/commands.py
index d4f571cb..922d81c1 100644
--- a/hikari/commands.py
+++ b/hikari/commands.py
@@ -146,6 +146,20 @@ class CommandOption:
     If `builtins.None`, then all channel types will be accepted.
     """
 
+    min_value: typing.Union[int, float, None] = attr.field(default=None, repr=False)
+    """The minimum value permitted (inclusive).
+
+    This will be `builtins.int` if the type of the option is `hikari.commands.OptionType.INTEGER`
+    and `builtins.float` if the type is `hikari.commands.OptionType.NUMBER`.
+    """
+
+    max_value: typing.Union[int, float, None] = attr.field(default=None, repr=False)
+    """The maximum value permitted (inclusive).
+
+    This will be `builtins.int` if the type of the option is `hikari.commands.OptionType.INTEGER`
+    and `builtins.float` if the type is `hikari.commands.OptionType.NUMBER`.
+    """
+
 
 @attr_extensions.with_copy
 @attr.define(hash=True, kw_only=True, weakref_slot=False)
diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py
index fce6589a..134e0c85 100644
--- a/hikari/impl/entity_factory.py
+++ b/hikari/impl/entity_factory.py
@@ -1690,6 +1690,8 @@ def _deserialize_command_option(self, payload: data_binding.JSONObject) -> comma
             choices=choices,
             options=suboptions,
             channel_types=channel_types,
+            min_value=payload.get("min_value"),
+            max_value=payload.get("max_value"),
         )
 
     def deserialize_command(
@@ -1913,6 +1915,11 @@ def serialize_command_option(self, option: commands.CommandOption) -> data_bindi
         if option.options is not None:
             payload["options"] = [self.serialize_command_option(suboption) for suboption in option.options]
 
+        if option.min_value is not None:
+            payload["min_value"] = option.min_value
+        if option.max_value is not None:
+            payload["max_value"] = option.max_value
+
         return payload
 
     def deserialize_component_interaction(
diff --git a/hikari/impl/rest.py b/hikari/impl/rest.py
index 20806553..9d43eb45 100644
--- a/hikari/impl/rest.py
+++ b/hikari/impl/rest.py
@@ -2883,7 +2883,7 @@ async def reposition_roles(
         guild: snowflakes.SnowflakeishOr[guilds.PartialGuild],
         positions: typing.Mapping[int, snowflakes.SnowflakeishOr[guilds.PartialRole]],
     ) -> None:
-        route = routes.POST_GUILD_ROLES.compile(guild=guild)
+        route = routes.PATCH_GUILD_ROLES.compile(guild=guild)
         body = [{"id": str(int(role)), "position": pos} for pos, role in positions.items()]
         await self._request(route, json=body)
 
diff --git a/hikari/internal/data_binding.py b/hikari/internal/data_binding.py
index 28577ea3..a6876bd6 100644
--- a/hikari/internal/data_binding.py
+++ b/hikari/internal/data_binding.py
@@ -91,7 +91,6 @@ def dump_json(_: typing.Union[JSONArray, JSONObject], /, *, indent: int = ...) -
     def load_json(_: typing.AnyStr, /) -> typing.Union[JSONArray, JSONObject]:
         """Convert a JSON string to a Python type."""
 
-
 else:
     import json
 


Test patch:
diff --git a/tests/hikari/impl/test_entity_factory.py b/tests/hikari/impl/test_entity_factory.py
index 92ce8008..3fa82441 100644
--- a/tests/hikari/impl/test_entity_factory.py
+++ b/tests/hikari/impl/test_entity_factory.py
@@ -2719,6 +2719,8 @@ def command_payload(self):
                     "description": "42",
                     "channel_types": [0, 1, 2],
                     "required": True,
+                    "min_value": 0,
+                    "max_value": 10,
                     "options": [
                         {
                             "type": 6,
@@ -2758,6 +2760,8 @@ def test_deserialize_command(self, entity_factory_impl, mock_app, command_payloa
             channel_models.ChannelType.DM,
             channel_models.ChannelType.GUILD_VOICE,
         ]
+        assert option.min_value == 0
+        assert option.max_value == 10
 
         assert len(option.options) == 1
         suboption = option.options[0]
@@ -3133,6 +3137,27 @@ def test_serialize_command_option_with_channel_type(self, entity_factory_impl):
             "channel_types": [13, 0, 100],
         }
 
+    def test_serialize_command_option_with_min_and_max_value(self, entity_factory_impl):
+        option = commands.CommandOption(
+            type=commands.OptionType.FLOAT,
+            name="a name",
+            description="go away",
+            is_required=True,
+            min_value=1.2,
+            max_value=9.999,
+        )
+
+        result = entity_factory_impl.serialize_command_option(option)
+
+        assert result == {
+            "type": 10,
+            "name": "a name",
+            "description": "go away",
+            "required": True,
+            "min_value": 1.2,
+            "max_value": 9.999,
+        }
+
     def test_serialize_command_option_with_choices(self, entity_factory_impl):
         option = commands.CommandOption(
             type=commands.OptionType.INTEGER,
diff --git a/tests/hikari/impl/test_rest.py b/tests/hikari/impl/test_rest.py
index 2a5fab21..009e06aa 100644
--- a/tests/hikari/impl/test_rest.py
+++ b/tests/hikari/impl/test_rest.py
@@ -23,11 +23,11 @@
 import contextlib
 import datetime
 import http
+import re
 import warnings
 
 import mock
 import pytest
-import regex
 
 from hikari import applications
 from hikari import audit_logs
@@ -1903,7 +1903,7 @@ async def test__create_message_when_non_collection_passed_to_plural(self, rest_c
             f"'{singular_arg}' (singular) instead?"
         )
 
-        with pytest.raises(TypeError, match=regex.escape(expected_error_message)):
+        with pytest.raises(TypeError, match=re.escape(expected_error_message)):
             await rest_client._create_message(None, {}, **kwargs)
 
     @pytest.mark.skip("TODO")
@@ -2025,7 +2025,7 @@ async def test__edit_message_when_non_collection_passed_to_plural(self, rest_cli
             f"'{singular_arg}' (singular) instead?"
         )
 
-        with pytest.raises(TypeError, match=regex.escape(expected_error_message)):
+        with pytest.raises(TypeError, match=re.escape(expected_error_message)):
             await rest_client._edit_message(None, {}, **kwargs)
 
     @pytest.mark.skip("TODO")
@@ -3678,7 +3678,7 @@ async def test_create_role_when_icon_unicode_emoji_specified(self, rest_client):
             await rest_client.create_role(StubModel(123), icon="icon.png", unicode_emoji="\N{OK HAND SIGN}")
 
     async def test_reposition_roles(self, rest_client):
-        expected_route = routes.POST_GUILD_ROLES.compile(guild=123)
+        expected_route = routes.PATCH_GUILD_ROLES.compile(guild=123)
         expected_json = [{"id": "456", "position": 1}, {"id": "789", "position": 2}]
         rest_client._request = mock.AsyncMock()