Repository: hikari-py/hikari
Pull request #1075 (2022-03-13 14:31:55)

Remove nonce parameter from create message
### Summary
This was purposely removed from the bot api documentation inferring that its no-longer officially supported

### Checklist
<!-- Make sure to tick all the following boxes by putting an `x` in between (like this `[x]`) -->
- [ ] I have run `nox` and all the pipelines have passed.
- [ ] I have made unittests according to the code I have added/modified/deleted.

### Related issues
<!--
To mention an issue use `#issue-id` and to mention a merge request use `!merge-request-id`
To close/fix an issue use `Close #issue-id` or `Fix #issue-id` (depending on the merge request)
-->



Patch:
diff --git a/changes/1073.feature.md b/changes/1073.feature.md
new file mode 100644
index 00000000..b8f7b5ec
--- /dev/null
+++ b/changes/1073.feature.md
@@ -0,0 +1,1 @@
+The threaded file reader now persists the open file pointer while the context manager is active.
diff --git a/changes/1075.breaking.md b/changes/1075.breaking.md
new file mode 100644
index 00000000..c99f440d
--- /dev/null
+++ b/changes/1075.breaking.md
@@ -0,0 +1,2 @@
+Remove nonce parameter from create message.
+This was purposely removed from the bot api documentation inferring that its no-longer officially supported.
diff --git a/dev-requirements.txt b/dev-requirements.txt
index 17210028..5e748d44 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -6,7 +6,7 @@
 mock==4.0.3
 
 # Py.test stuff.
-pytest==7.0.1
+pytest==7.1.0
 pytest-asyncio==0.18.2
 pytest-cov==3.0.0
 pytest-randomly==3.11.0
@@ -28,8 +28,8 @@ sphobjinv==2.2.1
 # TYPE CHECKING #
 #################
 
-mypy==0.931
-pyright==1.1.228
+mypy==0.941
+pyright==1.1.229
 
 #######################
 # DEPENDENCY CHECKING #
diff --git a/hikari/api/rest.py b/hikari/api/rest.py
index 8fda8759..12dba041 100644
--- a/hikari/api/rest.py
+++ b/hikari/api/rest.py
@@ -1082,7 +1082,6 @@ async def create_message(
         embed: undefined.UndefinedOr[embeds_.Embed] = undefined.UNDEFINED,
         embeds: undefined.UndefinedOr[typing.Sequence[embeds_.Embed]] = undefined.UNDEFINED,
         tts: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
-        nonce: undefined.UndefinedOr[str] = undefined.UNDEFINED,
         reply: undefined.UndefinedOr[snowflakes.SnowflakeishOr[messages_.PartialMessage]] = undefined.UNDEFINED,
         mentions_everyone: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
         mentions_reply: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
@@ -1133,12 +1132,6 @@ async def create_message(
         tts : hikari.undefined.UndefinedOr[builtins.bool]
             If provided, whether the message will be read out by a screen
             reader using Discord's TTS (text-to-speech) system.
-        nonce : hikari.undefined.UndefinedOr[builtins.str]
-            An arbitrary identifier to associate with the message. This
-            can be used to identify it later in received events. If provided,
-            this must be less than 32 bytes. If not provided, then
-            a null value is placed on the message instead. All users can
-            see this value.
         reply : hikari.undefined.UndefinedOr[hikari.snowflakes.SnowflakeishOr[hikari.messages.PartialMessage]]
             If provided, the message to reply to.
         mentions_everyone : hikari.undefined.UndefinedOr[builtins.bool]
diff --git a/hikari/channels.py b/hikari/channels.py
index ddb08aac..f0aa4f00 100644
--- a/hikari/channels.py
+++ b/hikari/channels.py
@@ -481,7 +481,6 @@ async def send(
         components: undefined.UndefinedOr[typing.Sequence[special_endpoints.ComponentBuilder]] = undefined.UNDEFINED,
         embed: undefined.UndefinedOr[embeds_.Embed] = undefined.UNDEFINED,
         embeds: undefined.UndefinedOr[typing.Sequence[embeds_.Embed]] = undefined.UNDEFINED,
-        nonce: undefined.UndefinedOr[str] = undefined.UNDEFINED,
         tts: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
         reply: undefined.UndefinedOr[snowflakes.SnowflakeishOr[messages.PartialMessage]] = undefined.UNDEFINED,
         mentions_everyone: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
@@ -530,9 +529,6 @@ async def send(
             If provided, the message embeds.
         tts : hikari.undefined.UndefinedOr[builtins.bool]
             If provided, whether the message will be TTS (Text To Speech).
-        nonce : hikari.undefined.UndefinedOr[builtins.str]
-            If provided, a nonce that can be used for optimistic message
-            sending.
         reply : hikari.undefined.UndefinedOr[hikari.snowflakes.SnowflakeishOr[hikari.messages.PartialMessage]]
             If provided, the message to reply to.
         mentions_everyone : hikari.undefined.UndefinedOr[builtins.bool]
@@ -622,7 +618,6 @@ async def send(
             components=components,
             embed=embed,
             embeds=embeds,
-            nonce=nonce,
             tts=tts,
             reply=reply,
             mentions_everyone=mentions_everyone,
diff --git a/hikari/files.py b/hikari/files.py
index 10b6dbac..178d624e 100644
--- a/hikari/files.py
+++ b/hikari/files.py
@@ -48,6 +48,7 @@
 import asyncio
 import base64
 import concurrent.futures
+import errno
 import inspect
 import io
 import mimetypes
@@ -771,7 +772,7 @@ class FileReader(AsyncReader, abc.ABC):
     """The path to the resource to read."""
 
 
-def _stat(path: pathlib.Path) -> os.stat_result:
+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:
@@ -779,25 +780,30 @@ def _stat(path: pathlib.Path) -> os.stat_result:
     except RuntimeError:
         pass  # A home directory couldn't be resolved, so we'll just use the path as-is.
 
-    return path.stat()
+    # 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 _FileAsyncReaderContextManagerImpl(AsyncReaderContextManager[FileReader]):
-    impl: FileReader = attr.field()
-
-    async def __aenter__(self) -> FileReader:
-        loop = asyncio.get_running_loop()
+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()
 
-        # Will raise FileNotFoundError if the file doesn't exist (unlike is_dir),
-        # which is what we want here.
-        file_stats = await loop.run_in_executor(self.impl.executor, _stat, self.impl.path)
+    async def __aenter__(self) -> ThreadedFileReader:
+        if self.file:
+            raise RuntimeError("File is already open")
 
-        if stat.S_ISDIR(file_stats.st_mode):
-            raise IsADirectoryError(self.impl.path)
-
-        return self.impl
+        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,
@@ -805,18 +811,13 @@ async def __aexit__(
         exc: typing.Optional[BaseException],
         exc_tb: typing.Optional[types.TracebackType],
     ) -> None:
-        pass
-
+        if not self.file:
+            raise RuntimeError("File isn't open")
 
-def _open_file(path: pathlib.Path) -> typing.BinaryIO:
-    # 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.
-
-    return path.open("rb")
+        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)
@@ -828,32 +829,44 @@ class ThreadedFileReader(FileReader):
     do not need to be pickled to be communicated.
     """
 
+    _pointer: typing.BinaryIO = attr.field()
+
     async def __aiter__(self) -> typing.AsyncGenerator[typing.Any, bytes]:
         loop = asyncio.get_running_loop()
 
-        fp = await loop.run_in_executor(self.executor, _open_file, self.path)
+        while True:
+            chunk = await loop.run_in_executor(self.executor, self._pointer.read, _MAGIC)
+            yield chunk
+            if len(chunk) < _MAGIC:
+                break
 
-        try:
-            while True:
-                chunk = await loop.run_in_executor(self.executor, fp.read, _MAGIC)
-                yield chunk
-                if len(chunk) < _MAGIC:
-                    break
 
-        finally:
-            await loop.run_in_executor(self.executor, fp.close)
+@attr.define(weakref_slot=False)
+@typing.final
+class _MultiProcessingFileReaderContextManagerImpl(AsyncReaderContextManager[FileReader]):
+    executor: concurrent.futures.ProcessPoolExecutor = 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()
 
+        path = await loop.run_in_executor(self.executor, _stat, self.path)
+        return MultiprocessingFileReader(self.filename, None, self.executor, path)
+
+    async def __aexit__(
+        self,
+        exc_type: typing.Optional[typing.Type[BaseException]],
+        exc: typing.Optional[BaseException],
+        exc_tb: typing.Optional[types.TracebackType],
+    ) -> None:
+        pass
 
-def _read_all(path: pathlib.Path) -> bytes:
-    # 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.
 
-    with path.open("rb") as fp:
-        return fp.read()
+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)
@@ -952,12 +965,23 @@ def stream(
         AsyncReaderContextManager[FileReader]
             An async context manager that when entered, produces the
             data stream.
+
+        Raises
+        ------
+        IsADirectoryError
+            If the file's path leads to a directory.
+        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.:
-        is_threaded = executor is None or isinstance(executor, concurrent.futures.ThreadPoolExecutor)
-        impl = ThreadedFileReader if is_threaded else MultiprocessingFileReader
-        return _FileAsyncReaderContextManagerImpl(impl(self.filename, None, executor, self.path))
+        if executor is None or isinstance(executor, concurrent.futures.ThreadPoolExecutor):
+            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)
 
 
 ########################################################################
diff --git a/hikari/impl/rest.py b/hikari/impl/rest.py
index 31459852..3d17f30c 100644
--- a/hikari/impl/rest.py
+++ b/hikari/impl/rest.py
@@ -1353,7 +1353,6 @@ async def create_message(
         embed: undefined.UndefinedOr[embeds_.Embed] = undefined.UNDEFINED,
         embeds: undefined.UndefinedOr[typing.Sequence[embeds_.Embed]] = undefined.UNDEFINED,
         tts: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
-        nonce: undefined.UndefinedOr[str] = undefined.UNDEFINED,
         reply: undefined.UndefinedOr[snowflakes.SnowflakeishOr[messages_.PartialMessage]] = undefined.UNDEFINED,
         mentions_everyone: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
         mentions_reply: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
@@ -1379,7 +1378,6 @@ async def create_message(
             user_mentions=user_mentions,
             role_mentions=role_mentions,
         )
-        body.put("nonce", nonce)
         body.put("message_reference", reply, conversion=lambda m: {"message_id": str(int(m))})
 
         if form_builder is not None:
diff --git a/hikari/messages.py b/hikari/messages.py
index 9984ab9f..835dcb79 100644
--- a/hikari/messages.py
+++ b/hikari/messages.py
@@ -1133,7 +1133,6 @@ async def respond(
         components: undefined.UndefinedOr[typing.Sequence[special_endpoints.ComponentBuilder]] = undefined.UNDEFINED,
         embed: undefined.UndefinedOr[embeds_.Embed] = undefined.UNDEFINED,
         embeds: undefined.UndefinedOr[typing.Sequence[embeds_.Embed]] = undefined.UNDEFINED,
-        nonce: undefined.UndefinedOr[str] = undefined.UNDEFINED,
         tts: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
         reply: typing.Union[
             undefined.UndefinedType, snowflakes.SnowflakeishOr[PartialMessage], bool
@@ -1184,9 +1183,6 @@ async def respond(
             If provided, the message embeds.
         tts : hikari.undefined.UndefinedOr[builtins.bool]
             If provided, whether the message will be TTS (Text To Speech).
-        nonce : hikari.undefined.UndefinedOr[builtins.str]
-            If provided, a nonce that can be used for optimistic message
-            sending.
         reply : typing.Union[hikari.undefined.UndefinedType, hikari.snowflakes.SnowflakeishOr[hikari.messages.PartialMessage], builtins.bool]
             If provided and `builtins.True`, reply to this message.
             If provided and not `builtins.bool`, the message to reply to.
@@ -1282,7 +1278,6 @@ async def respond(
             components=components,
             embed=embed,
             embeds=embeds,
-            nonce=nonce,
             tts=tts,
             reply=reply,
             mentions_everyone=mentions_everyone,
diff --git a/hikari/users.py b/hikari/users.py
index 0fd6acf8..cbf3786f 100644
--- a/hikari/users.py
+++ b/hikari/users.py
@@ -273,7 +273,6 @@ async def send(
         components: undefined.UndefinedOr[typing.Sequence[special_endpoints.ComponentBuilder]] = undefined.UNDEFINED,
         embed: undefined.UndefinedOr[embeds_.Embed] = undefined.UNDEFINED,
         embeds: undefined.UndefinedOr[typing.Sequence[embeds_.Embed]] = undefined.UNDEFINED,
-        nonce: undefined.UndefinedOr[str] = undefined.UNDEFINED,
         tts: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
         reply: undefined.UndefinedOr[snowflakes.SnowflakeishOr[messages.PartialMessage]] = undefined.UNDEFINED,
         mentions_everyone: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
@@ -324,12 +323,6 @@ async def send(
         tts : hikari.undefined.UndefinedOr[builtins.bool]
             If provided, whether the message will be read out by a screen
             reader using Discord's TTS (text-to-speech) system.
-        nonce : hikari.undefined.UndefinedOr[builtins.str]
-            An arbitrary identifier to associate with the message. This
-            can be used to identify it later in received events. If provided,
-            this must be less than 32 bytes. If not provided, then
-            a null value is placed on the message instead. All users can
-            see this value.
         reply : hikari.undefined.UndefinedOr[hikari.snowflakes.SnowflakeishOr[hikari.messages.PartialMessage]]
             If provided, the message to reply to.
         mentions_everyone : hikari.undefined.UndefinedOr[builtins.bool]
@@ -440,7 +433,6 @@ async def send(
             components=components,
             embed=embed,
             embeds=embeds,
-            nonce=nonce,
             tts=tts,
             reply=reply,
             mentions_everyone=mentions_everyone,


Test patch:
diff --git a/tests/hikari/impl/test_rest.py b/tests/hikari/impl/test_rest.py
index a9b621f0..428c8b19 100644
--- a/tests/hikari/impl/test_rest.py
+++ b/tests/hikari/impl/test_rest.py
@@ -2307,7 +2307,6 @@ async def test_create_message_when_form(self, rest_client):
             mentions_everyone=False,
             user_mentions=[9876],
             role_mentions=[1234],
-            nonce="noncing",
             reply=StubModel(987654321),
         )
         assert returned is rest_client._entity_factory.deserialize_message.return_value
@@ -2328,7 +2327,7 @@ async def test_create_message_when_form(self, rest_client):
         )
         mock_form.add_field.assert_called_once_with(
             "payload_json",
-            '{"testing": "ensure_in_test", "nonce": "noncing", "message_reference": {"message_id": "987654321"}}',
+            '{"testing": "ensure_in_test", "message_reference": {"message_id": "987654321"}}',
             content_type="application/json",
         )
         rest_client._request.assert_awaited_once_with(expected_route, form_builder=mock_form)
@@ -2360,7 +2359,6 @@ async def test_create_message_when_no_form(self, rest_client):
             mentions_everyone=False,
             user_mentions=[9876],
             role_mentions=[1234],
-            nonce="noncing",
             reply=StubModel(987654321),
         )
         assert returned is rest_client._entity_factory.deserialize_message.return_value
@@ -2381,7 +2379,7 @@ async def test_create_message_when_no_form(self, rest_client):
         )
         rest_client._request.assert_awaited_once_with(
             expected_route,
-            json={"testing": "ensure_in_test", "nonce": "noncing", "message_reference": {"message_id": "987654321"}},
+            json={"testing": "ensure_in_test", "message_reference": {"message_id": "987654321"}},
         )
         rest_client._entity_factory.deserialize_message.assert_called_once_with({"message_id": 123})
 
diff --git a/tests/hikari/test_channels.py b/tests/hikari/test_channels.py
index b6a9880f..4a4b226e 100644
--- a/tests/hikari/test_channels.py
+++ b/tests/hikari/test_channels.py
@@ -272,7 +272,6 @@ async def test_send(self, model):
 
         await model.send(
             content="test content",
-            nonce="abc123",
             tts=True,
             attachment=mock_attachment,
             attachments=mock_attachments,
@@ -290,7 +289,6 @@ async def test_send(self, model):
         model.app.rest.create_message.assert_awaited_once_with(
             channel=12345679,
             content="test content",
-            nonce="abc123",
             tts=True,
             attachment=mock_attachment,
             attachments=mock_attachments,
diff --git a/tests/hikari/test_files.py b/tests/hikari/test_files.py
index 59ee0c04..380db057 100644
--- a/tests/hikari/test_files.py
+++ b/tests/hikari/test_files.py
@@ -22,11 +22,9 @@
 
 import base64
 import concurrent.futures
-import contextlib
 import pathlib
 import random
 import tempfile
-import typing
 
 import mock
 import pytest
@@ -52,68 +50,154 @@ def test___exit__(self, reader):
             pytest.fail(exc)
 
 
-class Test_FileAsyncReaderContextManagerImpl:
-    @pytest.mark.parametrize(
-        "executor", [concurrent.futures.ThreadPoolExecutor, concurrent.futures.ProcessPoolExecutor]
-    )
+class Test_ThreadedFileReaderContextManagerImpl:
     @pytest.mark.asyncio()
-    async def test_context_manager(self, executor: typing.Callable[[], concurrent.futures.Executor]):
-        mock_reader = mock.Mock(executor=executor())
-        context_manager = files._FileAsyncReaderContextManagerImpl(mock_reader)
+    async def test_enter_dunder_method_when_already_open(self):
+        manager = files._ThreadedFileReaderContextManagerImpl(mock.Mock(), "ea", pathlib.Path("ea"))
+        manager.file = mock.Mock()
+        with pytest.raises(RuntimeError, match="File is already open"):
+            await manager.__aenter__()
 
-        with tempfile.NamedTemporaryFile() as file:
-            mock_reader.path = pathlib.Path(file.name)
+    @pytest.mark.asyncio()
+    async def test_exit_dunder_method_when_not_open(self):
+        manager = files._ThreadedFileReaderContextManagerImpl(mock.Mock(), "ea", pathlib.Path("ea"))
+
+        with pytest.raises(RuntimeError, match="File isn't open"):
+            await manager.__aexit__(None, None, None)
+
+    @pytest.mark.asyncio()
+    async def test_context_manager(self):
+        executor = concurrent.futures.ThreadPoolExecutor()
+        mock_data = b"meeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" * 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._ThreadedFileReaderContextManagerImpl(executor, "meow.txt", path)
 
             async with context_manager as reader:
-                assert reader is mock_reader
+                data = await reader.read()
+
+                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):
+    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 only run it threaded.
-        mock_reader = mock.Mock(executor=concurrent.futures.ThreadPoolExecutor())
-        context_manager = files._FileAsyncReaderContextManagerImpl(mock_reader)
+        # for this test we have to cheat and use a thread pool executor.
+        executor = concurrent.futures.ThreadPoolExecutor()
 
-        stack = contextlib.ExitStack()
-        file = stack.enter_context(tempfile.NamedTemporaryFile())
-        expandname = stack.enter_context(mock.patch.object(pathlib.Path, "expanduser", side_effect=RuntimeError))
+        # 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
 
-        with file:
-            mock_reader.path = pathlib.Path(file.name)
+        path = pathlib.Path(file.name)
+        try:
+            context_manager = files._ThreadedFileReaderContextManagerImpl(executor, "filename.txt", path)
 
             async with context_manager as reader:
-                assert reader is mock_reader
+                assert reader.path == path
+
+            expanduser.assert_called_once_with()
 
-        expandname.assert_called_once_with()
+        finally:
+            path.unlink()
 
-    @pytest.mark.parametrize(
-        "executor", [concurrent.futures.ThreadPoolExecutor, concurrent.futures.ProcessPoolExecutor]
-    )
     @pytest.mark.asyncio()
-    async def test_context_manager_for_unknown_file(self, executor: typing.Callable[[], concurrent.futures.Executor]):
-        mock_reader = mock.Mock(executor=executor())
-        context_manager = files._FileAsyncReaderContextManagerImpl(mock_reader)
+    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)
 
-        mock_reader.path = pathlib.Path(
-            base64.urlsafe_b64encode(random.getrandbits(512).to_bytes(64, "little")).decode()
-        )
+        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.parametrize(
-        "executor", [concurrent.futures.ThreadPoolExecutor, concurrent.futures.ProcessPoolExecutor]
-    )
     @pytest.mark.asyncio()
-    async def test_test_context_manager_when_target_is_dir(
-        self, executor: typing.Callable[[], concurrent.futures.Executor]
-    ):
-        mock_reader = mock.Mock(executor=executor())
-        context_manager = files._FileAsyncReaderContextManagerImpl(mock_reader)
+    async def test_test_context_manager_when_target_is_dir(self):
+        executor = concurrent.futures.ProcessPoolExecutor()
 
         with tempfile.TemporaryDirectory() as name:
-            mock_reader.path = pathlib.Path(name)
+            path = pathlib.Path(name)
+            context_manager = files._MultiProcessingFileReaderContextManagerImpl(executor, "meow.txt", path)
 
             with pytest.raises(IsADirectoryError):  # noqa:  PT012 - raises block should contain a single statement
                 async with context_manager:
diff --git a/tests/hikari/test_messages.py b/tests/hikari/test_messages.py
index c25aa2ce..ee7ad9ac 100644
--- a/tests/hikari/test_messages.py
+++ b/tests/hikari/test_messages.py
@@ -239,7 +239,6 @@ async def test_respond(self, message):
             attachments=attachments,
             component=component,
             components=components,
-            nonce="nonce",
             tts=True,
             reply=reference_messsage,
             mentions_everyone=True,
@@ -256,7 +255,6 @@ async def test_respond(self, message):
             attachments=attachments,
             component=component,
             components=components,
-            nonce="nonce",
             tts=True,
             reply=reference_messsage,
             mentions_everyone=True,
@@ -279,7 +277,6 @@ async def test_respond_when_reply_is_True(self, message):
             attachments=undefined.UNDEFINED,
             component=undefined.UNDEFINED,
             components=undefined.UNDEFINED,
-            nonce=undefined.UNDEFINED,
             tts=undefined.UNDEFINED,
             reply=message,
             mentions_everyone=undefined.UNDEFINED,
@@ -302,7 +299,6 @@ async def test_respond_when_reply_is_False(self, message):
             attachments=undefined.UNDEFINED,
             component=undefined.UNDEFINED,
             components=undefined.UNDEFINED,
-            nonce=undefined.UNDEFINED,
             tts=undefined.UNDEFINED,
             reply=undefined.UNDEFINED,
             mentions_everyone=undefined.UNDEFINED,
diff --git a/tests/hikari/test_users.py b/tests/hikari/test_users.py
index 7cd19113..476ecba1 100644
--- a/tests/hikari/test_users.py
+++ b/tests/hikari/test_users.py
@@ -75,7 +75,6 @@ async def test_send_uses_cached_id(self, obj):
             attachments=attachments,
             component=component,
             components=components,
-            nonce="nonce",
             tts=True,
             reply=reply,
             mentions_everyone=False,
@@ -97,7 +96,6 @@ async def test_send_uses_cached_id(self, obj):
             attachments=attachments,
             component=component,
             components=components,
-            nonce="nonce",
             tts=True,
             mentions_everyone=False,
             reply=reply,
@@ -128,7 +126,6 @@ async def test_send_when_not_cached(self, obj):
             attachments=undefined.UNDEFINED,
             component=undefined.UNDEFINED,
             components=undefined.UNDEFINED,
-            nonce=undefined.UNDEFINED,
             tts=undefined.UNDEFINED,
             mentions_everyone=undefined.UNDEFINED,
             reply=undefined.UNDEFINED,
@@ -157,7 +154,6 @@ async def test_send_when_not_cache_aware(self, obj):
             attachments=undefined.UNDEFINED,
             component=undefined.UNDEFINED,
             components=undefined.UNDEFINED,
-            nonce=undefined.UNDEFINED,
             tts=undefined.UNDEFINED,
             mentions_everyone=undefined.UNDEFINED,
             reply=undefined.UNDEFINED,