Repository: hikari-py/hikari (python)

Issue:
Document `Member.pending`
https://github.com/discord/discord-api-docs/commit/d89a5bf0fb6a4270eb3655d60b6944096edc9be0


Pull request:
Document Member.is_pending and the associated guild feature
### 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
Closes #390 


Patch:
diff --git a/hikari/guilds.py b/hikari/guilds.py
index d61ad06d..d9a5cd7b 100644
--- a/hikari/guilds.py
+++ b/hikari/guilds.py
@@ -150,6 +150,9 @@ class GuildFeature(str, enums.Enum):
     WELCOME_SCREEN_ENABLED = "WELCOME_SCREEN_ENABLED"
     """Guild has enabled the welcome screen."""
 
+    MEMBER_VERIFICATION_GATE_ENABLED = "MEMBER_VERIFICATION_GATE_ENABLED"
+    """Guild has enabled member verification gate."""
+
 
 GuildFeatureish = typing.Union[str, GuildFeature]
 """Type hint for possible guild features.
@@ -259,16 +262,21 @@ class Member(users.User):
     is_deaf: undefined.UndefinedOr[bool] = attr.ib(repr=False)
     """`builtins.True` if this member is deafened in the current voice channel.
 
-    This will be `hikari.undefined.UndefinedType if it's state is
+    This will be `hikari.undefined.UNDEFINED` if it's state is
     unknown.
     """
 
     is_mute: undefined.UndefinedOr[bool] = attr.ib(repr=False)
     """`builtins.True` if this member is muted in the current voice channel.
 
-    This will be `hikari.undefined.UndefinedType if it's state is unknown.
+    This will be `hikari.undefined.UNDEFINED` if it's state is unknown.
     """
 
+    is_pending: undefined.UndefinedOr[bool] = attr.ib(repr=False)
+    """Whether the user has passed the guild's membership screening requirements.
+
+    This will be `hikari.undefined.UNDEFINED` if it's state is unknown."""
+
     joined_at: datetime.datetime = attr.ib(repr=True)
     """The datetime of when this member joined the guild they belong to."""
 
diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py
index e7d86856..68a55348 100644
--- a/hikari/impl/entity_factory.py
+++ b/hikari/impl/entity_factory.py
@@ -1006,6 +1006,7 @@ def deserialize_member(
             premium_since=premium_since,
             is_deaf=payload["deaf"] if "deaf" in payload else undefined.UNDEFINED,
             is_mute=payload["mute"] if "mute" in payload else undefined.UNDEFINED,
+            is_pending=payload["pending"] if "pending" in payload else undefined.UNDEFINED,
         )
 
     def deserialize_role(
diff --git a/hikari/impl/shard.py b/hikari/impl/shard.py
index f32b0b63..4dcda965 100644
--- a/hikari/impl/shard.py
+++ b/hikari/impl/shard.py
@@ -194,14 +194,13 @@ async def _receive_and_check(self, timeout: typing.Optional[float], /) -> str:
                     errors.ShardCloseCode.RATE_LIMITED,
                 )
 
-                # Assume we can always resume first.
                 raise errors.GatewayServerClosedConnectionError(reason, close_code, can_reconnect)
 
             elif message.type == aiohttp.WSMsgType.CLOSING or message.type == aiohttp.WSMsgType.CLOSED:
                 # May be caused by the server shutting us down.
                 # May be caused by Windows injecting an EOF if something disconnects, as some
                 # network drivers appear to do this.
-                raise errors.GatewayError("Socket has closed")
+                raise errors.GatewayConnectionError("Socket has closed")
 
             elif len(buff) != 0 and message.type != aiohttp.WSMsgType.BINARY:
                 raise errors.GatewayError(f"Unexpected message type received {message.type.name}, expected BINARY")
diff --git a/hikari/internal/cache.py b/hikari/internal/cache.py
index 171e108c..37868ca8 100644
--- a/hikari/internal/cache.py
+++ b/hikari/internal/cache.py
@@ -394,6 +394,7 @@ class MemberData(BaseData[guilds.Member]):
     premium_since: typing.Optional[datetime.datetime] = attr.ib()
     is_deaf: undefined.UndefinedOr[bool] = attr.ib()
     is_mute: undefined.UndefinedOr[bool] = attr.ib()
+    is_pending: undefined.UndefinedOr[bool] = attr.ib()
     # meta-attribute
     has_been_deleted: bool = attr.ib(default=False)
     ref_count: int = attr.ib(default=0)
@@ -407,6 +408,7 @@ def build_from_entity(cls: typing.Type[MemberData], entity: guilds.Member) -> Me
             premium_since=entity.premium_since,
             is_deaf=entity.is_deaf,
             is_mute=entity.is_mute,
+            is_pending=entity.is_pending,
             id=entity.user.id,
             # role_ids is a special case as it may be mutable so we want to ensure it's immutable when cached.
             role_ids=tuple(entity.role_ids),
@@ -421,6 +423,7 @@ def build_entity(self, **kwargs: typing.Any) -> guilds.Member:
             premium_since=self.premium_since,
             is_deaf=self.is_deaf,
             is_mute=self.is_mute,
+            is_pending=self.is_pending,
             user=kwargs["user"],
         )
 
diff --git a/hikari/internal/data_binding.py b/hikari/internal/data_binding.py
index 8e81ac5c..c1a567a6 100644
--- a/hikari/internal/data_binding.py
+++ b/hikari/internal/data_binding.py
@@ -89,7 +89,7 @@ def load_json(_: typing.AnyStr) -> typing.Union[JSONArray, JSONObject]:
 class StringMapBuilder(multidict.MultiDict[str]):
     """Helper class used to quickly build query strings or header maps.
 
-    This will consume any items that are not `hikari.undefined.UndefinedType`.
+    This will consume any items that are not `hikari.undefined.UNDEFINED`.
     If a value _is_ unspecified, it will be ignored when inserting it. This reduces
     the amount of boilerplate needed for generating the headers and query strings for
     low-level HTTP API interaction, amongst other things.
@@ -157,7 +157,7 @@ def put(
 class JSONObjectBuilder(typing.Dict[str, JSONish]):
     """Helper class used to quickly build JSON objects from various values.
 
-    If provided with any values that are `hikari.undefined.UndefinedType`,
+    If provided with any values that are `hikari.undefined.UNDEFINED`,
     then these values will be ignored.
 
     This speeds up generation of JSON payloads for low level HTTP and websocket


Test patch:
diff --git a/tests/hikari/impl/test_entity_factory.py b/tests/hikari/impl/test_entity_factory.py
index 66ae8ef5..77fb65f0 100644
--- a/tests/hikari/impl/test_entity_factory.py
+++ b/tests/hikari/impl/test_entity_factory.py
@@ -1427,6 +1427,7 @@ def member_payload(self, user_payload):
             "premium_since": "2019-05-17T06:26:56.936000+00:00",
             "deaf": False,
             "mute": True,
+            "pending": False,
             "user": user_payload,
         }
 
@@ -1442,6 +1443,7 @@ def test_deserialize_member(self, entity_factory_impl, mock_app, member_payload,
         assert member.premium_since == datetime.datetime(2019, 5, 17, 6, 26, 56, 936000, tzinfo=datetime.timezone.utc)
         assert member.is_deaf is False
         assert member.is_mute is True
+        assert member.is_pending is False
         assert isinstance(member, guild_models.Member)
 
     def test_deserialize_member_when_guild_id_already_in_role_array(
@@ -1472,14 +1474,13 @@ def test_deserialize_member_with_null_fields(self, entity_factory_impl, user_pay
                 "premium_since": None,
                 "deaf": False,
                 "mute": True,
+                "pending": False,
                 "user": user_payload,
                 "guild_id": "123123453234",
             }
         )
         assert member.nickname is None
         assert member.premium_since is None
-        assert member.is_deaf is False
-        assert member.is_mute is True
         assert isinstance(member, guild_models.Member)
 
     def test_deserialize_member_with_undefined_fields(self, entity_factory_impl, user_payload):
@@ -1495,6 +1496,7 @@ def test_deserialize_member_with_undefined_fields(self, entity_factory_impl, use
         assert member.nickname is undefined.UNDEFINED
         assert member.is_deaf is undefined.UNDEFINED
         assert member.is_mute is undefined.UNDEFINED
+        assert member.is_pending is undefined.UNDEFINED
         assert member.premium_since == datetime.datetime(2019, 5, 17, 6, 26, 56, 936000, tzinfo=datetime.timezone.utc)
 
     def test_deserialize_member_with_passed_through_user_object_and_guild_id(self, entity_factory_impl):
diff --git a/tests/hikari/impl/test_stateful_cache.py b/tests/hikari/impl/test_stateful_cache.py
index 40140759..1ee0e37c 100644
--- a/tests/hikari/impl/test_stateful_cache.py
+++ b/tests/hikari/impl/test_stateful_cache.py
@@ -1540,6 +1540,7 @@ def test__build_member(self, cache_impl):
             premium_since=datetime.datetime(2020, 7, 17, 13, 11, 18, 384554, tzinfo=datetime.timezone.utc),
             is_deaf=False,
             is_mute=True,
+            is_pending=False,
         )
         mock_user = mock.MagicMock(users.User)
         cache_impl._user_entries = collections.FreezableDict(
@@ -1555,6 +1556,7 @@ def test__build_member(self, cache_impl):
         assert member.premium_since == datetime.datetime(2020, 7, 17, 13, 11, 18, 384554, tzinfo=datetime.timezone.utc)
         assert member.is_deaf is False
         assert member.is_mute is True
+        assert member.is_pending is False
 
     def test__build_member_for_passed_through_user(self, cache_impl):
         member_data = cache.MemberData(
@@ -1566,6 +1568,7 @@ def test__build_member_for_passed_through_user(self, cache_impl):
             premium_since=datetime.datetime(2020, 7, 17, 13, 11, 18, 384554, tzinfo=datetime.timezone.utc),
             is_deaf=False,
             is_mute=True,
+            is_pending=False,
         )
         mock_user = mock.MagicMock(users.User)
         cache_impl._user_entries = collections.FreezableDict({})
@@ -1902,6 +1905,7 @@ def test_set_member(self, cache_impl):
             premium_since=datetime.datetime(2020, 7, 1, 2, 0, 12, 501602, tzinfo=datetime.timezone.utc),
             is_deaf=True,
             is_mute=False,
+            is_pending=True,
         )
         cache_impl.set_user = mock.Mock()
         cache_impl._increment_user_ref_count = mock.Mock()
@@ -1927,6 +1931,7 @@ def test_set_member(self, cache_impl):
         )
         assert member_entry.is_deaf is True
         assert member_entry.is_mute is False
+        assert member_entry.is_pending is True
         assert not hasattr(member_entry, "user")
 
     def test_set_member_doesnt_increment_user_ref_count_for_pre_cached_member(self, cache_impl):
diff --git a/tests/hikari/integration/test_users_comparison.py b/tests/hikari/integration/test_users_comparison.py
index b8a5a1a5..341d85aa 100644
--- a/tests/hikari/integration/test_users_comparison.py
+++ b/tests/hikari/integration/test_users_comparison.py
@@ -78,6 +78,7 @@ def make_guild_member(user_id, username):
         premium_since=None,
         is_deaf=False,
         is_mute=False,
+        is_pending=False,
     )
 
 
diff --git a/tests/hikari/test_guilds.py b/tests/hikari/test_guilds.py
index 6ae0dd03..105479ea 100644
--- a/tests/hikari/test_guilds.py
+++ b/tests/hikari/test_guilds.py
@@ -196,6 +196,7 @@ def model(self, mock_user):
             guild_id=snowflakes.Snowflake(456),
             is_deaf=True,
             is_mute=True,
+            is_pending=False,
             joined_at=datetime.datetime.now().astimezone(),
             nickname="davb",
             premium_since=None,