Repository: hikari-py/hikari
Pull request #1084 (2022-03-20 16:08:12)

Add option to disable automatic member chunking
### Summary
Added kwarg to GatewayBot and EventManagerImpl which, when set to True, will prevent automatic chunking. I named it `disable_member_chunking` because that seemed to be the clearest.

### 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. (Modified the test_init test for TestGatewayBot)

### Related issues
#882 



Patch:
diff --git a/changes/1084.feature.md b/changes/1084.feature.md
new file mode 100644
index 00000000..16c233b4
--- /dev/null
+++ b/changes/1084.feature.md
@@ -0,0 +1,2 @@
+Add option to disable automatic member chunking.
+Added the `auto_chunk_members` kwarg to `GatewayBot` and `EventManagerImpl`, which when `False` will disable automatic member chunking.
diff --git a/changes/1108.bugfix.md b/changes/1108.bugfix.md
new file mode 100644
index 00000000..ed671938
--- /dev/null
+++ b/changes/1108.bugfix.md
@@ -0,0 +1,1 @@
+Improved pyright support.
diff --git a/changes/1119.bugfix.md b/changes/1119.bugfix.md
new file mode 100644
index 00000000..f3e4007e
--- /dev/null
+++ b/changes/1119.bugfix.md
@@ -0,0 +1,1 @@
+`RESTClientImpl.fetch_bans` now return a `LazyIterator` to allow pagination of values.
diff --git a/examples/__init__.py b/examples/__init__.py
index 10e6d281..81d74ef3 100644
--- a/examples/__init__.py
+++ b/examples/__init__.py
@@ -7,4 +7,4 @@
 #
 # You should have received a copy of the CC0 Public Domain Dedication along with this software.
 # If not, see <https://creativecommons.org/publicdomain/zero/1.0/>.
-"""Allows mypy to run here."""
+"""Allows type-checkers to run here."""
diff --git a/hikari/api/rest.py b/hikari/api/rest.py
index 9068d967..062b21bf 100644
--- a/hikari/api/rest.py
+++ b/hikari/api/rest.py
@@ -1199,9 +1199,6 @@ async def create_message(
             `role_mentions` or `user_mentions` or if both `attachment` and
             `attachments`, `component` and `components` or `embed` and `embeds`
             are specified.
-        builtins.TypeError
-            If `attachments`, `components` or `embeds` is passed but is not a
-            sequence.
         hikari.errors.BadRequestError
             This may be raised in several discrete situations, such as messages
             being empty with no attachments or embeds; messages with more than
@@ -1446,9 +1443,6 @@ async def edit_message(
         builtins.ValueError
             If both `attachment` and `attachments`, `component` and `components`
             or `embed` and `embeds` are specified.
-        builtins.TypeError
-            If `attachments`, `components` or `embeds` is passed but is not a
-            sequence.
         hikari.errors.BadRequestError
             This may be raised in several discrete situations, such as messages
             being empty with no embeds; messages with more than 2000 characters
@@ -2358,8 +2352,6 @@ async def execute_webhook(
             If more than 100 unique objects/entities are passed for
             `role_mentions` or `user_mentions` or if both `attachment` and
             `attachments` or `embed` and `embeds` are specified.
-        builtins.TypeError
-            If `attachments`, or `embeds` is passed but is not a sequence.
         hikari.errors.BadRequestError
             This may be raised in several discrete situations, such as messages
             being empty with no attachments or embeds; messages with more than
@@ -2579,9 +2571,6 @@ async def edit_webhook_message(
         builtins.ValueError
             If both `attachment` and `attachments`, `component` and `components`
             or `embed` and `embeds` are specified.
-        builtins.TypeError
-            If `attachments`, `components` or `embeds` is passed but is not a
-            sequence.
         hikari.errors.BadRequestError
             This may be raised in several discrete situations, such as messages
             being empty with no attachments or embeds; messages with more than
@@ -5509,21 +5498,42 @@ async def fetch_ban(
         """
 
     @abc.abstractmethod
-    async def fetch_bans(
+    def fetch_bans(
         self,
         guild: snowflakes.SnowflakeishOr[guilds.PartialGuild],
-    ) -> typing.Sequence[guilds.GuildBan]:
+        /,
+        *,
+        newest_first: bool = False,
+        start_at: undefined.UndefinedOr[snowflakes.SearchableSnowflakeishOr[users.PartialUser]] = undefined.UNDEFINED,
+    ) -> iterators.LazyIterator[guilds.GuildBan]:
         """Fetch the bans of a guild.
 
+        !!! note
+            This call is not a coroutine function, it returns a special type of
+            lazy iterator that will perform API calls as you iterate across it.
+            See `hikari.iterators` for the full API for this iterator type.
+
         Parameters
         ----------
         guild : hikari.snowflakes.SnowflakeishOr[hikari.guilds.PartialGuild]
             The guild to fetch the bans from. This may be the
             object or the ID of an existing guild.
 
+        Other Parameters
+        ----------------
+        newest_first : builtins.bool
+            Whether to fetch the newest first or the oldest first.
+
+            Defaults to `builtins.False`.
+        start_at : undefined.UndefinedOr[snowflakes.SearchableSnowflakeishOr[users.PartialUser]]
+            If provided, will start at this snowflake. If you provide
+            a datetime object, it will be transformed into a snowflake. This
+            may also be a scheduled event object object. In this case, the
+            date the object was first created will be used.
+
         Returns
         -------
-        typing.Sequence[hikari.guilds.GuildBan]
+        hikari.iterators.LazyIterator[hikari.guilds.GuildBan]
             The requested bans.
 
         Raises
@@ -7715,9 +7725,6 @@ async def edit_interaction_response(
         builtins.ValueError
             If both `attachment` and `attachments`, `component` and `components`
             or `embed` and `embeds` are specified.
-        builtins.TypeError
-            If `attachments`, `components` or `embeds` is passed but is not a
-            sequence.
         hikari.errors.BadRequestError
             This may be raised in several discrete situations, such as messages
             being empty with no attachments or embeds; messages with more than
diff --git a/hikari/colors.py b/hikari/colors.py
index cbdb9317..7808b681 100644
--- a/hikari/colors.py
+++ b/hikari/colors.py
@@ -517,12 +517,12 @@ def of(cls, value: Colorish, /) -> Color:
             if len(value) != 3:
                 raise ValueError(f"Color must be an RGB triplet if set to a {type(value).__name__} type")
 
-            if any(isinstance(c, float) for c in value):
-                r, g, b = value
+            r, g, b = value
+
+            if isinstance(r, float) and isinstance(g, float) and isinstance(b, float):
                 return cls.from_rgb_float(r, g, b)
 
-            if all(isinstance(c, int) for c in value):
-                r, g, b = value
+            if isinstance(r, int) and isinstance(g, int) and isinstance(b, int):
                 return cls.from_rgb(r, g, b)
 
         if isinstance(value, str):
diff --git a/hikari/embeds.py b/hikari/embeds.py
index 24321c52..442b61e1 100644
--- a/hikari/embeds.py
+++ b/hikari/embeds.py
@@ -278,11 +278,11 @@ def is_inline(self, value: bool) -> None:
         self._inline = value
 
 
-def _ensure_embed_resource(resource: files.Resourceish, cls: typing.Type[_T]) -> _T:
+def _ensure_embed_resource(resource: files.Resourceish) -> files.Resource[files.AsyncReader]:
     if isinstance(resource, EmbedResource):
-        return cls(resource=resource.resource)
+        return resource.resource
 
-    return cls(resource=files.ensure_resource(resource))
+    return files.ensure_resource(resource)
 
 
 class Embed:
@@ -742,7 +742,7 @@ def set_author(
         if name is None and url is None and icon is None:
             self._author = None
         else:
-            real_icon = _ensure_embed_resource(icon, EmbedResourceWithProxy) if icon is not None else None
+            real_icon = EmbedResourceWithProxy(resource=_ensure_embed_resource(icon)) if icon is not None else None
             self._author = EmbedAuthor(name=name, url=url, icon=real_icon)
         return self
 
@@ -791,7 +791,7 @@ def set_footer(self, text: typing.Optional[str], *, icon: typing.Optional[files.
 
             self._footer = None
         else:
-            real_icon = _ensure_embed_resource(icon, EmbedResourceWithProxy) if icon is not None else None
+            real_icon = EmbedResourceWithProxy(resource=_ensure_embed_resource(icon)) if icon is not None else None
             self._footer = EmbedFooter(icon=real_icon, text=text)
         return self
 
@@ -829,7 +829,7 @@ def set_image(self, image: typing.Optional[files.Resourceish] = None, /) -> Embe
             This embed. Allows for call chaining.
         """
         if image is not None:
-            self._image = _ensure_embed_resource(image, EmbedImage)
+            self._image = EmbedImage(resource=_ensure_embed_resource(image))
         else:
             self._image = None
 
@@ -868,7 +868,7 @@ def set_thumbnail(self, image: typing.Optional[files.Resourceish] = None, /) ->
             This embed. Allows for call chaining.
         """
         if image is not None:
-            self._thumbnail = _ensure_embed_resource(image, EmbedImage)
+            self._thumbnail = EmbedImage(resource=_ensure_embed_resource(image))
         else:
             self._thumbnail = None
 
diff --git a/hikari/events/typing_events.py b/hikari/events/typing_events.py
index 40756df7..6d02278c 100644
--- a/hikari/events/typing_events.py
+++ b/hikari/events/typing_events.py
@@ -196,12 +196,12 @@ def user_id(self) -> snowflakes.Snowflake:
         # <<inherited docstring from TypingEvent>>.
         return self.member.id
 
-    async def fetch_channel(self) -> typing.Union[channels.TextableGuildChannel]:
+    async def fetch_channel(self) -> channels.TextableGuildChannel:
         """Perform an API call to fetch an up-to-date image of this channel.
 
         Returns
         -------
-        typing.Union[hikari.channels.TextableGuildChannel]
+        hikari.channels.TextableGuildChannel
             The channel.
         """
         channel = await super().fetch_channel()
diff --git a/hikari/impl/bot.py b/hikari/impl/bot.py
index d77820cc..6107facd 100644
--- a/hikari/impl/bot.py
+++ b/hikari/impl/bot.py
@@ -207,6 +207,19 @@ class GatewayBot(traits.GatewayBotAware):
         Defaults to `hikari.intents.Intents.ALL_UNPRIVILEGED`. This allows you
         to change which intents your application will use on the gateway. This
         can be used to control and change the types of events you will receive.
+    auto_chunk_members : builtins.bool
+        Defaults to `builtins.True`. If `builtins.False`, then no member chunks
+        will be requested automatically, even if there are reasons to do so.
+
+        All following statements must be true to automatically request chunks:
+
+        1. `auto_chunk_members` is `builtins.True`.
+        2. The members intent is enabled.
+        3. The server is marked as "large" or the presences intent is not enabled
+            (since Discord only sends other members when presences are declared,
+            we should also chunk small guilds if the presences are not declared).
+        4. The members cache is enabled or there are listeners for the
+        `MemberChunkEvent`.
     logs : typing.Union[builtins.None, LoggerLevel, typing.Dict[str, typing.Any]]
         Defaults to `"INFO"`.
 
@@ -320,6 +333,7 @@ def __init__(
         cache_settings: typing.Optional[config_impl.CacheSettings] = None,
         http_settings: typing.Optional[config_impl.HTTPSettings] = None,
         intents: intents_.Intents = intents_.Intents.ALL_UNPRIVILEGED,
+        auto_chunk_members: bool = True,
         logs: typing.Union[None, int, str, typing.Dict[str, typing.Any]] = "INFO",
         max_rate_limit: float = 300,
         max_retries: int = 3,
@@ -352,7 +366,11 @@ def __init__(
 
         # Event handling
         self._event_manager = event_manager_impl.EventManagerImpl(
-            self._entity_factory, self._event_factory, self._intents, cache=self._cache
+            self._entity_factory,
+            self._event_factory,
+            self._intents,
+            auto_chunk_members=auto_chunk_members,
+            cache=self._cache,
         )
 
         # Voice subsystem
diff --git a/hikari/impl/buckets.py b/hikari/impl/buckets.py
index bc5848d8..1733c5dd 100644
--- a/hikari/impl/buckets.py
+++ b/hikari/impl/buckets.py
@@ -496,7 +496,7 @@ def do_gc_pass(self, expire_after: float) -> None:
             `RESTBucketManager.start` and `RESTBucketManager.close` to control
             this instead.
         """
-        buckets_to_purge = []
+        buckets_to_purge: typing.List[str] = []
 
         now = time.monotonic()
 
diff --git a/hikari/impl/config.py b/hikari/impl/config.py
index 9fc03597..581a878c 100644
--- a/hikari/impl/config.py
+++ b/hikari/impl/config.py
@@ -246,7 +246,7 @@ class HTTPTimeoutSettings:
     @request_socket_connect.validator
     @request_socket_read.validator
     @total.validator
-    def _(self, attrib: attr.Attribute[typing.Optional[float]], value: typing.Optional[float]) -> None:
+    def _(self, attrib: attr.Attribute[typing.Optional[float]], value: typing.Any) -> None:
         # This error won't occur until some time in the future where it will be annoying to
         # try and determine the root cause, so validate it NOW.
         if value is not None and (not isinstance(value, (float, int)) or value <= 0):
@@ -317,7 +317,7 @@ class HTTPSettings(config.HTTPSettings):
     """
 
     @max_redirects.validator
-    def _(self, _: attr.Attribute[typing.Optional[int]], value: typing.Optional[int]) -> None:
+    def _(self, _: attr.Attribute[typing.Optional[int]], value: typing.Any) -> None:
         # This error won't occur until some time in the future where it will be annoying to
         # try and determine the root cause, so validate it NOW.
         if value is not None and (not isinstance(value, int) or value <= 0):
diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py
index 5d31777a..27ceaa4d 100644
--- a/hikari/impl/entity_factory.py
+++ b/hikari/impl/entity_factory.py
@@ -636,7 +636,7 @@ def deserialize_implicit_token(self, query: data_binding.Query) -> application_m
     def _deserialize_audit_log_change_roles(
         self, payload: data_binding.JSONArray
     ) -> typing.Mapping[snowflakes.Snowflake, guild_models.PartialRole]:
-        roles = {}
+        roles: typing.Dict[snowflakes.Snowflake, guild_models.PartialRole] = {}
         for role_payload in payload:
             role = guild_models.PartialRole(
                 app=self._app, id=snowflakes.Snowflake(role_payload["id"]), name=role_payload["name"]
@@ -710,7 +710,7 @@ def deserialize_audit_log(self, payload: data_binding.JSONObject) -> audit_log_m
         for entry_payload in payload["audit_log_entries"]:
             entry_id = snowflakes.Snowflake(entry_payload["id"])
 
-            changes = []
+            changes: typing.List[audit_log_models.AuditLogChange] = []
             if (change_payloads := entry_payload.get("changes")) is not None:
                 for change_payload in change_payloads:
                     key: typing.Union[audit_log_models.AuditLogChangeKey, str] = audit_log_models.AuditLogChangeKey(
@@ -723,7 +723,9 @@ def deserialize_audit_log(self, payload: data_binding.JSONObject) -> audit_log_m
                         new_value = value_converter(new_value) if new_value is not None else None
                         old_value = value_converter(old_value) if old_value is not None else None
 
-                    elif not isinstance(key, audit_log_models.AuditLogChangeKey):
+                    elif not isinstance(
+                        key, audit_log_models.AuditLogChangeKey
+                    ):  # pyright: ignore [reportUnnecessaryIsInstance]
                         _LOGGER.debug("Unknown audit log change key found %r", key)
 
                     changes.append(audit_log_models.AuditLogChange(key=key, new_value=new_value, old_value=old_value))
@@ -1375,10 +1377,9 @@ def deserialize_member(
             time.iso8601_datetime_string_to_datetime(raw_premium_since) if raw_premium_since is not None else None
         )
 
+        communication_disabled_until: typing.Optional[datetime.datetime] = None
         if raw_communication_disabled_until := payload.get("communication_disabled_until"):
             communication_disabled_until = time.iso8601_datetime_string_to_datetime(raw_communication_disabled_until)
-        else:
-            communication_disabled_until = None
 
         return guild_models.Member(
             user=user,
@@ -2622,7 +2623,7 @@ def deserialize_member_presence(  # noqa: CFQ001 - Max function length
         *,
         guild_id: undefined.UndefinedOr[snowflakes.Snowflake] = undefined.UNDEFINED,
     ) -> presence_models.MemberPresence:
-        activities = []
+        activities: typing.List[presence_models.RichActivity] = []
         for activity_payload in payload["activities"]:
             timestamps: typing.Optional[presence_models.ActivityTimestamps] = None
             if "timestamps" in activity_payload:
diff --git a/hikari/impl/event_manager.py b/hikari/impl/event_manager.py
index a9feacbf..e635df2c 100644
--- a/hikari/impl/event_manager.py
+++ b/hikari/impl/event_manager.py
@@ -92,7 +92,7 @@ async def _request_guild_members(
 class EventManagerImpl(event_manager_base.EventManagerBase):
     """Provides event handling logic for Discord events."""
 
-    __slots__: typing.Sequence[str] = ("_cache", "_entity_factory")
+    __slots__: typing.Sequence[str] = ("_cache", "_entity_factory", "_auto_chunk_members")
 
     def __init__(
         self,
@@ -101,9 +101,11 @@ def __init__(
         intents: intents_.Intents,
         /,
         *,
+        auto_chunk_members: bool = True,
         cache: typing.Optional[cache_.MutableCache] = None,
     ) -> None:
         self._cache = cache
+        self._auto_chunk_members = auto_chunk_members
         self._entity_factory = entity_factory
         components = cache.settings.components if cache else config.CacheComponents.NONE
         super().__init__(event_factory=event_factory, intents=intents, cache_components=components)
@@ -257,7 +259,8 @@ async def on_guild_create(  # noqa: C901 - Function too complex
         # payload if presence intents are also declared, so if this isn't the case then we also want
         # to chunk small guilds.
         if (
-            self._intents & intents_.Intents.GUILD_MEMBERS
+            self._auto_chunk_members
+            and self._intents & intents_.Intents.GUILD_MEMBERS
             and (payload.get("large") or not presences_declared)
             and (
                 self._cache_enabled_for(config.CacheComponents.MEMBERS)
diff --git a/hikari/impl/event_manager_base.py b/hikari/impl/event_manager_base.py
index c4a98354..e855d314 100644
--- a/hikari/impl/event_manager_base.py
+++ b/hikari/impl/event_manager_base.py
@@ -63,7 +63,7 @@
         typing.List[event_manager_.CallbackT[base_events.EventT]],
     ]
     _WaiterT = typing.Tuple[
-        typing.Optional[event_manager_.PredicateT[base_events.EventT]], asyncio.Future[base_events.EventT]
+        typing.Optional[event_manager_.PredicateT[base_events.EventT]], "asyncio.Future[base_events.EventT]"
     ]
     _WaiterMapT = typing.Dict[typing.Type[base_events.EventT], typing.Set[_WaiterT[base_events.EventT]]]
 
@@ -103,8 +103,8 @@ def __events_bitmask__(self) -> int:
 
 def _generate_weak_listener(
     reference: weakref.WeakMethod[typing.Any],
-) -> typing.Callable[[base_events.EventT], typing.Coroutine[typing.Any, typing.Any, None]]:
-    async def call_weak_method(event: base_events.EventT) -> None:
+) -> typing.Callable[[base_events.Event], typing.Coroutine[typing.Any, typing.Any, None]]:
+    async def call_weak_method(event: base_events.Event) -> None:
         method = reference()
         if method is None:
             raise TypeError(
@@ -555,10 +555,7 @@ def decorator(
 
         return decorator
 
-    def dispatch(self, event: base_events.EventT) -> asyncio.Future[typing.Any]:
-        if not isinstance(event, base_events.Event):
-            raise TypeError(f"Events must be subclasses of {base_events.Event.__name__}, not {type(event).__name__}")
-
+    def dispatch(self, event: base_events.Event) -> asyncio.Future[typing.Any]:
         tasks: typing.List[typing.Coroutine[None, typing.Any, None]] = []
 
         for cls in event.dispatches():
@@ -613,6 +610,7 @@ async def wait_for(
 
         future: asyncio.Future[base_events.EventT] = asyncio.get_running_loop().create_future()
 
+        waiter_set: typing.MutableSet[_WaiterT[base_events.Event]]
         try:
             waiter_set = self._waiters[event_type]
         except KeyError:
diff --git a/hikari/impl/rest.py b/hikari/impl/rest.py
index a9e7c6c1..ec3af346 100644
--- a/hikari/impl/rest.py
+++ b/hikari/impl/rest.py
@@ -260,9 +260,6 @@ def proxy_settings(self) -> config_impl.ProxySettings:
         return self._rest().proxy_settings
 
 
-_NONE_OR_UNDEFINED: typing.Final[typing.Tuple[None, undefined.UndefinedType]] = (None, undefined.UNDEFINED)
-
-
 class RESTApp(traits.ExecutorAware):
     """The base for a HTTP-only Discord application.
 
@@ -762,7 +759,7 @@ async def _request(
                     )
 
                     if trace_logging_enabled:
-                        time_taken = (time.monotonic() - start) * 1_000
+                        time_taken = (time.monotonic() - start) * 1_000  # pyright: ignore[reportUnboundVariable]
                         _LOGGER.log(
                             ux.TRACE,
                             "%s %s %s in %sms\n%s",
@@ -1246,24 +1243,6 @@ def _build_message_payload(  # noqa: C901- Function too complex
         if not undefined.any_undefined(embed, embeds):
             raise ValueError("You may only specify one of 'embed' or 'embeds', not both")
 
-        if attachments is not undefined.UNDEFINED and not isinstance(attachments, typing.Collection):
-            raise TypeError(
-                "You passed a non-collection to 'attachments', but this expects a collection. Maybe you meant to "
-                "use 'attachment' (singular) instead?"
-            )
-
-        if components not in _NONE_OR_UNDEFINED and not isinstance(components, typing.Collection):
-            raise TypeError(
-                "You passed a non-collection to 'components', but this expects a collection. Maybe you meant to "
-                "use 'component' (singular) instead?"
-            )
-
-        if embeds not in _NONE_OR_UNDEFINED and not isinstance(embeds, typing.Collection):
-            raise TypeError(
-                "You passed a non-collection to 'embeds', but this expects a collection. Maybe you meant to "
-                "use 'embed' (singular) instead?"
-            )
-
         if undefined.all_undefined(embed, embeds) and isinstance(content, embeds_.Embed):
             # Syntactic sugar, common mistake to accidentally send an embed
             # as the content, so let's detect this and fix it for the user.
@@ -2786,13 +2765,24 @@ async def fetch_ban(
         assert isinstance(response, dict)
         return self._entity_factory.deserialize_guild_member_ban(response)
 
-    async def fetch_bans(
-        self, guild: snowflakes.SnowflakeishOr[guilds.PartialGuild]
-    ) -> typing.Sequence[guilds.GuildBan]:
-        route = routes.GET_GUILD_BANS.compile(guild=guild)
-        response = await self._request(route)
-        assert isinstance(response, list)
-        return [self._entity_factory.deserialize_guild_member_ban(ban_payload) for ban_payload in response]
+    def fetch_bans(
+        self,
+        guild: snowflakes.SnowflakeishOr[guilds.PartialGuild],
+        /,
+        *,
+        newest_first: bool = False,
+        start_at: undefined.UndefinedOr[snowflakes.SearchableSnowflakeishOr[users.PartialUser]] = undefined.UNDEFINED,
+    ) -> iterators.LazyIterator[guilds.GuildBan]:
+        if start_at is undefined.UNDEFINED:
+            start_at = snowflakes.Snowflake.max() if newest_first else snowflakes.Snowflake.min()
+        elif isinstance(start_at, datetime.datetime):
+            start_at = snowflakes.Snowflake.from_datetime(start_at)
+        else:
+            start_at = int(start_at)
+
+        return special_endpoints_impl.GuildBanIterator(
+            self._entity_factory, self._request, guild, newest_first, str(start_at)
+        )
 
     async def fetch_roles(
         self,
diff --git a/hikari/impl/special_endpoints.py b/hikari/impl/special_endpoints.py
index 772d1559..a2aa18f7 100644
--- a/hikari/impl/special_endpoints.py
+++ b/hikari/impl/special_endpoints.py
@@ -609,6 +609,58 @@ async def _next_chunk(self) -> typing.Optional[typing.Generator[applications.Own
         return (self._entity_factory.deserialize_own_guild(g) for g in chunk)
 
 
+# We use an explicit forward reference for this, since this breaks potential
+# circular import issues (once the file has executed, using those resources is
+# not an issue for us).
+class GuildBanIterator(iterators.BufferedLazyIterator["guilds.GuildBan"]):
+    """Iterator implementation for retrieving guild bans."""
+
+    __slots__: typing.Sequence[str] = (
+        "_entity_factory",
+        "_guild_id",
+        "_request_call",
+        "_route",
+        "_first_id",
+        "_newest_first",
+    )
+
+    def __init__(
+        self,
+        entity_factory: entity_factory_.EntityFactory,
+        request_call: typing.Callable[
+            ..., typing.Coroutine[None, None, typing.Union[None, data_binding.JSONObject, data_binding.JSONArray]]
+        ],
+        guild: snowflakes.SnowflakeishOr[guilds.PartialGuild],
+        newest_first: bool,
+        first_id: str,
+    ) -> None:
+        super().__init__()
+        self._guild_id = snowflakes.Snowflake(str(int(guild)))
+        self._route = routes.GET_GUILD_BANS.compile(guild=guild)
+        self._request_call = request_call
+        self._entity_factory = entity_factory
+        self._first_id = first_id
+        self._newest_first = newest_first
+
+    async def _next_chunk(self) -> typing.Optional[typing.Generator[guilds.GuildBan, typing.Any, None]]:
+        query = data_binding.StringMapBuilder()
+        query.put("before" if self._newest_first else "after", self._first_id)
+        query.put("limit", 1000)
+
+        chunk = await self._request_call(compiled_route=self._route, query=query)
+        assert isinstance(chunk, list)
+
+        if not chunk:
+            return None
+
+        if self._newest_first:
+            # These are always returned in ascending order by `.user.id`.
+            chunk.reverse()
+
+        self._first_id = chunk[-1]["user"]["id"]
+        return (self._entity_factory.deserialize_guild_member_ban(b) for b in chunk)
+
+
 # We use an explicit forward reference for this, since this breaks potential
 # circular import issues (once the file has executed, using those resources is
 # not an issue for us).
diff --git a/hikari/internal/attr_extensions.py b/hikari/internal/attr_extensions.py
index 82fa4743..faf114d6 100644
--- a/hikari/internal/attr_extensions.py
+++ b/hikari/internal/attr_extensions.py
@@ -60,7 +60,7 @@ def invalidate_deep_copy_cache() -> None:
 
 
 def get_fields_definition(
-    cls: typing.Type[ModelT],
+    cls: type,
 ) -> typing.Tuple[
     typing.Sequence[typing.Tuple[attr.Attribute[typing.Any], str]], typing.Sequence[attr.Attribute[typing.Any]]
 ]:
@@ -76,8 +76,8 @@ def get_fields_definition(
     typing.Sequence[typing.Tuple[builtins.str, builtins.str]]
         A sequence of tuples of string attribute names to string key-word names.
     """
-    init_results = []
-    non_init_results = []
+    init_results: typing.List[typing.Tuple[attr.Attribute[typing.Any], str]] = []
+    non_init_results: typing.List[attr.Attribute[typing.Any]] = []
 
     for field in attr.fields(cls):
         if field.init:
diff --git a/hikari/internal/collections.py b/hikari/internal/collections.py
index db61827b..3c5509e0 100644
--- a/hikari/internal/collections.py
+++ b/hikari/internal/collections.py
@@ -134,28 +134,6 @@ def __setitem__(self, key: KeyT, value: ValueT) -> None:
         self._data[key] = value
 
 
-class _FrozenDict(typing.MutableMapping[KeyT, ValueT]):
-    __slots__: typing.Sequence[str] = ("_source",)
-
-    def __init__(self, source: typing.Dict[KeyT, typing.Tuple[float, ValueT]], /) -> None:
-        self._source = source
-
-    def __getitem__(self, key: KeyT) -> ValueT:
-        return self._source[key][1]
-
-    def __iter__(self) -> typing.Iterator[KeyT]:
-        return iter(self._source)
-
-    def __len__(self) -> int:
-        return len(self._source)
-
-    def __delitem__(self, key: KeyT) -> None:
-        del self._source[key]
-
-    def __setitem__(self, key: KeyT, value: ValueT) -> None:
-        self._source[key] = (0.0, value)
-
-
 class LimitedCapacityCacheMap(ExtendedMutableMapping[KeyT, ValueT]):
     """Implementation of a capacity-limited most-recently-inserted mapping.
 
@@ -357,8 +335,6 @@ def get_index_or_slice(
 
     Raises
     ------
-    TypeError
-        If `index_or_slice` isn't a `builtins.slice` or `builtins.int`.
     IndexError
         If `index_or_slice` is an int and is outside the range of the mapping's
         contents.
@@ -366,10 +342,7 @@ def get_index_or_slice(
     if isinstance(index_or_slice, slice):
         return tuple(itertools.islice(mapping.values(), index_or_slice.start, index_or_slice.stop, index_or_slice.step))
 
-    if isinstance(index_or_slice, int):
-        try:
-            return next(itertools.islice(mapping.values(), index_or_slice, None))
-        except StopIteration:
-            raise IndexError(index_or_slice) from None
-
-    raise TypeError(f"sequence indices must be integers or slices, not {type(index_or_slice).__name__}")
+    try:
+        return next(itertools.islice(mapping.values(), index_or_slice, None))
+    except StopIteration:
+        raise IndexError(index_or_slice) from None
diff --git a/hikari/internal/reflect.py b/hikari/internal/reflect.py
index d28af6c3..ec1524b1 100644
--- a/hikari/internal/reflect.py
+++ b/hikari/internal/reflect.py
@@ -62,7 +62,7 @@ def resolve_signature(func: typing.Callable[..., typing.Any]) -> inspect.Signatu
 
     signature = inspect.signature(func)
     resolved_typehints = typing.get_type_hints(func)
-    params = []
+    params: typing.List[inspect.Parameter] = []
 
     none_type = type(None)
     for name, param in signature.parameters.items():
diff --git a/hikari/internal/ux.py b/hikari/internal/ux.py
index e4604a5c..d38c744e 100644
--- a/hikari/internal/ux.py
+++ b/hikari/internal/ux.py
@@ -222,8 +222,7 @@ def print_banner(
         for code in colorlog.escape_codes.escape_codes:
             args[code] = ""
 
-    # 1 maps to stdout
-    with open(1, "w", encoding="utf-8") as stdout:
+    with open(sys.stdout.fileno(), "w", encoding="utf-8", closefd=False) as stdout:
         stdout.write(string.Template(raw_banner).safe_substitute(args))
 
 
diff --git a/hikari/iterators.py b/hikari/iterators.py
index 0aeeabe6..8b83de56 100644
--- a/hikari/iterators.py
+++ b/hikari/iterators.py
@@ -115,7 +115,7 @@ def __call__(self, item: ValueT) -> bool:
     def __invert__(self) -> typing.Callable[[ValueT], bool]:
         return lambda item: not self(item)
 
-    def __or__(self, other: All[ValueT]) -> All[ValueT]:
+    def __or__(self, other: typing.Any) -> All[ValueT]:
         if not isinstance(other, All):
             raise TypeError(f"unsupported operand type(s) for |: {type(self).__name__!r} and {type(other).__name__!r}")
 
diff --git a/hikari/snowflakes.py b/hikari/snowflakes.py
index b0f06576..77d8ac0b 100644
--- a/hikari/snowflakes.py
+++ b/hikari/snowflakes.py
@@ -93,7 +93,7 @@ def min(cls) -> Snowflake:
             return cls.___MIN___
 
         except AttributeError:
-            cls.___MIN___ = Snowflake(0)
+            cls.___MIN___ = Snowflake(0)  # pyright: ignore[reportConstantRedefinition]
             return cls.___MIN___
 
     @classmethod
@@ -103,7 +103,7 @@ def max(cls) -> Snowflake:
             return cls.___MAX___
 
         except AttributeError:
-            cls.___MAX___ = Snowflake((1 << 63) - 1)
+            cls.___MAX___ = Snowflake((1 << 63) - 1)  # pyright: ignore[reportConstantRedefinition]
             return cls.___MAX___
 
     @classmethod
diff --git a/pipelines/mypy.nox.py b/pipelines/mypy.nox.py
index bb5a5395..65c0cfde 100644
--- a/pipelines/mypy.nox.py
+++ b/pipelines/mypy.nox.py
@@ -34,7 +34,7 @@
 
 @nox.session(reuse_venv=True)
 def mypy(session: nox.Session) -> None:
-    """Perform static type analysis on Python source code."""
+    """Perform static type analysis on Python source code using mypy."""
     session.install(
         "-r",
         "requirements.txt",
diff --git a/pipelines/pyright.nox.py b/pipelines/pyright.nox.py
index 60eecdbf..4e2d6d01 100644
--- a/pipelines/pyright.nox.py
+++ b/pipelines/pyright.nox.py
@@ -25,6 +25,27 @@
 from pipelines import nox
 
 
+@nox.session()
+def pyright(session: nox.Session) -> None:
+    """Perform static type analysis on Python source code using pyright.
+
+    At the time of writing this, this pipeline will not run successfully,
+    as hikari does not have 100% compatibility with pyright just yet. This
+    exists to make it easier to test and eventually reach that 100% compatibility.
+    """
+    session.install(
+        "-r",
+        "requirements.txt",
+        "-r",
+        "speedup-requirements.txt",
+        "-r",
+        "server-requirements.txt",
+        "-r",
+        "dev-requirements.txt",
+    )
+    session.run("python", "-m", "pyright")
+
+
 @nox.session()
 def verify_types(session: nox.Session) -> None:
     """Verify the "type completeness" of types exported by the library using Pyright."""
diff --git a/pyproject.toml b/pyproject.toml
index dcbab9a8..29e5efc0 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -59,10 +59,24 @@ exclude_lines = [
 ]
 
 [tool.pyright]
-include = ["examples", "hikari"]
+include = ["hikari", "examples"]
+exclude = ["examples/simple_dashboard.py", "**/__init__.py", "hikari/internal/enums.py", "hikari/internal/fast_protocol.py"]
 pythonVersion = "3.8"
 typeCheckingMode = "strict"
 
+reportUnnecessaryTypeIgnoreComment = "error"
+reportMissingTypeStubs = "none"
+reportImportCycles = "none" # Doesn't account for TYPE_CHECKING
+reportIncompatibleMethodOverride = "none"  # This relies on ordering for keyword-only arguments
+reportOverlappingOverload = "none" # Type-Vars in last overloads may interfere
+reportIncompatibleVariableOverride = "none" # Cannot overwrite abstract properties using attrs
+
+# Attrs validators will always be unknown
+# https://github.com/python-attrs/attrs/issues/795
+reportUnknownMemberType = "warning"
+reportUntypedFunctionDecorator = "warning"
+reportOptionalMemberAccess = "warning"
+
 [tool.pytest.ini_options]
 asyncio_mode = "strict"
 xfail_strict = true


Test patch:
diff --git a/tests/hikari/impl/test_bot.py b/tests/hikari/impl/test_bot.py
index 29cce434..0a632bc2 100644
--- a/tests/hikari/impl/test_bot.py
+++ b/tests/hikari/impl/test_bot.py
@@ -192,6 +192,7 @@ def test_init(self):
                 cache_settings=cache_settings,
                 http_settings=http_settings,
                 intents=intents,
+                auto_chunk_members=False,
                 logs="DEBUG",
                 max_rate_limit=200,
                 max_retries=0,
@@ -205,7 +206,11 @@ def test_init(self):
         cache.assert_called_once_with(bot, cache_settings)
         assert bot._event_manager is event_manager.return_value
         event_manager.assert_called_once_with(
-            entity_factory.return_value, event_factory.return_value, intents, cache=cache.return_value
+            entity_factory.return_value,
+            event_factory.return_value,
+            intents,
+            auto_chunk_members=False,
+            cache=cache.return_value,
         )
         assert bot._entity_factory is entity_factory.return_value
         entity_factory.assert_called_once_with(bot)
diff --git a/tests/hikari/impl/test_event_manager.py b/tests/hikari/impl/test_event_manager.py
index 426d0eaa..351b3787 100644
--- a/tests/hikari/impl/test_event_manager.py
+++ b/tests/hikari/impl/test_event_manager.py
@@ -438,6 +438,29 @@ async def test_on_guild_create_when_members_declared_and_enabled_for_member_chun
         assert mock_event.chunk_nonce == "123.abc"
         stateless_event_manager_impl.dispatch.assert_awaited_once_with(mock_event)
 
+    @pytest.mark.parametrize("cache_enabled", [True, False])
+    @pytest.mark.parametrize("large", [True, False])
+    @pytest.mark.parametrize("enabled_for_event", [True, False])
+    @pytest.mark.asyncio()
+    async def test_on_guild_create_when_chunk_members_disabled(
+        self,
+        stateless_event_manager_impl,
+        shard,
+        large,
+        cache_enabled,
+        enabled_for_event,
+    ):
+        shard.id = 123
+        stateless_event_manager_impl._intents = intents.Intents.GUILD_MEMBERS
+        stateless_event_manager_impl._cache_enabled_for = mock.Mock(return_value=cache_enabled)
+        stateless_event_manager_impl._enabled_for_event = mock.Mock(return_value=enabled_for_event)
+        stateless_event_manager_impl._auto_chunk_members = False
+
+        with mock.patch.object(event_manager, "_request_guild_members") as request_guild_members:
+            await stateless_event_manager_impl.on_guild_create(shard, {"id": 456, "large": large})
+
+        request_guild_members.assert_not_called()
+
     @pytest.mark.asyncio()
     async def test_on_guild_update_when_stateless(
         self, stateless_event_manager_impl, shard, event_factory, entity_factory
diff --git a/tests/hikari/impl/test_rest.py b/tests/hikari/impl/test_rest.py
index f821e524..21ef655a 100644
--- a/tests/hikari/impl/test_rest.py
+++ b/tests/hikari/impl/test_rest.py
@@ -23,7 +23,6 @@
 import contextlib
 import datetime
 import http
-import re
 import warnings
 
 import mock
@@ -1044,6 +1043,59 @@ def test_unban_member(self, rest_client):
         assert reason is mock_unban_user.return_value
         mock_unban_user.assert_called_once_with(123, 321, reason="ayaya")
 
+    def test_fetch_bans(self, rest_client: rest.RESTClientImpl):
+        with mock.patch.object(special_endpoints, "GuildBanIterator") as iterator_cls:
+            iterator = rest_client.fetch_bans(187, newest_first=True, start_at=StubModel(65652342134))
+
+        iterator_cls.assert_called_once_with(
+            rest_client._entity_factory,
+            rest_client._request,
+            187,
+            True,
+            "65652342134",
+        )
+        assert iterator is iterator_cls.return_value
+
+    def test_fetch_bans_when_datetime_for_start_at(self, rest_client: rest.RESTClientImpl):
+        start_at = datetime.datetime(2022, 3, 6, 12, 1, 58, 415625, tzinfo=datetime.timezone.utc)
+        with mock.patch.object(special_endpoints, "GuildBanIterator") as iterator_cls:
+            iterator = rest_client.fetch_bans(9000, newest_first=True, start_at=start_at)
+
+        iterator_cls.assert_called_once_with(
+            rest_client._entity_factory,
+            rest_client._request,
+            9000,
+            True,
+            "950000286338908160",
+        )
+        assert iterator is iterator_cls.return_value
+
+    def test_fetch_bans_when_start_at_undefined(self, rest_client: rest.RESTClientImpl):
+        with mock.patch.object(special_endpoints, "GuildBanIterator") as iterator_cls:
+            iterator = rest_client.fetch_bans(8844)
+
+        iterator_cls.assert_called_once_with(
+            rest_client._entity_factory,
+            rest_client._request,
+            8844,
+            False,
+            str(snowflakes.Snowflake.min()),
+        )
+        assert iterator is iterator_cls.return_value
+
+    def test_fetch_bans_when_start_at_undefined_and_newest_first(self, rest_client: rest.RESTClientImpl):
+        with mock.patch.object(special_endpoints, "GuildBanIterator") as iterator_cls:
+            iterator = rest_client.fetch_bans(3848, newest_first=True)
+
+        iterator_cls.assert_called_once_with(
+            rest_client._entity_factory,
+            rest_client._request,
+            3848,
+            True,
+            str(snowflakes.Snowflake.max()),
+        )
+        assert iterator is iterator_cls.return_value
+
     def test_command_builder(self, rest_client):
         with warnings.catch_warnings():
             warnings.simplefilter("ignore", category=DeprecationWarning)
@@ -1334,19 +1386,6 @@ def test__build_message_payload_when_both_single_and_plural_args_passed(
         ):
             rest_client._build_message_payload(**{singular_arg: object(), plural_arg: object()})
 
-    @pytest.mark.parametrize(
-        ("singular_arg", "plural_arg"),
-        [("attachment", "attachments"), ("component", "components"), ("embed", "embeds")],
-    )
-    def test__build_message_payload_when_non_collection_passed_to_plural(self, rest_client, singular_arg, plural_arg):
-        expected_error_message = (
-            f"You passed a non-collection to '{plural_arg}', but this expects a collection. Maybe you meant to use "
-            f"'{singular_arg}' (singular) instead?"
-        )
-
-        with pytest.raises(TypeError, match=re.escape(expected_error_message)):
-            rest_client._build_message_payload(**{plural_arg: object()})
-
     def test_interaction_deferred_builder(self, rest_client):
         result = rest_client.interaction_deferred_builder(5)
 
@@ -4208,21 +4247,6 @@ async def test_fetch_ban(self, rest_client):
         rest_client._request.assert_awaited_once_with(expected_route)
         rest_client._entity_factory.deserialize_guild_member_ban.assert_called_once_with({"id": "789"})
 
-    async def test_fetch_bans(self, rest_client):
-        ban1 = StubModel(456)
-        ban2 = StubModel(789)
-        expected_route = routes.GET_GUILD_BANS.compile(guild=123)
-        rest_client._request = mock.AsyncMock(return_value=[{"id": "456"}, {"id": "789"}])
-        rest_client._entity_factory.deserialize_guild_member_ban = mock.Mock(side_effect=[ban1, ban2])
-
-        assert await rest_client.fetch_bans(StubModel(123)) == [ban1, ban2]
-
-        rest_client._request.assert_awaited_once_with(expected_route)
-        assert rest_client._entity_factory.deserialize_guild_member_ban.call_count == 2
-        rest_client._entity_factory.deserialize_guild_member_ban.assert_has_calls(
-            [mock.call({"id": "456"}), mock.call({"id": "789"})]
-        )
-
     async def test_fetch_roles(self, rest_client):
         role1 = StubModel(456)
         role2 = StubModel(789)
diff --git a/tests/hikari/impl/test_special_endpoints.py b/tests/hikari/impl/test_special_endpoints.py
index 9a482727..971dc1b3 100644
--- a/tests/hikari/impl/test_special_endpoints.py
+++ b/tests/hikari/impl/test_special_endpoints.py
@@ -165,6 +165,133 @@ async def test_aiter_when_empty_chunk(self, newest_first: bool):
         mock_request.assert_awaited_once_with(compiled_route=expected_route, query=query)
 
 
+class TestGuildBanIterator:
+    @pytest.mark.asyncio()
+    async def test_aiter(self):
+        expected_route = routes.GET_GUILD_BANS.compile(guild=10000)
+        mock_entity_factory = mock.Mock()
+        mock_payload_1 = {"user": {"id": "45234"}}
+        mock_payload_2 = {"user": {"id": "452745"}}
+        mock_payload_3 = {"user": {"id": "45237656"}}
+        mock_payload_4 = {"user": {"id": "452345666"}}
+        mock_payload_5 = {"user": {"id": "4523456744"}}
+        mock_result_1 = mock.Mock()
+        mock_result_2 = mock.Mock()
+        mock_result_3 = mock.Mock()
+        mock_result_4 = mock.Mock()
+        mock_result_5 = mock.Mock()
+        mock_entity_factory.deserialize_guild_member_ban.side_effect = [
+            mock_result_1,
+            mock_result_2,
+            mock_result_3,
+            mock_result_4,
+            mock_result_5,
+        ]
+        mock_request = mock.AsyncMock(
+            side_effect=[[mock_payload_1, mock_payload_2, mock_payload_3], [mock_payload_4, mock_payload_5], []]
+        )
+        iterator = special_endpoints.GuildBanIterator(
+            entity_factory=mock_entity_factory,
+            request_call=mock_request,
+            guild=10000,
+            newest_first=False,
+            first_id="0",
+        )
+
+        result = await iterator
+
+        assert result == [mock_result_1, mock_result_2, mock_result_3, mock_result_4, mock_result_5]
+        mock_entity_factory.deserialize_guild_member_ban.assert_has_calls(
+            [
+                mock.call(mock_payload_1),
+                mock.call(mock_payload_2),
+                mock.call(mock_payload_3),
+                mock.call(mock_payload_4),
+                mock.call(mock_payload_5),
+            ]
+        )
+        mock_request.assert_has_awaits(
+            [
+                mock.call(compiled_route=expected_route, query={"after": "0", "limit": "1000"}),
+                mock.call(compiled_route=expected_route, query={"after": "45237656", "limit": "1000"}),
+                mock.call(compiled_route=expected_route, query={"after": "4523456744", "limit": "1000"}),
+            ]
+        )
+
+    @pytest.mark.asyncio()
+    async def test_aiter_when_newest_first(self):
+        expected_route = routes.GET_GUILD_BANS.compile(guild=10000)
+        mock_entity_factory = mock.Mock()
+        mock_payload_1 = {"user": {"id": "432234"}}
+        mock_payload_2 = {"user": {"id": "1233211"}}
+        mock_payload_3 = {"user": {"id": "12332112"}}
+        mock_payload_4 = {"user": {"id": "1233"}}
+        mock_payload_5 = {"user": {"id": "54334"}}
+        mock_result_1 = mock.Mock()
+        mock_result_2 = mock.Mock()
+        mock_result_3 = mock.Mock()
+        mock_result_4 = mock.Mock()
+        mock_result_5 = mock.Mock()
+        mock_entity_factory.deserialize_guild_member_ban.side_effect = [
+            mock_result_1,
+            mock_result_2,
+            mock_result_3,
+            mock_result_4,
+            mock_result_5,
+        ]
+        mock_request = mock.AsyncMock(
+            side_effect=[[mock_payload_1, mock_payload_2, mock_payload_3], [mock_payload_4, mock_payload_5], []]
+        )
+        iterator = special_endpoints.GuildBanIterator(
+            entity_factory=mock_entity_factory,
+            request_call=mock_request,
+            guild=10000,
+            newest_first=True,
+            first_id="321123321",
+        )
+
+        result = await iterator
+
+        assert result == [mock_result_1, mock_result_2, mock_result_3, mock_result_4, mock_result_5]
+        mock_entity_factory.deserialize_guild_member_ban.assert_has_calls(
+            [
+                mock.call(mock_payload_3),
+                mock.call(mock_payload_2),
+                mock.call(mock_payload_1),
+                mock.call(mock_payload_5),
+                mock.call(mock_payload_4),
+            ]
+        )
+        mock_request.assert_has_awaits(
+            [
+                mock.call(compiled_route=expected_route, query={"before": "321123321", "limit": "1000"}),
+                mock.call(compiled_route=expected_route, query={"before": "432234", "limit": "1000"}),
+                mock.call(compiled_route=expected_route, query={"before": "1233", "limit": "1000"}),
+            ]
+        )
+
+    @pytest.mark.parametrize("newest_first", [True, False])
+    @pytest.mark.asyncio()
+    async def test_aiter_when_empty_chunk(self, newest_first: bool):
+        expected_route = routes.GET_GUILD_BANS.compile(guild=10000)
+        mock_entity_factory = mock.Mock()
+        mock_request = mock.AsyncMock(return_value=[])
+        iterator = special_endpoints.GuildBanIterator(
+            entity_factory=mock_entity_factory,
+            request_call=mock_request,
+            guild=10000,
+            newest_first=newest_first,
+            first_id="54234123123",
+        )
+
+        result = await iterator
+
+        assert result == []
+        mock_entity_factory.deserialize_guild_member_ban.assert_not_called()
+        query = {"before" if newest_first else "after": "54234123123", "limit": "1000"}
+        mock_request.assert_awaited_once_with(compiled_route=expected_route, query=query)
+
+
 class TestScheduledEventUserIterator:
     @pytest.mark.asyncio()
     async def test_aiter(self):
diff --git a/tests/hikari/internal/test_collections.py b/tests/hikari/internal/test_collections.py
index a6b98026..4b4cdac1 100644
--- a/tests/hikari/internal/test_collections.py
+++ b/tests/hikari/internal/test_collections.py
@@ -80,35 +80,6 @@ def test___setitem__(self):
         assert mock_map == {"hmm": "forearm", "cat": "bag", "ok": "bye", "bye": 4}
 
 
-class TestFrozenDict:
-    def test___init__(self):
-        mock_map = collections._FrozenDict({"foo": (0.432, "bar"), "blam": (0.111, "okok")})
-        assert mock_map == {"foo": "bar", "blam": "okok"}
-
-    def test___getitem__(self):
-        mock_map = collections._FrozenDict({"blam": (0.432, "bar"), "obar": (0.111, "okok")})
-        assert mock_map["obar"] == "okok"
-
-    def test___iter__(self):
-        mock_map = collections._FrozenDict({"bye": (0.33, "bye"), "111": (0.2, "222"), "45949": (0.5, "020202")})
-        assert list(mock_map) == ["bye", "111", "45949"]
-
-    def test___len__(self):
-        mock_map = collections._FrozenDict({"wsw": (0.3, "3"), "fdsa": (0.55, "ewqwe"), "45949": (0.23, "fsasd")})
-        assert len(mock_map) == 3
-
-    def test___delitem__(self):
-        mock_map = collections._FrozenDict({"rororo": (0.55, "bye bye"), "raw": (0.999, "ywywyw")})
-        del mock_map["raw"]
-        assert mock_map == {"rororo": "bye bye"}
-
-    def test___setitem__(self):
-        mock_map = collections._FrozenDict({"rororo": (0.55, "bye 3231"), "2121": (0.999, "4321")})
-        mock_map["foo bar"] = 42
-
-        assert mock_map == {"rororo": "bye 3231", "2121": "4321", "foo bar": 42}
-
-
 class TestLimitedCapacityCacheMap:
     def test___init___with_source(self):
         raw_map = {"voo": "doo", "blam": "blast", "foo": "bye"}
@@ -380,8 +351,3 @@ def test_get_index_or_slice_with_index_outside_range():
 def test_get_index_or_slice_with_slice():
     test_map = {"o": "b", "b": "o", "a": "m", "arara": "blam", "oof": "no", "rika": "may"}
     assert collections.get_index_or_slice(test_map, slice(1, 5, 2)) == ("o", "blam")
-
-
-def test_get_index_or_slice_with_invalid_type():
-    with pytest.raises(TypeError):
-        collections.get_index_or_slice({}, object())
diff --git a/tests/hikari/internal/test_ux.py b/tests/hikari/internal/test_ux.py
index ff544396..fbbcb563 100644
--- a/tests/hikari/internal/test_ux.py
+++ b/tests/hikari/internal/test_ux.py
@@ -184,6 +184,7 @@ def test_when_supports_color(self, mock_args):
         builtins_open = stack.enter_context(mock.patch.object(builtins, "open"))
         abspath = stack.enter_context(mock.patch.object(os.path, "abspath", return_value="some path"))
         dirname = stack.enter_context(mock.patch.object(os.path, "dirname"))
+        fileno = stack.enter_context(mock.patch.object(sys.stdout, "fileno"))
 
         with stack:
             ux.print_banner("hikari", True, False)
@@ -208,7 +209,7 @@ def test_when_supports_color(self, mock_args):
 
         template.assert_called_once_with(read_text())
         template().safe_substitute.assert_called_once_with(args)
-        builtins_open.assert_called_once_with(1, "w", encoding="utf-8")
+        builtins_open.assert_called_once_with(fileno.return_value, "w", encoding="utf-8", closefd=False)
         builtins_open.return_value.__enter__.return_value.write.assert_called_once_with(template().safe_substitute())
         dirname.assert_called_once_with("~/hikari")
         abspath.assert_called_once_with(dirname())
@@ -226,6 +227,7 @@ def test_when_doesnt_supports_color(self, mock_args):
         abspath = stack.enter_context(mock.patch.object(os.path, "abspath", return_value="some path"))
         dirname = stack.enter_context(mock.patch.object(os.path, "dirname"))
         builtins_open = stack.enter_context(mock.patch.object(builtins, "open"))
+        fileno = stack.enter_context(mock.patch.object(sys.stdout, "fileno"))
 
         with stack:
             ux.print_banner("hikari", True, False)
@@ -253,7 +255,7 @@ def test_when_doesnt_supports_color(self, mock_args):
         dirname.assert_called_once_with("~/hikari")
         abspath.assert_called_once_with(dirname())
         supports_color.assert_called_once_with(True, False)
-        builtins_open.assert_called_once_with(1, "w", encoding="utf-8")
+        builtins_open.assert_called_once_with(fileno.return_value, "w", encoding="utf-8", closefd=False)
         builtins_open.return_value.__enter__.return_value.write.assert_called_once_with(template().safe_substitute())
 
     def test_use_extra_args(self, mock_args):
@@ -264,6 +266,7 @@ def test_use_extra_args(self, mock_args):
         template = stack.enter_context(mock.patch.object(string, "Template"))
         builtins_open = stack.enter_context(mock.patch.object(builtins, "open"))
         stack.enter_context(mock.patch.object(os.path, "abspath", return_value="some path"))
+        fileno = stack.enter_context(mock.patch.object(sys.stdout, "fileno"))
 
         extra_args = {
             "extra_argument_1": "one",
@@ -292,7 +295,7 @@ def test_use_extra_args(self, mock_args):
 
         template.assert_called_once_with(read_text())
         template().safe_substitute.assert_called_once_with(args)
-        builtins_open.assert_called_once_with(1, "w", encoding="utf-8")
+        builtins_open.assert_called_once_with(fileno.return_value, "w", encoding="utf-8", closefd=False)
         builtins_open.return_value.__enter__.return_value.write.assert_called_once_with(template().safe_substitute())
 
     def test_overwrite_args_raises_error(self, mock_args):
diff --git a/tests/hikari/test_audit_logs.py b/tests/hikari/test_audit_logs.py
index b76c44e7..b14ae126 100644
--- a/tests/hikari/test_audit_logs.py
+++ b/tests/hikari/test_audit_logs.py
@@ -158,15 +158,6 @@ def test_get_item_with_slice(self):
         )
         assert audit_log[1:5:2] == (entry_1, entry_2)
 
-    def test_get_item_with_ivalid_type(self):
-        with pytest.raises(TypeError):
-            audit_logs.AuditLog(
-                entries=[object(), object()],
-                integrations={},
-                users={},
-                webhooks={},
-            )["OK"]
-
     def test_len(self):
         audit_log = audit_logs.AuditLog(
             entries={
diff --git a/tests/hikari/test_colors.py b/tests/hikari/test_colors.py
index 2a99976c..1f178fb5 100644
--- a/tests/hikari/test_colors.py
+++ b/tests/hikari/test_colors.py
@@ -276,7 +276,7 @@ def test_Color_to_bytes(self):
         [
             (colors.Color(0xFF051A), colors.Color(0xFF051A)),
             (0xFF051A, colors.Color(0xFF051A)),
-            ((1, 0.5, 0), colors.Color(0xFF7F00)),
+            ((1.0, 0.5, 0.0), colors.Color(0xFF7F00)),
             ([0xFF, 0x5, 0x1A], colors.Color(0xFF051A)),
             ("#1a2b3c", colors.Color(0x1A2B3C)),
             ("#123", colors.Color(0x112233)),
@@ -305,9 +305,9 @@ def test_Color_of_happy_path(self, input, expected_result):
             (NotImplemented, r"Could not transform NotImplemented into a Color object"),
             ((1, 1, 1, 1), r"Color must be an RGB triplet if set to a tuple type"),
             ((1, "a", 1), r"Could not transform \(1, 'a', 1\) into a Color object"),
-            ((1.1, 1, 1), r"Expected red channel to be in the inclusive range of 0.0 and 1.0"),
-            ((1, 1.1, 1), r"Expected green channel to be in the inclusive range of 0.0 and 1.0"),
-            ((1, 1, 1.1), r"Expected blue channel to be in the inclusive range of 0.0 and 1.0"),
+            ((1.1, 1.0, 1.0), r"Expected red channel to be in the inclusive range of 0.0 and 1.0"),
+            ((1.0, 1.1, 1.0), r"Expected green channel to be in the inclusive range of 0.0 and 1.0"),
+            ((1.0, 1.0, 1.1), r"Expected blue channel to be in the inclusive range of 0.0 and 1.0"),
             ((), r"Color must be an RGB triplet if set to a tuple type"),
             ({}, r"Could not transform \{\} into a Color object"),
             ([], r"Color must be an RGB triplet if set to a list type"),