Repository: hikari-py/hikari
Pull request #401 (2020-12-19 08:37:01)

Add missing Member.roles property
### 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.
- [x] I have made unittests according to the code I have added/modified/deleted.

### Related issues
#400 



Patch:
diff --git a/hikari/api/cache.py b/hikari/api/cache.py
index 1d3d411c..4ca2ea5a 100644
--- a/hikari/api/cache.py
+++ b/hikari/api/cache.py
@@ -45,7 +45,10 @@
 
 
 class CacheView(typing.Mapping[_KeyT, _ValueT], abc.ABC):
-    """Interface describing an immutable snapshot view of part of a cache."""
+    """Interface describing an immutable snapshot view of part of a cache.
+
+    This can be treated as a normal `typing.Mapping` but with some special methods.
+    """
 
     __slots__: typing.Sequence[str] = ()
 
diff --git a/hikari/guilds.py b/hikari/guilds.py
index d9a5cd7b..0a3b96eb 100644
--- a/hikari/guilds.py
+++ b/hikari/guilds.py
@@ -407,6 +407,21 @@ def presence(self) -> typing.Optional[presences_.MemberPresence]:
         """
         return self.app.cache.get_presence(self.guild_id, self.id)
 
+    @property
+    def roles(self) -> typing.Sequence[Role]:
+        """Return the roles the user has.
+
+        This will be empty if the roles are missing from the cache.
+
+        Returns
+        -------
+        typing.Sequence[hikari.guilds.Role]
+            The roles the users has.
+        """
+        roles_view = self.app.cache.get_roles_view_for_guild(self.guild_id)
+
+        return [r for r in roles_view.values() if r.id in self.role_ids]
+
     @property
     def top_role(self) -> typing.Optional[Role]:
         """Return the highest role the member has.
@@ -417,13 +432,7 @@ def top_role(self) -> typing.Optional[Role]:
             `builtins.None` if the cache is missing the roles information or
             the highest role the user has.
         """
-        roles_view = self.app.cache.get_roles_view_for_guild(self.guild_id)
-
-        roles = sorted(
-            (r for r in roles_view.values() if r.id in self.role_ids),
-            key=lambda r: r.position,
-            reverse=True,
-        )
+        roles = sorted(self.roles, key=lambda r: r.position, reverse=True)
 
         try:
             return next(iter(roles))
diff --git a/setup.py b/setup.py
index 50c199bc..f84abf4c 100644
--- a/setup.py
+++ b/setup.py
@@ -92,8 +92,7 @@ def parse_requirements_file(path):
         "Operating System :: OS Independent",
         "Programming Language :: Python :: 3.8",
         "Programming Language :: Python :: 3.9",
-        # TODO: enable when PyPI release with newest trove-classifiers dependency.
-        # "Programming Language :: Python :: 3.10",
+        "Programming Language :: Python :: 3.10",
         "Programming Language :: Python :: Implementation :: CPython",
         "Programming Language :: Python :: Implementation :: Stackless",
         "Topic :: Communications :: Chat",


Test patch:
diff --git a/tests/hikari/test_guilds.py b/tests/hikari/test_guilds.py
index 105479ea..32295faf 100644
--- a/tests/hikari/test_guilds.py
+++ b/tests/hikari/test_guilds.py
@@ -275,34 +275,45 @@ def test_mention_property_when_no_nickname(self, model, mock_user):
         model.nickname = None
         assert model.mention == mock_user.mention
 
-    def test_top_role_when_empty_cache(self, model):
-        model.app.cache.get_roles_view_for_guild.return_value = {}
+    def test_roles(self, model):
+        role1 = mock.Mock(id=321, position=2)
+        role2 = mock.Mock(id=654, position=1)
+        mock_cache_view = {321: role1, 654: role2}
+        model.app.cache.get_roles_view_for_guild.return_value = mock_cache_view
+        model.role_ids = [321, 654]
 
-        assert model.top_role is None
+        assert model.roles == [role1, role2]
 
         model.app.cache.get_roles_view_for_guild.assert_called_once_with(456)
 
-    def test_top_role_when_role_ids_not_in_cache(self, model):
+    def test_roles_when_role_ids_not_in_cache(self, model):
         role1 = mock.Mock(id=123, position=2)
         role2 = mock.Mock(id=456, position=1)
         mock_cache_view = {123: role1, 456: role2}
         model.app.cache.get_roles_view_for_guild.return_value = mock_cache_view
-        model.role_ids = [321, 654]
+        model.role_ids = [321, 456]
+
+        assert model.roles == [role2]
 
-        assert model.top_role is None
+        model.app.cache.get_roles_view_for_guild.assert_called_once_with(456)
+
+    def test_roles_when_empty_cache(self, model):
+        model.app.cache.get_roles_view_for_guild.return_value = {}
+
+        assert model.roles == []
 
         model.app.cache.get_roles_view_for_guild.assert_called_once_with(456)
 
     def test_top_role(self, model):
         role1 = mock.Mock(id=321, position=2)
         role2 = mock.Mock(id=654, position=1)
-        mock_cache_view = {321: role1, 654: role2}
-        model.app.cache.get_roles_view_for_guild.return_value = mock_cache_view
-        model.role_ids = [321, 654]
 
-        assert model.top_role is role1
+        with mock.patch.object(guilds.Member, "roles", new=[role1, role2]):
+            assert model.top_role is role1
 
-        model.app.cache.get_roles_view_for_guild.assert_called_once_with(456)
+    def test_top_role_when_roles_is_empty(self, model):
+        with mock.patch.object(guilds.Member, "roles", new=[]):
+            assert model.top_role is None
 
 
 class TestPartialGuild: