Repository: hikari-py/hikari
Pull request #1273 (2022-08-27 21:00:59)

Remove support for ProcessPoolExecutor executor when reading files
 - Also cleans up code for reading using ThreadPool executors
 - It is more efficient to use a threadpool executor and it makes no sense to use anything else
   - Due to the nature of process pool, were were also not able to perform proper chunking when reading off the file

### 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.



Patch:
diff --git a/changes/1273.breaking.md b/changes/1273.breaking.md
new file mode 100644
index 00000000..ae6f812a
--- /dev/null
+++ b/changes/1273.breaking.md
@@ -0,0 +1,3 @@
+Remove support for ProcessPoolExecutor executor when reading files
+- It is much more efficient to use a threadpool executor for I/O actions like this one
+  - Due to the nature of process pool, we were also not able to perform proper chunking when reading off the file
diff --git a/changes/1284.bugfix.md b/changes/1284.bugfix.md
new file mode 100644
index 00000000..6b8986de
--- /dev/null
+++ b/changes/1284.bugfix.md
@@ -0,0 +1,1 @@
+Ignore guild create events which contain unavailable guilds
diff --git a/dev-requirements/flake8.txt b/dev-requirements/flake8.txt
index d375c105..8867e258 100644
--- a/dev-requirements/flake8.txt
+++ b/dev-requirements/flake8.txt
@@ -1,9 +1,9 @@
-flake8==4.0.1
+flake8==5.0.4
 
 # Plugins
 # Ref: https://github.com/DmytroLitvinov/awesome-flake8-extensions
 
-flake8-bandit~=3.0.0                    # runs bandit
+flake8-bandit~=4.1.1                    # runs bandit
 flake8-black==0.3.3                     # runs black
 flake8-broken-line==0.5.0               # forbey "\" linebreaks
 flake8-builtins==1.5.3                  # builtin shadowing checks
@@ -15,7 +15,6 @@ flake8-executable==2.1.1                # shebangs
 flake8-fixme==1.1.1                     # "fix me" counter
 flake8-functions==0.0.7                 # function linting
 flake8-html==0.4.2                      # html output
-flake8-if-statements==0.1.0             # condition linting
 flake8-isort==4.2.0                     # runs isort
 flake8-mutable==1.2.0                   # mutable default argument detection
 flake8-pep3101==1.3.0                   # new-style format strings only
diff --git a/dev-requirements/formatting.txt b/dev-requirements/formatting.txt
index 354a2a0d..ddf7fd7c 100644
--- a/dev-requirements/formatting.txt
+++ b/dev-requirements/formatting.txt
@@ -1,2 +1,2 @@
-black==22.6.0
+black==22.8.0
 isort==5.10.1
diff --git a/dev-requirements/pyright.txt b/dev-requirements/pyright.txt
index 1657465d..cabc77c7 100644
--- a/dev-requirements/pyright.txt
+++ b/dev-requirements/pyright.txt
@@ -1,1 +1,1 @@
-pyright==1.1.268
+pyright==1.1.271
diff --git a/dev-requirements/safety.txt b/dev-requirements/safety.txt
index c4d6f390..de1f816a 100644
--- a/dev-requirements/safety.txt
+++ b/dev-requirements/safety.txt
@@ -1,4 +1,4 @@
-safety~=2.1.1
+safety~=2.2.0
 
 # Temporary addition to avoid safety erroring due to https://github.com/pypa/pip/pull/9827
 pip>=21.1
diff --git a/dev-requirements/towncrier.txt b/dev-requirements/towncrier.txt
index 6c31b0cf..6928c85b 100644
--- a/dev-requirements/towncrier.txt
+++ b/dev-requirements/towncrier.txt
@@ -1,1 +1,1 @@
-towncrier==21.9.0
+towncrier==22.8.0
diff --git a/hikari/commands.py b/hikari/commands.py
index 2909d6f2..2684a470 100644
--- a/hikari/commands.py
+++ b/hikari/commands.py
@@ -489,7 +489,7 @@ class SlashCommand(PartialCommand):
 @attr_extensions.with_copy
 @attr.define(hash=True, kw_only=True, weakref_slot=False)
 class ContextMenuCommand(PartialCommand):
-    """Represents a slash command on Discord."""
+    """Represents a context menu command on Discord."""
 
 
 class CommandPermissionType(int, enums.Enum):
diff --git a/hikari/files.py b/hikari/files.py
index 80b896e8..aec1d811 100644
--- a/hikari/files.py
+++ b/hikari/files.py
@@ -36,7 +36,6 @@
     "AsyncReaderContextManager",
     "Resource",
     "File",
-    "FileReader",
     "WebResource",
     "URL",
     "WebReader",
@@ -48,13 +47,11 @@
 import asyncio
 import base64
 import concurrent.futures
-import errno
 import inspect
 import io
 import mimetypes
 import os
 import pathlib
-import stat
 import typing
 import urllib.parse
 import urllib.request
@@ -757,71 +754,7 @@ def filename(self) -> str:
 
 
 @attr.define(weakref_slot=False)
-class FileReader(AsyncReader, abc.ABC):
-    """Abstract base for a file reader object.
-
-    Various implementations have to exist in order to cater for situations
-    where we cannot pass IO objects around (e.g. ProcessPoolExecutors, since
-    they pickle things).
-    """
-
-    executor: typing.Optional[concurrent.futures.Executor] = attr.field()
-    """The associated `concurrent.futures.Executor` to use for blocking IO."""
-
-    path: pathlib.Path = attr.field(converter=ensure_path)
-    """The path to the resource to read."""
-
-
-def _stat(path: pathlib.Path) -> pathlib.Path:
-    # While paths will be implicitly resolved, we still need to explicitly
-    # call expanduser to deal with a ~ base.
-    try:
-        path = path.expanduser()
-    except RuntimeError:
-        pass  # A home directory couldn't be resolved, so we'll just use the path as-is.
-
-    # path.stat() will raise FileNotFoundError if the file doesn't exist
-    # (unlike is_dir) which is what we want here.
-    if stat.S_ISDIR(path.stat().st_mode):
-        raise IsADirectoryError(errno.EISDIR, "Cannot open the path specified as it is a directory", str(path))
-
-    return path
-
-
-@attr.define(weakref_slot=False)
-@typing.final
-class _ThreadedFileReaderContextManagerImpl(AsyncReaderContextManager[FileReader]):
-    executor: typing.Optional[concurrent.futures.ThreadPoolExecutor] = attr.field()
-    file: typing.Optional[typing.BinaryIO] = attr.field(default=None, init=False)
-    filename: str = attr.field()
-    path: pathlib.Path = attr.field()
-
-    async def __aenter__(self) -> ThreadedFileReader:
-        if self.file:
-            raise RuntimeError("File is already open")
-
-        loop = asyncio.get_running_loop()
-        self.path = await loop.run_in_executor(self.executor, _stat, self.path)
-        self.file = typing.cast(io.BufferedReader, await loop.run_in_executor(self.executor, self.path.open, "rb"))
-        return ThreadedFileReader(self.filename, None, self.executor, self.path, self.file)
-
-    async def __aexit__(
-        self,
-        exc_type: typing.Optional[typing.Type[BaseException]],
-        exc: typing.Optional[BaseException],
-        exc_tb: typing.Optional[types.TracebackType],
-    ) -> None:
-        if not self.file:
-            raise RuntimeError("File isn't open")
-
-        loop = asyncio.get_running_loop()
-        file = self.file
-        self.file = None
-        await loop.run_in_executor(self.executor, file.close)
-
-
-@attr.define(weakref_slot=False)
-class ThreadedFileReader(FileReader):
+class ThreadedFileReader(AsyncReader):
     """Asynchronous file reader that reads a resource from local storage.
 
     This implementation works with pools that exist in the same interpreter
@@ -829,31 +762,39 @@ class ThreadedFileReader(FileReader):
     do not need to be pickled to be communicated.
     """
 
+    _executor: typing.Optional[concurrent.futures.ThreadPoolExecutor] = attr.field()
     _pointer: typing.BinaryIO = attr.field()
 
     async def __aiter__(self) -> typing.AsyncGenerator[typing.Any, bytes]:
         loop = asyncio.get_running_loop()
 
         while True:
-            chunk = await loop.run_in_executor(self.executor, self._pointer.read, _MAGIC)
+            chunk = await loop.run_in_executor(self._executor, self._pointer.read, _MAGIC)
             yield chunk
             if len(chunk) < _MAGIC:
                 break
 
 
+def _open_path(path: pathlib.Path) -> typing.BinaryIO:
+    return path.expanduser().open("rb")
+
+
 @attr.define(weakref_slot=False)
 @typing.final
-class _MultiProcessingFileReaderContextManagerImpl(AsyncReaderContextManager[FileReader]):
-    executor: concurrent.futures.ProcessPoolExecutor = attr.field()
+class _ThreadedFileReaderContextManagerImpl(AsyncReaderContextManager[ThreadedFileReader]):
+    executor: typing.Optional[concurrent.futures.ThreadPoolExecutor] = attr.field()
     file: typing.Optional[typing.BinaryIO] = attr.field(default=None, init=False)
     filename: str = attr.field()
     path: pathlib.Path = attr.field()
 
-    async def __aenter__(self) -> MultiprocessingFileReader:
-        loop = asyncio.get_running_loop()
+    async def __aenter__(self) -> ThreadedFileReader:
+        if self.file:
+            raise RuntimeError("File is already open")
 
-        path = await loop.run_in_executor(self.executor, _stat, self.path)
-        return MultiprocessingFileReader(self.filename, None, self.executor, path)
+        loop = asyncio.get_running_loop()
+        file = await loop.run_in_executor(self.executor, _open_path, self.path)
+        self.file = file
+        return ThreadedFileReader(self.filename, None, self.executor, file)
 
     async def __aexit__(
         self,
@@ -861,38 +802,15 @@ async def __aexit__(
         exc: typing.Optional[BaseException],
         exc_tb: typing.Optional[types.TracebackType],
     ) -> None:
-        pass
-
-
-def _read_all(path: pathlib.Path) -> bytes:
-    with path.open("rb") as file:
-        return file.read()
-
-
-@attr.define(slots=False, weakref_slot=False)  # Do not slot (pickle)
-class MultiprocessingFileReader(FileReader):
-    """Asynchronous file reader that reads a resource from local storage.
-
-    This implementation works with pools that exist in a different interpreter
-    instance to the caller. Currently this only includes ProcessPoolExecutors
-    and custom implementations where objects have to be pickled to be used
-    by the pool.
-    """
-
-    async def __aiter__(self) -> typing.AsyncGenerator[typing.Any, bytes]:
-        yield await asyncio.get_running_loop().run_in_executor(self.executor, _read_all, self.path)
-
-    def __getstate__(self) -> typing.Dict[str, typing.Any]:
-        return {"path": self.path, "filename": self.filename}
+        if not self.file:
+            raise RuntimeError("File isn't open")
 
-    def __setstate__(self, state: typing.Dict[str, typing.Any]) -> None:
-        self.path: pathlib.Path = state["path"]
-        self.filename: str = state["filename"]
-        self.executor: typing.Optional[concurrent.futures.Executor] = None
-        self.mimetype: typing.Optional[str] = None
+        loop = asyncio.get_running_loop()
+        await loop.run_in_executor(self.executor, self.file.close)
+        self.file = None
 
 
-class File(Resource[FileReader]):
+class File(Resource[ThreadedFileReader]):
     """A resource that exists on the local machine's storage to be uploaded.
 
     Parameters
@@ -948,21 +866,23 @@ def stream(
         *,
         executor: typing.Optional[concurrent.futures.Executor] = None,
         head_only: bool = False,
-    ) -> AsyncReaderContextManager[FileReader]:
+    ) -> AsyncReaderContextManager[ThreadedFileReader]:
         """Start streaming the resource using a thread pool executor.
 
         Parameters
         ----------
         executor : typing.Optional[concurrent.futures.Executor]
-            The executor to run the blocking read operations in. If
+            The thread executor to run the blocking read operations in. If
             `builtins.None`, the default executor for the running event loop
             will be used instead.
+
+            Only `concurrent.futures.TheadPoolExecutor` is supported.
         head_only : builtins.bool
             Not used. Provided only to match the underlying interface.
 
         Returns
         -------
-        AsyncReaderContextManager[FileReader]
+        AsyncReaderContextManager[ThreadedFileReader]
             An async context manager that when entered, produces the
             data stream.
 
@@ -973,15 +893,12 @@ def stream(
         FileNotFoundError
             If the file doesn't exist.
         """
-        # asyncio forces the default executor when this is None to always be a thread pool executor anyway,
-        # so this is safe enough to do.:
         if executor is None or isinstance(executor, concurrent.futures.ThreadPoolExecutor):
+            # asyncio forces the default executor when this is None to always be a thread pool executor anyway,
+            # so this is safe enough to do:
             return _ThreadedFileReaderContextManagerImpl(executor, self.filename, self.path)
 
-        if not isinstance(executor, concurrent.futures.ProcessPoolExecutor):
-            raise TypeError("The executor must be a ProcessPoolExecutor, ThreadPoolExecutor, or `builtins.None`.")
-
-        return _MultiProcessingFileReaderContextManagerImpl(executor, self.filename, self.path)
+        raise TypeError("The executor must be a ThreadPoolExecutor or None")
 
 
 ########################################################################
diff --git a/hikari/impl/event_manager.py b/hikari/impl/event_manager.py
index 0af15177..4e159f85 100644
--- a/hikari/impl/event_manager.py
+++ b/hikari/impl/event_manager.py
@@ -180,9 +180,17 @@ async def on_guild_create(  # noqa: C901 - Function too complex
         """See https://discord.com/developers/docs/topics/gateway#guild-create for more info."""
         event: typing.Union[guild_events.GuildAvailableEvent, guild_events.GuildJoinEvent, None]
 
-        if "unavailable" in payload and self._enabled_for_event(guild_events.GuildAvailableEvent):
+        unavailable = payload.get("unavailable")
+
+        if unavailable:
+            # It is possible for GUILD_CREATE to contain unavailable guilds during outages
+            # In cases like this, we can just ignore the event and wait for the outage to
+            # be resolved, where the correct guild visibility event will be dispatched
+            return
+
+        if unavailable is not None and self._enabled_for_event(guild_events.GuildAvailableEvent):
             event = self._event_factory.deserialize_guild_available_event(shard, payload)
-        elif "unavailable" not in payload and self._enabled_for_event(guild_events.GuildJoinEvent):
+        elif unavailable is None and self._enabled_for_event(guild_events.GuildJoinEvent):
             event = self._event_factory.deserialize_guild_join_event(shard, payload)
         else:
             event = None
diff --git a/hikari/impl/special_endpoints.py b/hikari/impl/special_endpoints.py
index b4d0c3cc..6cb801b2 100644
--- a/hikari/impl/special_endpoints.py
+++ b/hikari/impl/special_endpoints.py
@@ -1258,6 +1258,7 @@ async def create(
             self._type,
             self._name,
             guild=guild,
+            name_localizations=self._name_localizations,
             default_member_permissions=self._default_member_permissions,
             dm_enabled=self._is_dm_enabled,
         )
diff --git a/pyproject.toml b/pyproject.toml
index 33c33f6b..3cc73903 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -156,6 +156,5 @@ require-subclass = true
 exclude-classes = """
 (
     ^hikari\\.internal\\.enums:(Enum|Flag|_EnumMeta|_FlagMeta)$
-    |^hikari\\.files:MultiprocessingFileReader$
 )
 """
diff --git a/requirements.txt b/requirements.txt
index 82d4e349..8345feba 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
 aiohttp~=3.8
 attrs~=22.1
-colorlog~=6.6
+colorlog~=6.7
 multidict~=6.0


Test patch:
diff --git a/dev-requirements/pytest.txt b/dev-requirements/pytest.txt
index fff06958..015cb30b 100644
--- a/dev-requirements/pytest.txt
+++ b/dev-requirements/pytest.txt
@@ -1,7 +1,7 @@
 # mock in the stdlib changes between versions of Python, so are not consistent in behaviour like the backport is
 mock==4.0.3
 
-pytest==7.1.2
+pytest==7.1.3
 pytest-asyncio==0.19.0
 pytest-cov==3.0.0
 pytest-randomly==3.12.0
diff --git a/tests/hikari/impl/test_event_manager.py b/tests/hikari/impl/test_event_manager.py
index 1374433b..50839974 100644
--- a/tests/hikari/impl/test_event_manager.py
+++ b/tests/hikari/impl/test_event_manager.py
@@ -246,12 +246,46 @@ async def test_on_channel_pins_update(self, stateless_event_manager_impl, shard,
             event_factory.deserialize_channel_pins_update_event.return_value
         )
 
-    @pytest.mark.parametrize("unavailable", [True, False])
+    @pytest.mark.asyncio()
+    async def test_on_guild_create_when_unavailable_guild(
+        self, event_manager_impl, shard, event_factory, entity_factory
+    ):
+        payload = {"unavailable": True}
+        event_manager_impl._cache_enabled_for = mock.Mock(return_value=True)
+        event_manager_impl._enabled_for_event = mock.Mock(return_value=True)
+
+        with mock.patch.object(event_manager, "_request_guild_members") as request_guild_members:
+            await event_manager_impl.on_guild_create(shard, payload)
+
+        event_manager_impl._enabled_for_event.assert_not_called()
+        event_factory.deserialize_guild_available_event.assert_not_called()
+        event_factory.deserialize_guild_join_event.assert_not_called()
+
+        event_manager_impl._cache.update_guild.assert_not_called()
+        event_manager_impl._cache.clear_guild_channels_for_guild.assert_not_called()
+        event_manager_impl._cache.set_guild_channel.assert_not_called()
+        event_manager_impl._cache.clear_emojis_for_guild.assert_not_called()
+        event_manager_impl._cache.set_emoji.assert_not_called()
+        event_manager_impl._cache.clear_stickers_for_guild.assert_not_called()
+        event_manager_impl._cache.set_sticker.assert_not_called()
+        event_manager_impl._cache.clear_roles_for_guild.assert_not_called()
+        event_manager_impl._cache.set_role.assert_not_called()
+        event_manager_impl._cache.clear_members_for_guild.assert_not_called()
+        event_manager_impl._cache.set_member.assert_not_called()
+        event_manager_impl._cache.clear_presences_for_guild.assert_not_called()
+        event_manager_impl._cache.set_presence.assert_not_called()
+        event_manager_impl._cache.clear_voice_states_for_guild.assert_not_called()
+        event_manager_impl._cache.set_voice_state.assert_not_called()
+        request_guild_members.assert_not_called()
+
+        event_manager_impl.dispatch.assert_not_called()
+
+    @pytest.mark.parametrize("include_unavailable", [True, False])
     @pytest.mark.asyncio()
     async def test_on_guild_create_when_dispatching_and_not_caching(
-        self, event_manager_impl, shard, event_factory, entity_factory, unavailable
+        self, event_manager_impl, shard, event_factory, entity_factory, include_unavailable
     ):
-        payload = {"unavailable": False} if unavailable else {}
+        payload = {"unavailable": False} if include_unavailable else {}
         event_manager_impl._intents = intents.Intents.NONE
         event_manager_impl._cache_enabled_for = mock.Mock(return_value=False)
         event_manager_impl._enabled_for_event = mock.Mock(return_value=True)
@@ -259,7 +293,7 @@ async def test_on_guild_create_when_dispatching_and_not_caching(
         with mock.patch.object(event_manager, "_request_guild_members") as request_guild_members:
             await event_manager_impl.on_guild_create(shard, payload)
 
-        if unavailable:
+        if include_unavailable:
             event_manager_impl._enabled_for_event.assert_called_once_with(guild_events.GuildAvailableEvent)
             event_factory.deserialize_guild_available_event.assert_called_once_with(shard, payload)
             event = event_factory.deserialize_guild_available_event.return_value
@@ -287,12 +321,12 @@ async def test_on_guild_create_when_dispatching_and_not_caching(
 
         event_manager_impl.dispatch.assert_awaited_once_with(event)
 
-    @pytest.mark.parametrize("unavailable", [True, False])
+    @pytest.mark.parametrize("include_unavailable", [True, False])
     @pytest.mark.asyncio()
     async def test_on_guild_create_when_not_dispatching_and_not_caching(
-        self, event_manager_impl, shard, event_factory, entity_factory, unavailable
+        self, event_manager_impl, shard, event_factory, entity_factory, include_unavailable
     ):
-        payload = {"unavailable": False} if unavailable else {}
+        payload = {"unavailable": False} if include_unavailable else {}
         event_manager_impl._intents = intents.Intents.NONE
         event_manager_impl._cache_enabled_for = mock.Mock(return_value=False)
         event_manager_impl._enabled_for_event = mock.Mock(return_value=False)
@@ -300,7 +334,7 @@ async def test_on_guild_create_when_not_dispatching_and_not_caching(
         with mock.patch.object(event_manager, "_request_guild_members") as request_guild_members:
             await event_manager_impl.on_guild_create(shard, payload)
 
-        if unavailable:
+        if include_unavailable:
             event_manager_impl._enabled_for_event.assert_called_once_with(guild_events.GuildAvailableEvent)
         else:
             event_manager_impl._enabled_for_event.assert_called_once_with(guild_events.GuildJoinEvent)
@@ -327,12 +361,12 @@ async def test_on_guild_create_when_not_dispatching_and_not_caching(
 
         event_manager_impl.dispatch.assert_not_called()
 
-    @pytest.mark.parametrize("unavailable", [True, False])
+    @pytest.mark.parametrize("include_unavailable", [True, False])
     @pytest.mark.asyncio()
     async def test_on_guild_create_when_not_dispatching_and_caching(
-        self, event_manager_impl, shard, event_factory, entity_factory, unavailable
+        self, event_manager_impl, shard, event_factory, entity_factory, include_unavailable
     ):
-        payload = {"unavailable": False} if unavailable else {}
+        payload = {"unavailable": False} if include_unavailable else {}
         event_manager_impl._intents = intents.Intents.NONE
         event_manager_impl._cache_enabled_for = mock.Mock(return_value=True)
         event_manager_impl._enabled_for_event = mock.Mock(return_value=False)
@@ -348,7 +382,7 @@ async def test_on_guild_create_when_not_dispatching_and_caching(
         with mock.patch.object(event_manager, "_request_guild_members") as request_guild_members:
             await event_manager_impl.on_guild_create(shard, payload)
 
-        if unavailable:
+        if include_unavailable:
             event_manager_impl._enabled_for_event.assert_called_once_with(guild_events.GuildAvailableEvent)
         else:
             event_manager_impl._enabled_for_event.assert_called_once_with(guild_events.GuildJoinEvent)
@@ -375,13 +409,13 @@ async def test_on_guild_create_when_not_dispatching_and_caching(
 
         event_manager_impl.dispatch.assert_not_called()
 
-    @pytest.mark.parametrize("unavailable", [True, False])
+    @pytest.mark.parametrize("include_unavailable", [True, False])
     @pytest.mark.asyncio()
     async def test_on_guild_create_when_stateless(
-        self, stateless_event_manager_impl, shard, event_factory, entity_factory, unavailable
+        self, stateless_event_manager_impl, shard, event_factory, entity_factory, include_unavailable
     ):
         payload = {"id": 123}
-        if unavailable:
+        if include_unavailable:
             payload["unavailable"] = False
 
         stateless_event_manager_impl._intents = intents.Intents.NONE
@@ -391,7 +425,7 @@ async def test_on_guild_create_when_stateless(
         with mock.patch.object(event_manager, "_request_guild_members") as request_guild_members:
             await stateless_event_manager_impl.on_guild_create(shard, payload)
 
-        if unavailable:
+        if include_unavailable:
             stateless_event_manager_impl._enabled_for_event.assert_called_once_with(guild_events.GuildAvailableEvent)
         else:
             stateless_event_manager_impl._enabled_for_event.assert_called_once_with(guild_events.GuildJoinEvent)
diff --git a/tests/hikari/impl/test_special_endpoints.py b/tests/hikari/impl/test_special_endpoints.py
index f9d8fadb..c1e1e1bc 100644
--- a/tests/hikari/impl/test_special_endpoints.py
+++ b/tests/hikari/impl/test_special_endpoints.py
@@ -808,6 +808,7 @@ async def test_create(self):
         builder = (
             special_endpoints.ContextMenuCommandBuilder(commands.CommandType.USER, "we are number")
             .set_default_member_permissions(permissions.Permissions.BAN_MEMBERS)
+            .set_name_localizations({"meow": "nyan"})
             .set_is_dm_enabled(True)
         )
         mock_rest = mock.AsyncMock()
@@ -821,6 +822,7 @@ async def test_create(self):
             builder.name,
             guild=undefined.UNDEFINED,
             default_member_permissions=permissions.Permissions.BAN_MEMBERS,
+            name_localizations={"meow": "nyan"},
             dm_enabled=True,
         )
 
@@ -829,6 +831,7 @@ async def test_create_with_guild(self):
         builder = (
             special_endpoints.ContextMenuCommandBuilder(commands.CommandType.USER, "we are number")
             .set_default_member_permissions(permissions.Permissions.BAN_MEMBERS)
+            .set_name_localizations({"en-ghibli": "meow"})
             .set_is_dm_enabled(True)
         )
         mock_rest = mock.AsyncMock()
@@ -842,6 +845,7 @@ async def test_create_with_guild(self):
             builder.name,
             guild=765234123,
             default_member_permissions=permissions.Permissions.BAN_MEMBERS,
+            name_localizations={"en-ghibli": "meow"},
             dm_enabled=True,
         )
 
diff --git a/tests/hikari/test_files.py b/tests/hikari/test_files.py
index 380db057..4660ae1c 100644
--- a/tests/hikari/test_files.py
+++ b/tests/hikari/test_files.py
@@ -20,11 +20,8 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
-import base64
-import concurrent.futures
+import asyncio
 import pathlib
-import random
-import tempfile
 
 import mock
 import pytest
@@ -50,7 +47,16 @@ def test___exit__(self, reader):
             pytest.fail(exc)
 
 
-class Test_ThreadedFileReaderContextManagerImpl:
+def test__open_path():
+    expanded_path = mock.Mock()
+    path = mock.Mock(expanduser=mock.Mock(return_value=expanded_path))
+
+    assert files._open_path(path) is expanded_path.open.return_value
+
+    expanded_path.open.assert_called_once_with("rb")
+
+
+class TestThreadedFileReaderContextManagerImpl:
     @pytest.mark.asyncio()
     async def test_enter_dunder_method_when_already_open(self):
         manager = files._ThreadedFileReaderContextManagerImpl(mock.Mock(), "ea", pathlib.Path("ea"))
@@ -67,138 +73,23 @@ async def test_exit_dunder_method_when_not_open(self):
 
     @pytest.mark.asyncio()
     async def test_context_manager(self):
-        executor = concurrent.futures.ThreadPoolExecutor()
-        mock_data = b"meeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" * 50
+        mock_file = mock.Mock()
+        executor = object()
+        path = pathlib.Path("test/path/")
 
-        # A try, finally is used to delete the file rather than relying on delete=True behaviour
-        # as on Windows the file cannot be accessed by other processes if delete is True.
-        file = tempfile.NamedTemporaryFile("wb", delete=False)
-        path = pathlib.Path(file.name)
-        try:
-            with file:
-                file.write(mock_data)
+        loop = mock.Mock(run_in_executor=mock.AsyncMock(side_effect=[mock_file, None]))
 
-            context_manager = files._ThreadedFileReaderContextManagerImpl(executor, "meow.txt", path)
+        context_manager = files._ThreadedFileReaderContextManagerImpl(executor, "meow.txt", path)
 
+        with mock.patch.object(asyncio, "get_running_loop", return_value=loop):
             async with context_manager as reader:
-                data = await reader.read()
+                assert context_manager.file is mock_file
 
                 assert reader.filename == "meow.txt"
-                assert reader.path == path
-                assert reader.executor is executor
-                assert data == mock_data
-
-        finally:
-            path.unlink()
-
-    @mock.patch.object(pathlib.Path, "expanduser", side_effect=RuntimeError)
-    @pytest.mark.asyncio()
-    async def test_context_manager_when_expandname_raises_runtime_error(self, expanduser: mock.Mock):
-        # We can't mock patch stuff in other processes easily (if at all) so
-        # for this test we have to cheat and use a thread pool executor.
-        executor = concurrent.futures.ThreadPoolExecutor()
-
-        # A try, finally is used to delete the file rather than relying on delete=True behaviour
-        # as on Windows the file cannot be accessed by other processes if delete is True.
-        with tempfile.NamedTemporaryFile(delete=False) as file:
-            pass
-
-        path = pathlib.Path(file.name)
-        try:
-            context_manager = files._ThreadedFileReaderContextManagerImpl(executor, "filename.txt", path)
-
-            async with context_manager as reader:
-                assert reader.path == path
-
-            expanduser.assert_called_once_with()
-
-        finally:
-            path.unlink()
-
-    @pytest.mark.asyncio()
-    async def test_context_manager_for_unknown_file(self):
-        executor = concurrent.futures.ThreadPoolExecutor()
-        path = pathlib.Path(base64.urlsafe_b64encode(random.getrandbits(512).to_bytes(64, "little")).decode())
-        context_manager = files._ThreadedFileReaderContextManagerImpl(executor, "ea.txt", path)
-
-        with pytest.raises(FileNotFoundError):  # noqa:  PT012 - raises block should contain a single statement
-            async with context_manager:
-                ...
-
-    @pytest.mark.asyncio()
-    async def test_test_context_manager_when_target_is_dir(self):
-        executor = concurrent.futures.ThreadPoolExecutor()
-
-        with tempfile.TemporaryDirectory() as name:
-            path = pathlib.Path(name)
-            context_manager = files._ThreadedFileReaderContextManagerImpl(executor, "meow.txt", path)
-
-            with pytest.raises(IsADirectoryError):  # noqa:  PT012 - raises block should contain a single statement
-                async with context_manager:
-                    ...
-
-
-class Test_MultiProcessingFileReaderContextManagerImpl:
-    @pytest.mark.asyncio()
-    async def test_context_manager(self):
-        executor = concurrent.futures.ProcessPoolExecutor()
-        mock_data = b"kon'nichiwa i am yellow and blue da be meow da bayeet" * 50
-
-        # A try, finally is used to delete the file rather than relying on delete=True behaviour
-        # as on Windows the file cannot be accessed by other processes if delete is True.
-        file = tempfile.NamedTemporaryFile("wb", delete=False)
-        path = pathlib.Path(file.name)
-        try:
-            with file:
-                file.write(mock_data)
-
-            context_manager = files._MultiProcessingFileReaderContextManagerImpl(executor, "filename.txt", path)
-
-            async with context_manager as reader:
-                data = await reader.read()
-
-                assert reader.filename == "filename.txt"
-                assert reader.path == path
-                assert reader.executor is executor
-                assert data == mock_data
-
-        finally:
-            path.unlink()
-
-    @mock.patch.object(pathlib.Path, "expanduser", side_effect=RuntimeError)
-    @pytest.mark.asyncio()
-    async def test_context_manager_when_expandname_raises_runtime_error(self, expanduser: mock.Mock):
-        # We can't mock patch stuff in other processes easily (if at all) so
-        # for this test we have to cheat and use a thread pool executor.
-        executor = concurrent.futures.ThreadPoolExecutor()
-
-        with tempfile.NamedTemporaryFile() as file:
-            path = pathlib.Path(file.name)
-            context_manager = files._MultiProcessingFileReaderContextManagerImpl(executor, "filename.txt", path)
-
-            async with context_manager as reader:
-                assert reader.path == path
-
-        expanduser.assert_called_once_with()
-
-    @pytest.mark.asyncio()
-    async def test_context_manager_for_unknown_file(self):
-        executor = concurrent.futures.ProcessPoolExecutor()
-        path = pathlib.Path(base64.urlsafe_b64encode(random.getrandbits(512).to_bytes(64, "little")).decode())
-        context_manager = files._MultiProcessingFileReaderContextManagerImpl(executor, "ea.txt", path)
-
-        with pytest.raises(FileNotFoundError):  # noqa:  PT012 - raises block should contain a single statement
-            async with context_manager:
-                ...
-
-    @pytest.mark.asyncio()
-    async def test_test_context_manager_when_target_is_dir(self):
-        executor = concurrent.futures.ProcessPoolExecutor()
+                assert reader._executor is executor
 
-        with tempfile.TemporaryDirectory() as name:
-            path = pathlib.Path(name)
-            context_manager = files._MultiProcessingFileReaderContextManagerImpl(executor, "meow.txt", path)
+                loop.run_in_executor.assert_called_once_with(executor, files._open_path, path)
+                loop.run_in_executor.reset_mock()
 
-            with pytest.raises(IsADirectoryError):  # noqa:  PT012 - raises block should contain a single statement
-                async with context_manager:
-                    ...
+        loop.run_in_executor.assert_called_once_with(executor, mock_file.close)
+        assert context_manager.file is None