From db48601c09d1a1275932612932f201dd825758f7 Mon Sep 17 00:00:00 2001
From: jakkdl
Date: Thu, 17 Aug 2023 15:05:04 +0200
Subject: [PATCH 1/9] Edit various files so the entire trio/ directory can be
type-checked without error
---
check.sh | 6 +-
pyproject.toml | 100 ++++++++----------
trio/_core/_generated_run.py | 2 +-
trio/_core/_io_common.py | 8 +-
trio/_core/_ki.py | 8 +-
trio/_core/_parking_lot.py | 2 +-
trio/_core/_tests/test_io.py | 13 ++-
trio/_core/_tests/test_ki.py | 10 +-
trio/_core/_tests/test_multierror.py | 2 +-
.../apport_excepthook.py | 2 +-
.../ipython_custom_exc.py | 2 +-
.../simple_excepthook.py | 2 +-
trio/_core/_tests/test_run.py | 4 +-
trio/_core/_wakeup_socketpair.py | 16 +--
trio/_path.py | 8 +-
trio/_tests/check_type_completeness.py | 2 +
trio/_tests/test_contextvars.py | 4 +-
trio/_tests/test_dtls.py | 4 +-
trio/_tests/test_exports.py | 12 ++-
trio/_tests/test_highlevel_serve_listeners.py | 2 +-
trio/_tests/test_socket.py | 2 +-
trio/_tests/test_subprocess.py | 14 ++-
trio/_tests/test_threads.py | 12 ++-
trio/_tests/test_tracing.py | 10 +-
trio/_tests/test_unix_pipes.py | 8 +-
trio/_tools/gen_exports.py | 7 --
trio/_unix_pipes.py | 3 +
trio/py.typed | 0
trio/testing/_fake_net.py | 35 ++++--
29 files changed, 172 insertions(+), 128 deletions(-)
create mode 100644 trio/py.typed
diff --git a/check.sh b/check.sh
index a0efa531b6..ace193a62a 100755
--- a/check.sh
+++ b/check.sh
@@ -27,9 +27,9 @@ fi
flake8 trio/ || EXIT_STATUS=$?
# Run mypy on all supported platforms
-mypy -m trio -m trio.testing --platform linux || EXIT_STATUS=$?
-mypy -m trio -m trio.testing --platform darwin || EXIT_STATUS=$? # tests FreeBSD too
-mypy -m trio -m trio.testing --platform win32 || EXIT_STATUS=$?
+mypy trio --platform linux || EXIT_STATUS=$?
+mypy trio --platform darwin || EXIT_STATUS=$? # tests FreeBSD too
+mypy trio --platform win32 || EXIT_STATUS=$?
# Check pip compile is consistent
pip-compile test-requirements.in
diff --git a/pyproject.toml b/pyproject.toml
index 073e2508d1..a212393452 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -34,70 +34,58 @@ warn_redundant_casts = true
warn_return_any = true
# Avoid subtle backsliding
-#disallow_any_decorated = true
-#disallow_incomplete_defs = true
-#disallow_subclassing_any = true
+disallow_any_decorated = true
+disallow_any_generics = true
+disallow_any_unimported = false # Enable once Outcome has stubs.
+disallow_incomplete_defs = true
+disallow_subclassing_any = true
+disallow_untyped_decorators = true
+disallow_untyped_defs = true
-# Enable gradually / for new modules
+# Enable once other problems are dealt with
check_untyped_defs = false
disallow_untyped_calls = false
-disallow_untyped_defs = false
-# DO NOT use `ignore_errors`; it doesn't apply
-# downstream and users have to deal with them.
-[[tool.mypy.overrides]]
-# Fully typed, enable stricter checks
-module = [
- "trio._abc",
- "trio._core._asyncgens",
- "trio._core._entry_queue",
- "trio._core._generated_io_epoll",
- "trio._core._generated_io_kqueue",
- "trio._core._generated_run",
- "trio._core._io_epoll",
- "trio._core._io_kqueue",
- "trio._core._local",
- "trio._core._multierror",
- "trio._core._run",
- "trio._core._thread_cache",
- "trio._core._traps",
- "trio._core._unbounded_queue",
- "trio._core._unbounded_queue",
- "trio._deprecate",
- "trio._dtls",
- "trio._file_io",
- "trio._highlevel_open_tcp_stream",
- "trio._ki",
- "trio._socket",
- "trio._subprocess",
- "trio._subprocess_platform",
- "trio._subprocess_platform.kqueue",
- "trio._subprocess_platform.waitid",
- "trio._subprocess_platform.windows",
- "trio._sync",
- "trio._threads",
- "trio._tools.gen_exports",
- "trio._util",
-]
-disallow_incomplete_defs = true
-disallow_untyped_defs = true
-disallow_untyped_decorators = true
-disallow_any_generics = true
-disallow_any_decorated = true
-disallow_any_unimported = false # Enable once outcome has stubs.
-disallow_subclassing_any = true
+# files not yet fully typed
[[tool.mypy.overrides]]
-# Needs to use Any due to some complex introspection.
module = [
- "trio._path",
+# 2747
+"trio/testing/_network",
+"trio/testing/_trio_test",
+"trio/testing/_checkpoints",
+"trio/testing/_check_streams",
+"trio/testing/_memory_streams",
+# 2745
+"trio/_ssl",
+# 2756
+"trio/_highlevel_open_unix_stream",
+"trio/_highlevel_serve_listeners",
+"trio/_highlevel_ssl_helpers",
+"trio/_highlevel_socket",
+# 2755
+"trio/_core/_windows_cffi",
+"trio/_wait_for_object",
+# 2761
+"trio/_core/_generated_io_windows",
+"trio/_core/_io_windows",
+
+
+"trio/_signals",
+
+# internal
+"trio/_windows_pipes",
+
+# tests
+"trio/_core/_tests/*",
+"trio/_tests/*",
+"trio/testing/_fake_net", # 30
]
-disallow_incomplete_defs = true
-disallow_untyped_defs = true
-#disallow_any_generics = true
-#disallow_any_decorated = true
-disallow_any_unimported = true
-disallow_subclassing_any = true
+disallow_any_decorated = false
+disallow_any_generics = false
+disallow_any_unimported = false
+disallow_incomplete_defs = false
+disallow_untyped_defs = false
[tool.pytest.ini_options]
addopts = ["--strict-markers", "--strict-config"]
diff --git a/trio/_core/_generated_run.py b/trio/_core/_generated_run.py
index 35ecd45a1b..bd5abbd639 100644
--- a/trio/_core/_generated_run.py
+++ b/trio/_core/_generated_run.py
@@ -88,7 +88,7 @@ def current_root_task() ->(Task | None):
raise RuntimeError("must be called from async context")
-def reschedule(task: Task, next_send: Outcome[Any]=_NO_SEND) ->None: # type: ignore[has-type]
+def reschedule(task: Task, next_send: Outcome[Any]=_NO_SEND) ->None:
"""Reschedule the given task with the given
:class:`outcome.Outcome`.
diff --git a/trio/_core/_io_common.py b/trio/_core/_io_common.py
index b141474fda..c1af293278 100644
--- a/trio/_core/_io_common.py
+++ b/trio/_core/_io_common.py
@@ -1,12 +1,18 @@
+from __future__ import annotations
+
import copy
+from typing import TYPE_CHECKING
import outcome
from .. import _core
+if TYPE_CHECKING:
+ from ._io_epoll import EpollWaiters
+
# Utility function shared between _io_epoll and _io_windows
-def wake_all(waiters, exc):
+def wake_all(waiters: EpollWaiters, exc: BaseException) -> None:
try:
current_task = _core.current_task()
except RuntimeError:
diff --git a/trio/_core/_ki.py b/trio/_core/_ki.py
index 8ae83c287a..10172e4989 100644
--- a/trio/_core/_ki.py
+++ b/trio/_core/_ki.py
@@ -121,7 +121,7 @@ def currently_ki_protected() -> bool:
# see python-trio/async_generator/async_generator/_impl.py
def legacy_isasyncgenfunction(
obj: object,
-) -> TypeGuard[Callable[..., types.AsyncGeneratorType]]:
+) -> TypeGuard[Callable[..., types.AsyncGeneratorType[object, object]]]:
return getattr(obj, "_async_gen_function", None) == id(obj)
@@ -196,7 +196,9 @@ def wrapper(*args: ArgsT.args, **kwargs: ArgsT.kwargs) -> RetT:
@attr.s
class KIManager:
- handler = attr.ib(default=None)
+ handler: Callable[[int, types.FrameType | None], None] | None = attr.ib(
+ default=None
+ )
def install(
self,
@@ -221,7 +223,7 @@ def handler(signum: int, frame: types.FrameType | None) -> None:
self.handler = handler
signal.signal(signal.SIGINT, handler)
- def close(self):
+ def close(self) -> None:
if self.handler is not None:
if signal.getsignal(signal.SIGINT) is self.handler:
signal.signal(signal.SIGINT, signal.default_int_handler)
diff --git a/trio/_core/_parking_lot.py b/trio/_core/_parking_lot.py
index 74708433da..6510745e5b 100644
--- a/trio/_core/_parking_lot.py
+++ b/trio/_core/_parking_lot.py
@@ -139,7 +139,7 @@ async def park(self) -> None:
self._parked[task] = None
task.custom_sleep_data = self
- def abort_fn(_):
+ def abort_fn(_: _core.RaiseCancelT) -> _core.Abort:
del task.custom_sleep_data._parked[task]
return _core.Abort.SUCCEEDED
diff --git a/trio/_core/_tests/test_io.py b/trio/_core/_tests/test_io.py
index 21a954941c..2205c83976 100644
--- a/trio/_core/_tests/test_io.py
+++ b/trio/_core/_tests/test_io.py
@@ -1,6 +1,9 @@
+from __future__ import annotations
+
import random
import socket as stdlib_socket
from contextlib import suppress
+from typing import Callable
import pytest
@@ -47,15 +50,15 @@ def fileno_wrapper(fileobj):
return fileno_wrapper
-wait_readable_options = [trio.lowlevel.wait_readable]
-wait_writable_options = [trio.lowlevel.wait_writable]
-notify_closing_options = [trio.lowlevel.notify_closing]
+wait_readable_options: list[Callable] = [trio.lowlevel.wait_readable]
+wait_writable_options: list[Callable] = [trio.lowlevel.wait_writable]
+notify_closing_options: list[Callable] = [trio.lowlevel.notify_closing]
-for options_list in [
+for options_list in (
wait_readable_options,
wait_writable_options,
notify_closing_options,
-]:
+):
options_list += [using_fileno(f) for f in options_list]
# Decorators that feed in different settings for wait_readable / wait_writable
diff --git a/trio/_core/_tests/test_ki.py b/trio/_core/_tests/test_ki.py
index fdbada4624..b6eef68e22 100644
--- a/trio/_core/_tests/test_ki.py
+++ b/trio/_core/_tests/test_ki.py
@@ -1,7 +1,10 @@
+from __future__ import annotations
+
import contextlib
import inspect
import signal
import threading
+from typing import TYPE_CHECKING
import outcome
import pytest
@@ -16,6 +19,9 @@
from ..._util import signal_raise
from ...testing import wait_all_tasks_blocked
+if TYPE_CHECKING:
+ from ..._core import Abort, RaiseCancelT
+
def ki_self():
signal_raise(signal.SIGINT)
@@ -375,7 +381,7 @@ async def main():
ki_self()
task = _core.current_task()
- def abort(_):
+ def abort(_: RaiseCancelT) -> Abort:
_core.reschedule(task, outcome.Value(1))
return _core.Abort.FAILED
@@ -394,7 +400,7 @@ async def main():
ki_self()
task = _core.current_task()
- def abort(raise_cancel):
+ def abort(raise_cancel: RaiseCancelT) -> Abort:
result = outcome.capture(raise_cancel)
_core.reschedule(task, result)
return _core.Abort.FAILED
diff --git a/trio/_core/_tests/test_multierror.py b/trio/_core/_tests/test_multierror.py
index 7a8bd2f9a8..52e5e39d1b 100644
--- a/trio/_core/_tests/test_multierror.py
+++ b/trio/_core/_tests/test_multierror.py
@@ -555,7 +555,7 @@ def test_apport_excepthook_monkeypatch_interaction():
@pytest.mark.parametrize("protocol", range(0, pickle.HIGHEST_PROTOCOL + 1))
-def test_pickle_multierror(protocol) -> None:
+def test_pickle_multierror(protocol: int) -> None:
# use trio.MultiError to make sure that pickle works through the deprecation layer
import trio
diff --git a/trio/_core/_tests/test_multierror_scripts/apport_excepthook.py b/trio/_core/_tests/test_multierror_scripts/apport_excepthook.py
index 3e1d23ca8e..e51b8cdca0 100644
--- a/trio/_core/_tests/test_multierror_scripts/apport_excepthook.py
+++ b/trio/_core/_tests/test_multierror_scripts/apport_excepthook.py
@@ -12,4 +12,4 @@
import trio
-raise trio.MultiError([KeyError("key_error"), ValueError("value_error")])
+raise trio.MultiError([KeyError("key_error"), ValueError("value_error")]) # type: ignore[attr-defined]
diff --git a/trio/_core/_tests/test_multierror_scripts/ipython_custom_exc.py b/trio/_core/_tests/test_multierror_scripts/ipython_custom_exc.py
index 80e42b6a2c..c8086d3a0e 100644
--- a/trio/_core/_tests/test_multierror_scripts/ipython_custom_exc.py
+++ b/trio/_core/_tests/test_multierror_scripts/ipython_custom_exc.py
@@ -33,4 +33,4 @@ def custom_exc_hook(etype, value, tb, tb_offset=None):
# The custom excepthook should run, because Trio was polite and didn't
# override it
-raise trio.MultiError([ValueError(), KeyError()])
+raise trio.MultiError([ValueError(), KeyError()]) # type: ignore[attr-defined]
diff --git a/trio/_core/_tests/test_multierror_scripts/simple_excepthook.py b/trio/_core/_tests/test_multierror_scripts/simple_excepthook.py
index 94004525db..c2297df400 100644
--- a/trio/_core/_tests/test_multierror_scripts/simple_excepthook.py
+++ b/trio/_core/_tests/test_multierror_scripts/simple_excepthook.py
@@ -18,4 +18,4 @@ def exc2_fn():
# This should be printed nicely, because Trio overrode sys.excepthook
-raise trio.MultiError([exc1_fn(), exc2_fn()])
+raise trio.MultiError([exc1_fn(), exc2_fn()]) # type: ignore[attr-defined]
diff --git a/trio/_core/_tests/test_run.py b/trio/_core/_tests/test_run.py
index 81c3b73cc4..6d34d8f223 100644
--- a/trio/_core/_tests/test_run.py
+++ b/trio/_core/_tests/test_run.py
@@ -1954,7 +1954,7 @@ async def test_Nursery_private_init():
def test_Nursery_subclass():
with pytest.raises(TypeError):
- class Subclass(_core._run.Nursery):
+ class Subclass(_core._run.Nursery): # type: ignore[misc]
pass
@@ -1984,7 +1984,7 @@ class Subclass(_core.Cancelled):
def test_CancelScope_subclass():
with pytest.raises(TypeError):
- class Subclass(_core.CancelScope):
+ class Subclass(_core.CancelScope): # type: ignore[misc]
pass
diff --git a/trio/_core/_wakeup_socketpair.py b/trio/_core/_wakeup_socketpair.py
index 51a80ef024..2ad1a023fe 100644
--- a/trio/_core/_wakeup_socketpair.py
+++ b/trio/_core/_wakeup_socketpair.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
import signal
import socket
import warnings
@@ -7,7 +9,7 @@
class WakeupSocketpair:
- def __init__(self):
+ def __init__(self) -> None:
self.wakeup_sock, self.write_sock = socket.socketpair()
self.wakeup_sock.setblocking(False)
self.write_sock.setblocking(False)
@@ -27,26 +29,26 @@ def __init__(self):
self.write_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
except OSError:
pass
- self.old_wakeup_fd = None
+ self.old_wakeup_fd: int | None = None
- def wakeup_thread_and_signal_safe(self):
+ def wakeup_thread_and_signal_safe(self) -> None:
try:
self.write_sock.send(b"\x00")
except BlockingIOError:
pass
- async def wait_woken(self):
+ async def wait_woken(self) -> None:
await _core.wait_readable(self.wakeup_sock)
self.drain()
- def drain(self):
+ def drain(self) -> None:
try:
while True:
self.wakeup_sock.recv(2**16)
except BlockingIOError:
pass
- def wakeup_on_signals(self):
+ def wakeup_on_signals(self) -> None:
assert self.old_wakeup_fd is None
if not is_main_thread():
return
@@ -64,7 +66,7 @@ def wakeup_on_signals(self):
)
)
- def close(self):
+ def close(self) -> None:
self.wakeup_sock.close()
self.write_sock.close()
if self.old_wakeup_fd is not None:
diff --git a/trio/_path.py b/trio/_path.py
index b7e6b16e4a..c2763e03af 100644
--- a/trio/_path.py
+++ b/trio/_path.py
@@ -116,9 +116,9 @@ async def wrapper(self: Path, *args: P.args, **kwargs: P.kwargs) -> Path:
def classmethod_wrapper_factory(
cls: AsyncAutoWrapperType, meth_name: str
-) -> classmethod:
+) -> classmethod: # type: ignore[type-arg]
@async_wraps(cls, cls._wraps, meth_name)
- async def wrapper(cls: type[Path], *args: Any, **kwargs: Any) -> Path:
+ async def wrapper(cls: type[Path], *args: Any, **kwargs: Any) -> Path: # type: ignore[misc] # contains Any
meth = getattr(cls._wraps, meth_name)
func = partial(meth, *args, **kwargs)
value = await trio.to_thread.run_sync(func)
@@ -163,7 +163,7 @@ def generate_forwards(cls, attrs: dict[str, object]) -> None:
def generate_wraps(cls, attrs: dict[str, object]) -> None:
# generate wrappers for functions of _wraps
- wrapper: classmethod | Callable
+ wrapper: classmethod | Callable[..., object] # type: ignore[type-arg]
for attr_name, attr in cls._wraps.__dict__.items():
# .z. exclude cls._wrap_iter
if attr_name.startswith("_") or attr_name in attrs:
@@ -188,7 +188,7 @@ def generate_magic(cls, attrs: dict[str, object]) -> None:
def generate_iter(cls, attrs: dict[str, object]) -> None:
# generate wrappers for methods that return iterators
- wrapper: Callable
+ wrapper: Callable[..., object]
for attr_name, attr in cls._wraps.__dict__.items():
if attr_name in cls._wrap_iter:
wrapper = iter_wrapper_factory(cls, attr_name)
diff --git a/trio/_tests/check_type_completeness.py b/trio/_tests/check_type_completeness.py
index 7a65a4249e..abaabcf785 100755
--- a/trio/_tests/check_type_completeness.py
+++ b/trio/_tests/check_type_completeness.py
@@ -1,4 +1,6 @@
#!/usr/bin/env python3
+from __future__ import annotations
+
# this file is not run as part of the tests, instead it's run standalone from check.sh
import argparse
import json
diff --git a/trio/_tests/test_contextvars.py b/trio/_tests/test_contextvars.py
index 63853f5171..0ff13435cf 100644
--- a/trio/_tests/test_contextvars.py
+++ b/trio/_tests/test_contextvars.py
@@ -2,7 +2,9 @@
from .. import _core
-trio_testing_contextvar = contextvars.ContextVar("trio_testing_contextvar")
+trio_testing_contextvar: contextvars.ContextVar = contextvars.ContextVar(
+ "trio_testing_contextvar"
+)
async def test_contextvars_default():
diff --git a/trio/_tests/test_dtls.py b/trio/_tests/test_dtls.py
index b8c32c6d5f..8cb06ccb3d 100644
--- a/trio/_tests/test_dtls.py
+++ b/trio/_tests/test_dtls.py
@@ -17,10 +17,10 @@
ca = trustme.CA()
server_cert = ca.issue_cert("example.com")
-server_ctx = SSL.Context(SSL.DTLS_METHOD)
+server_ctx = SSL.Context(SSL.DTLS_METHOD) # type: ignore[attr-defined]
server_cert.configure_cert(server_ctx)
-client_ctx = SSL.Context(SSL.DTLS_METHOD)
+client_ctx = SSL.Context(SSL.DTLS_METHOD) # type: ignore[attr-defined]
ca.configure_trust(client_ctx)
diff --git a/trio/_tests/test_exports.py b/trio/_tests/test_exports.py
index c0da975fb4..1a145f397e 100644
--- a/trio/_tests/test_exports.py
+++ b/trio/_tests/test_exports.py
@@ -9,7 +9,7 @@
import sys
from pathlib import Path
from types import ModuleType
-from typing import Protocol
+from typing import Dict, Protocol
import attrs
import pytest
@@ -27,7 +27,7 @@
try: # If installed, check both versions of this class.
from typing_extensions import Protocol as Protocol_ext
except ImportError: # pragma: no cover
- Protocol_ext = Protocol
+ Protocol_ext = Protocol # type: ignore[assignment]
def _ensure_mypy_cache_updated():
@@ -240,7 +240,9 @@ def no_underscores(symbols):
)
@pytest.mark.parametrize("module_name", PUBLIC_MODULE_NAMES)
@pytest.mark.parametrize("tool", ["jedi", "mypy"])
-def test_static_tool_sees_class_members(tool, module_name, tmpdir) -> None:
+def test_static_tool_sees_class_members(
+ tool: str, module_name: str, tmpdir: Path
+) -> None:
module = PUBLIC_MODULES[PUBLIC_MODULE_NAMES.index(module_name)]
# ignore hidden, but not dunder, symbols
@@ -301,7 +303,7 @@ def lookup_symbol(symbol):
with mod_cache.open() as f:
return json.loads(f.read())["names"][name]
- errors: dict[str, object] = {}
+ errors: Dict[str, object] = {}
for class_name, class_ in module.__dict__.items():
if not isinstance(class_, type):
continue
@@ -503,7 +505,7 @@ def test_classes_are_final():
continue
# These are classes that are conceptually abstract, but
# inspect.isabstract returns False for boring reasons.
- if class_ in {trio.abc.Instrument, trio.socket.SocketType}:
+ if class_ in (trio.abc.Instrument, trio.socket.SocketType):
continue
# Enums have their own metaclass, so we can't use our metaclasses.
# And I don't think there's a lot of risk from people subclassing
diff --git a/trio/_tests/test_highlevel_serve_listeners.py b/trio/_tests/test_highlevel_serve_listeners.py
index 4385263899..67e2eecbc8 100644
--- a/trio/_tests/test_highlevel_serve_listeners.py
+++ b/trio/_tests/test_highlevel_serve_listeners.py
@@ -12,7 +12,7 @@
class MemoryListener(trio.abc.Listener):
closed = attr.ib(default=False)
accepted_streams = attr.ib(factory=list)
- queued_streams = attr.ib(factory=(lambda: trio.open_memory_channel(1)))
+ queued_streams = attr.ib(factory=(lambda: trio.open_memory_channel[object](1)))
accept_hook = attr.ib(default=None)
async def connect(self):
diff --git a/trio/_tests/test_socket.py b/trio/_tests/test_socket.py
index e9baff436a..f01b4fde14 100644
--- a/trio/_tests/test_socket.py
+++ b/trio/_tests/test_socket.py
@@ -360,7 +360,7 @@ async def test_SocketType_basics():
sock.close()
-async def test_SocketType_setsockopt():
+async def test_SocketType_setsockopt() -> None:
sock = tsocket.socket()
with sock as _:
# specifying optlen. Not supported on pypy, and I couldn't find
diff --git a/trio/_tests/test_subprocess.py b/trio/_tests/test_subprocess.py
index 4dfaef4c7f..7986dfd71e 100644
--- a/trio/_tests/test_subprocess.py
+++ b/trio/_tests/test_subprocess.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
import os
import random
import signal
@@ -6,6 +8,7 @@
from contextlib import asynccontextmanager
from functools import partial
from pathlib import Path as SyncPath
+from typing import TYPE_CHECKING
import pytest
@@ -24,8 +27,15 @@
from ..lowlevel import open_process
from ..testing import assert_no_checkpoints, wait_all_tasks_blocked
+if TYPE_CHECKING:
+ ...
+ from signal import Signals
+
posix = os.name == "posix"
-if posix:
+SIGKILL: Signals | None
+SIGTERM: Signals | None
+SIGUSR1: Signals | None
+if (not TYPE_CHECKING and posix) or sys.platform != "win32":
from signal import SIGKILL, SIGTERM, SIGUSR1
else:
SIGKILL, SIGTERM, SIGUSR1 = None, None, None
@@ -574,7 +584,7 @@ async def test_for_leaking_fds():
async def test_subprocess_pidfd_unnotified():
noticed_exit = None
- async def wait_and_tell(proc) -> None:
+ async def wait_and_tell(proc: Process) -> None:
nonlocal noticed_exit
noticed_exit = Event()
await proc.wait()
diff --git a/trio/_tests/test_threads.py b/trio/_tests/test_threads.py
index 21eb7b12e8..9149f43037 100644
--- a/trio/_tests/test_threads.py
+++ b/trio/_tests/test_threads.py
@@ -170,7 +170,7 @@ async def main():
async def test_named_thread():
ending = " from trio._tests.test_threads.test_named_thread"
- def inner(name="inner" + ending) -> threading.Thread:
+ def inner(name: str = "inner" + ending) -> threading.Thread:
assert threading.current_thread().name == name
return threading.current_thread()
@@ -185,7 +185,7 @@ def f(name: str) -> Callable[[None], threading.Thread]:
await to_thread_run_sync(f("None" + ending))
# test that you can set a custom name, and that it's reset afterwards
- async def test_thread_name(name: str):
+ async def test_thread_name(name: str) -> None:
thread = await to_thread_run_sync(f(name), thread_name=name)
assert re.match("Trio thread [0-9]*", thread.name)
@@ -235,7 +235,7 @@ def _get_thread_name(ident: Optional[int] = None) -> Optional[str]:
# and most mac machines. So unless the platform is linux it will just skip
# in case it fails to fetch the os thread name.
async def test_named_thread_os():
- def inner(name) -> threading.Thread:
+ def inner(name: str) -> threading.Thread:
os_thread_name = _get_thread_name()
if os_thread_name is None and sys.platform != "linux":
pytest.skip(f"no pthread OS support on {sys.platform}")
@@ -253,7 +253,7 @@ def f(name: str) -> Callable[[None], threading.Thread]:
await to_thread_run_sync(f(default), thread_name=None)
# test that you can set a custom name, and that it's reset afterwards
- async def test_thread_name(name: str, expected: Optional[str] = None):
+ async def test_thread_name(name: str, expected: Optional[str] = None) -> None:
if expected is None:
expected = name
thread = await to_thread_run_sync(f(expected), thread_name=name)
@@ -584,7 +584,9 @@ async def async_fn(): # pragma: no cover
await to_thread_run_sync(async_fn)
-trio_test_contextvar = contextvars.ContextVar("trio_test_contextvar")
+trio_test_contextvar: contextvars.ContextVar = contextvars.ContextVar(
+ "trio_test_contextvar"
+)
async def test_trio_to_thread_run_sync_contextvars():
diff --git a/trio/_tests/test_tracing.py b/trio/_tests/test_tracing.py
index 07d1ff7609..e5110eaff3 100644
--- a/trio/_tests/test_tracing.py
+++ b/trio/_tests/test_tracing.py
@@ -1,26 +1,26 @@
import trio
-async def coro1(event: trio.Event):
+async def coro1(event: trio.Event) -> None:
event.set()
await trio.sleep_forever()
-async def coro2(event: trio.Event):
+async def coro2(event: trio.Event) -> None:
await coro1(event)
-async def coro3(event: trio.Event):
+async def coro3(event: trio.Event) -> None:
await coro2(event)
-async def coro2_async_gen(event: trio.Event):
+async def coro2_async_gen(event):
yield await trio.lowlevel.checkpoint()
yield await coro1(event)
yield await trio.lowlevel.checkpoint()
-async def coro3_async_gen(event: trio.Event):
+async def coro3_async_gen(event: trio.Event) -> None:
async for x in coro2_async_gen(event):
pass
diff --git a/trio/_tests/test_unix_pipes.py b/trio/_tests/test_unix_pipes.py
index acee75aafb..0b0d2ceb23 100644
--- a/trio/_tests/test_unix_pipes.py
+++ b/trio/_tests/test_unix_pipes.py
@@ -1,7 +1,10 @@
+from __future__ import annotations
+
import errno
import os
import select
import sys
+from typing import TYPE_CHECKING
import pytest
@@ -11,6 +14,9 @@
posix = os.name == "posix"
pytestmark = pytest.mark.skipif(not posix, reason="posix only")
+
+assert not TYPE_CHECKING or sys.platform == "unix"
+
if posix:
from .._unix_pipes import FdStream
else:
@@ -19,7 +25,7 @@
# Have to use quoted types so import doesn't crash on windows
-async def make_pipe() -> "Tuple[FdStream, FdStream]":
+async def make_pipe() -> "tuple[FdStream, FdStream]":
"""Makes a new pair of pipes."""
(r, w) = os.pipe()
return FdStream(w), FdStream(r)
diff --git a/trio/_tools/gen_exports.py b/trio/_tools/gen_exports.py
index 9d78cd5bd7..3c598e8eae 100755
--- a/trio/_tools/gen_exports.py
+++ b/trio/_tools/gen_exports.py
@@ -158,13 +158,6 @@ def gen_public_wrappers_source(file: File) -> str:
if is_cm: # pragma: no cover
func = func.replace("->Iterator", "->ContextManager")
- # TODO: hacky workaround until we run mypy without `-m`, which breaks imports
- # enough that it cannot figure out the type of _NO_SEND
- if file.path.stem == "_run" and func.startswith(
- "def reschedule"
- ): # pragma: no cover
- func = func.replace("None:\n", "None: # type: ignore[has-type]\n")
-
# Create export function body
template = TEMPLATE.format(
" await " if isinstance(method, ast.AsyncFunctionDef) else " ",
diff --git a/trio/_unix_pipes.py b/trio/_unix_pipes.py
index 716550790e..1a389e12dd 100644
--- a/trio/_unix_pipes.py
+++ b/trio/_unix_pipes.py
@@ -2,6 +2,7 @@
import errno
import os
+import sys
from typing import TYPE_CHECKING
import trio
@@ -12,6 +13,8 @@
if TYPE_CHECKING:
from typing import Final as FinalType
+assert not TYPE_CHECKING or sys.platform != "win32"
+
if os.name != "posix":
# We raise an error here rather than gating the import in lowlevel.py
# in order to keep jedi static analysis happy.
diff --git a/trio/py.typed b/trio/py.typed
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/trio/testing/_fake_net.py b/trio/testing/_fake_net.py
index b3bdfd85c0..9befedf21b 100644
--- a/trio/testing/_fake_net.py
+++ b/trio/testing/_fake_net.py
@@ -19,6 +19,7 @@
from trio._util import Final, NoPublicConstructor
if TYPE_CHECKING:
+ from socket import AddressFamily, SocketKind
from types import TracebackType
IPAddress = Union[ipaddress.IPv4Address, ipaddress.IPv6Address]
@@ -104,7 +105,7 @@ def reply(self, payload):
class FakeSocketFactory(trio.abc.SocketFactory):
fake_net: "FakeNet"
- def socket(self, family: int, type: int, proto: int) -> "FakeSocket":
+ def socket(self, family: int, type: int, proto: int) -> FakeSocket: # type: ignore[override]
return FakeSocket._create(self.fake_net, family, type, proto)
@@ -113,22 +114,38 @@ class FakeHostnameResolver(trio.abc.HostnameResolver):
fake_net: "FakeNet"
async def getaddrinfo(
- self, host: str, port: Union[int, str], family=0, type=0, proto=0, flags=0
- ):
+ self,
+ host: bytes | str | None,
+ port: bytes | str | int | None,
+ family: int = 0,
+ type: int = 0,
+ proto: int = 0,
+ flags: int = 0,
+ ) -> list[
+ tuple[
+ AddressFamily,
+ SocketKind,
+ int,
+ str,
+ tuple[str, int] | tuple[str, int, int, int],
+ ]
+ ]:
raise NotImplementedError("FakeNet doesn't do fake DNS yet")
- async def getnameinfo(self, sockaddr, flags: int):
+ async def getnameinfo(
+ self, sockaddr: tuple[str, int] | tuple[str, int, int, int], flags: int
+ ) -> tuple[str, str]:
raise NotImplementedError("FakeNet doesn't do fake DNS yet")
class FakeNet(metaclass=Final):
- def __init__(self):
+ def __init__(self) -> None:
# When we need to pick an arbitrary unique ip address/port, use these:
self._auto_ipv4_iter = ipaddress.IPv4Network("1.0.0.0/8").hosts()
- self._auto_ipv4_iter = ipaddress.IPv6Network("1::/16").hosts()
+ self._auto_ipv4_iter = ipaddress.IPv6Network("1::/16").hosts() # type: ignore[assignment]
self._auto_port_iter = iter(range(50000, 65535))
- self._bound: Dict[UDPBinding, FakeSocket] = {}
+ self._bound: dict[UDPBinding, FakeSocket] = {}
self.route_packet = None
@@ -176,7 +193,7 @@ def __init__(self, fake_net: FakeNet, family: int, type: int, proto: int):
self._closed = False
- self._packet_sender, self._packet_receiver = trio.open_memory_channel(
+ self._packet_sender, self._packet_receiver = trio.open_memory_channel[object](
float("inf")
)
@@ -206,7 +223,7 @@ async def _resolve_address_nocp(self, address, *, local):
local=local,
)
- def _deliver_packet(self, packet: UDPPacket):
+ def _deliver_packet(self, packet: UDPPacket) -> None:
try:
self._packet_sender.send_nowait(packet)
except trio.BrokenResourceError:
From 6bcf4059444346429853172bef619ba329d059da Mon Sep 17 00:00:00 2001
From: Spencer Brown
Date: Fri, 18 Aug 2023 08:27:56 +1000
Subject: [PATCH 2/9] Use the correct type here
---
trio/testing/_fake_net.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/trio/testing/_fake_net.py b/trio/testing/_fake_net.py
index 9befedf21b..de6a9d2ebf 100644
--- a/trio/testing/_fake_net.py
+++ b/trio/testing/_fake_net.py
@@ -193,7 +193,7 @@ def __init__(self, fake_net: FakeNet, family: int, type: int, proto: int):
self._closed = False
- self._packet_sender, self._packet_receiver = trio.open_memory_channel[object](
+ self._packet_sender, self._packet_receiver = trio.open_memory_channel[UDPPacket](
float("inf")
)
From e2e0273e39b74de63f8cbc3b0d7532dfb539850c Mon Sep 17 00:00:00 2001
From: Spencer Brown
Date: Fri, 18 Aug 2023 08:28:05 +1000
Subject: [PATCH 3/9] Fix wrong function name
---
trio/testing/_fake_net.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/trio/testing/_fake_net.py b/trio/testing/_fake_net.py
index de6a9d2ebf..a5cb97b282 100644
--- a/trio/testing/_fake_net.py
+++ b/trio/testing/_fake_net.py
@@ -237,7 +237,7 @@ def _deliver_packet(self, packet: UDPPacket) -> None:
async def bind(self, addr):
self._check_closed()
if self._binding is not None:
- _fake_error(errno.EINVAL)
+ _fake_err(errno.EINVAL)
await trio.lowlevel.checkpoint()
ip_str, port = await self._resolve_address_nocp(addr, local=True)
ip = ipaddress.ip_address(ip_str)
From 6c009d26d2b62d7a6f357af4adac0785ae6f450a Mon Sep 17 00:00:00 2001
From: Spencer Brown
Date: Fri, 18 Aug 2023 08:38:08 +1000
Subject: [PATCH 4/9] This is a StapledStream
---
trio/_tests/test_highlevel_serve_listeners.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/trio/_tests/test_highlevel_serve_listeners.py b/trio/_tests/test_highlevel_serve_listeners.py
index 67e2eecbc8..652223ad9b 100644
--- a/trio/_tests/test_highlevel_serve_listeners.py
+++ b/trio/_tests/test_highlevel_serve_listeners.py
@@ -12,7 +12,7 @@
class MemoryListener(trio.abc.Listener):
closed = attr.ib(default=False)
accepted_streams = attr.ib(factory=list)
- queued_streams = attr.ib(factory=(lambda: trio.open_memory_channel[object](1)))
+ queued_streams = attr.ib(factory=(lambda: trio.open_memory_channel[trio.StapledStream](1)))
accept_hook = attr.ib(default=None)
async def connect(self):
From d6afea2d0f8be329ba75a157f85af02564b1a934 Mon Sep 17 00:00:00 2001
From: Spencer Brown
Date: Fri, 18 Aug 2023 08:40:57 +1000
Subject: [PATCH 5/9] Specify generic type for ContextVar
---
trio/_tests/test_contextvars.py | 13 +++++++------
trio/_tests/test_threads.py | 3 ++-
2 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/trio/_tests/test_contextvars.py b/trio/_tests/test_contextvars.py
index 0ff13435cf..15d1839f3c 100644
--- a/trio/_tests/test_contextvars.py
+++ b/trio/_tests/test_contextvars.py
@@ -1,17 +1,18 @@
+from __future__ import annotations
import contextvars
from .. import _core
-trio_testing_contextvar: contextvars.ContextVar = contextvars.ContextVar(
+trio_testing_contextvar: contextvars.ContextVar[str] = contextvars.ContextVar(
"trio_testing_contextvar"
)
-async def test_contextvars_default():
+async def test_contextvars_default() -> None:
trio_testing_contextvar.set("main")
- record = []
+ record: list[str] = []
- async def child():
+ async def child() -> None:
value = trio_testing_contextvar.get()
record.append(value)
@@ -20,7 +21,7 @@ async def child():
assert record == ["main"]
-async def test_contextvars_set():
+async def test_contextvars_set() -> None:
trio_testing_contextvar.set("main")
record = []
@@ -36,7 +37,7 @@ async def child():
assert value == "main"
-async def test_contextvars_copy():
+async def test_contextvars_copy() -> None:
trio_testing_contextvar.set("main")
context = contextvars.copy_context()
trio_testing_contextvar.set("second_main")
diff --git a/trio/_tests/test_threads.py b/trio/_tests/test_threads.py
index 9149f43037..6983de772c 100644
--- a/trio/_tests/test_threads.py
+++ b/trio/_tests/test_threads.py
@@ -1,3 +1,4 @@
+from __future__ import annotations
import contextvars
import queue as stdlib_queue
import re
@@ -584,7 +585,7 @@ async def async_fn(): # pragma: no cover
await to_thread_run_sync(async_fn)
-trio_test_contextvar: contextvars.ContextVar = contextvars.ContextVar(
+trio_test_contextvar: contextvars.ContextVar[str] = contextvars.ContextVar(
"trio_test_contextvar"
)
From f3a508b400d8609a1f7c39148b96e47de2fba14e Mon Sep 17 00:00:00 2001
From: Spencer Brown
Date: Fri, 18 Aug 2023 09:20:42 +1000
Subject: [PATCH 6/9] Run linters
---
trio/_tests/test_contextvars.py | 1 +
trio/_tests/test_highlevel_serve_listeners.py | 4 +++-
trio/_tests/test_threads.py | 1 +
trio/testing/_fake_net.py | 6 +++---
4 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/trio/_tests/test_contextvars.py b/trio/_tests/test_contextvars.py
index 15d1839f3c..2eed4be762 100644
--- a/trio/_tests/test_contextvars.py
+++ b/trio/_tests/test_contextvars.py
@@ -1,4 +1,5 @@
from __future__ import annotations
+
import contextvars
from .. import _core
diff --git a/trio/_tests/test_highlevel_serve_listeners.py b/trio/_tests/test_highlevel_serve_listeners.py
index 652223ad9b..65804f4222 100644
--- a/trio/_tests/test_highlevel_serve_listeners.py
+++ b/trio/_tests/test_highlevel_serve_listeners.py
@@ -12,7 +12,9 @@
class MemoryListener(trio.abc.Listener):
closed = attr.ib(default=False)
accepted_streams = attr.ib(factory=list)
- queued_streams = attr.ib(factory=(lambda: trio.open_memory_channel[trio.StapledStream](1)))
+ queued_streams = attr.ib(
+ factory=(lambda: trio.open_memory_channel[trio.StapledStream](1))
+ )
accept_hook = attr.ib(default=None)
async def connect(self):
diff --git a/trio/_tests/test_threads.py b/trio/_tests/test_threads.py
index 6983de772c..a2988ed0ff 100644
--- a/trio/_tests/test_threads.py
+++ b/trio/_tests/test_threads.py
@@ -1,4 +1,5 @@
from __future__ import annotations
+
import contextvars
import queue as stdlib_queue
import re
diff --git a/trio/testing/_fake_net.py b/trio/testing/_fake_net.py
index a5cb97b282..ddf46174f3 100644
--- a/trio/testing/_fake_net.py
+++ b/trio/testing/_fake_net.py
@@ -193,9 +193,9 @@ def __init__(self, fake_net: FakeNet, family: int, type: int, proto: int):
self._closed = False
- self._packet_sender, self._packet_receiver = trio.open_memory_channel[UDPPacket](
- float("inf")
- )
+ self._packet_sender, self._packet_receiver = trio.open_memory_channel[
+ UDPPacket
+ ](float("inf"))
# This is the source-of-truth for what port etc. this socket is bound to
self._binding: Optional[UDPBinding] = None
From e2347a2e4e7568140d23728f8f773672fe6ed82c Mon Sep 17 00:00:00 2001
From: Spencer Brown
Date: Fri, 18 Aug 2023 09:21:25 +1000
Subject: [PATCH 7/9] Test double-binding FakeNet sockets, for coverage
---
trio/_tests/test_fakenet.py | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/trio/_tests/test_fakenet.py b/trio/_tests/test_fakenet.py
index bc691c9db5..d250a105a3 100644
--- a/trio/_tests/test_fakenet.py
+++ b/trio/_tests/test_fakenet.py
@@ -1,16 +1,18 @@
+import errno
+
import pytest
import trio
from trio.testing._fake_net import FakeNet
-def fn():
+def fn() -> FakeNet:
fn = FakeNet()
fn.enable()
return fn
-async def test_basic_udp():
+async def test_basic_udp() -> None:
fn()
s1 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)
s2 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)
@@ -19,6 +21,11 @@ async def test_basic_udp():
ip, port = s1.getsockname()
assert ip == "127.0.0.1"
assert port != 0
+
+ with pytest.raises(OSError) as exc: # Cannot rebind.
+ await s1.bind(("192.0.2.1", 0))
+ assert exc.value.errno == errno.EINVAL
+
await s2.sendto(b"xyz", s1.getsockname())
data, addr = await s1.recvfrom(10)
assert data == b"xyz"
@@ -29,7 +36,7 @@ async def test_basic_udp():
assert addr == s1.getsockname()
-async def test_msg_trunc():
+async def test_msg_trunc() -> None:
fn()
s1 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)
s2 = trio.socket.socket(type=trio.socket.SOCK_DGRAM)
@@ -38,7 +45,7 @@ async def test_msg_trunc():
data, addr = await s1.recvfrom(10)
-async def test_basic_tcp():
+async def test_basic_tcp() -> None:
fn()
with pytest.raises(NotImplementedError):
trio.socket.socket()
From 01f7f150b8a3856716df6facd196977f2069b8af Mon Sep 17 00:00:00 2001
From: Spencer Brown
Date: Fri, 18 Aug 2023 10:45:05 +1000
Subject: [PATCH 8/9] Apply suggestions from CoolCat
---
trio/_core/_tests/test_io.py | 2 +-
trio/_tests/test_contextvars.py | 8 ++++----
trio/_tests/test_exports.py | 9 +++++----
3 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/trio/_core/_tests/test_io.py b/trio/_core/_tests/test_io.py
index 2205c83976..65b9b82bcf 100644
--- a/trio/_core/_tests/test_io.py
+++ b/trio/_core/_tests/test_io.py
@@ -2,8 +2,8 @@
import random
import socket as stdlib_socket
+from collections.abc import Callable
from contextlib import suppress
-from typing import Callable
import pytest
diff --git a/trio/_tests/test_contextvars.py b/trio/_tests/test_contextvars.py
index 2eed4be762..ae0c25f876 100644
--- a/trio/_tests/test_contextvars.py
+++ b/trio/_tests/test_contextvars.py
@@ -24,9 +24,9 @@ async def child() -> None:
async def test_contextvars_set() -> None:
trio_testing_contextvar.set("main")
- record = []
+ record: list[str] = []
- async def child():
+ async def child() -> None:
trio_testing_contextvar.set("child")
value = trio_testing_contextvar.get()
record.append(value)
@@ -42,9 +42,9 @@ async def test_contextvars_copy() -> None:
trio_testing_contextvar.set("main")
context = contextvars.copy_context()
trio_testing_contextvar.set("second_main")
- record = []
+ record: list[str] = []
- async def child():
+ async def child() -> None:
value = trio_testing_contextvar.get()
record.append(value)
diff --git a/trio/_tests/test_exports.py b/trio/_tests/test_exports.py
index 1a145f397e..2f1157db06 100644
--- a/trio/_tests/test_exports.py
+++ b/trio/_tests/test_exports.py
@@ -1,3 +1,4 @@
+from __future__ import annotations # isort: split
import __future__ # Regular import, not special!
import enum
@@ -9,7 +10,7 @@
import sys
from pathlib import Path
from types import ModuleType
-from typing import Dict, Protocol
+from typing import Protocol
import attrs
import pytest
@@ -303,7 +304,7 @@ def lookup_symbol(symbol):
with mod_cache.open() as f:
return json.loads(f.read())["names"][name]
- errors: Dict[str, object] = {}
+ errors: dict[str, object] = {}
for class_name, class_ in module.__dict__.items():
if not isinstance(class_, type):
continue
@@ -483,7 +484,7 @@ def lookup_symbol(symbol):
assert not errors
-def test_classes_are_final():
+def test_classes_are_final() -> None:
for module in PUBLIC_MODULES:
for name, class_ in module.__dict__.items():
if not isinstance(class_, type):
@@ -505,7 +506,7 @@ def test_classes_are_final():
continue
# These are classes that are conceptually abstract, but
# inspect.isabstract returns False for boring reasons.
- if class_ in (trio.abc.Instrument, trio.socket.SocketType):
+ if class_ is trio.abc.Instrument or class_ is trio.socket.SocketType:
continue
# Enums have their own metaclass, so we can't use our metaclasses.
# And I don't think there's a lot of risk from people subclassing
From cb6214e403db5dcf2566abe461539e10cf0697ef Mon Sep 17 00:00:00 2001
From: Spencer Brown
Date: Fri, 18 Aug 2023 10:55:54 +1000
Subject: [PATCH 9/9] Import MultiError directly to bypass deprecation warnings
---
.../_tests/test_multierror_scripts/apport_excepthook.py | 6 +++---
.../_tests/test_multierror_scripts/ipython_custom_exc.py | 6 +++---
.../_tests/test_multierror_scripts/simple_excepthook.py | 6 +++---
3 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/trio/_core/_tests/test_multierror_scripts/apport_excepthook.py b/trio/_core/_tests/test_multierror_scripts/apport_excepthook.py
index e51b8cdca0..0e46f37e17 100644
--- a/trio/_core/_tests/test_multierror_scripts/apport_excepthook.py
+++ b/trio/_core/_tests/test_multierror_scripts/apport_excepthook.py
@@ -3,13 +3,13 @@
# make sure it's on sys.path.
import sys
-import _common
+import _common # isort: split
sys.path.append("/usr/lib/python3/dist-packages")
import apport_python_hook
apport_python_hook.install()
-import trio
+from trio._core._multierror import MultiError # Bypass deprecation warnings
-raise trio.MultiError([KeyError("key_error"), ValueError("value_error")]) # type: ignore[attr-defined]
+raise MultiError([KeyError("key_error"), ValueError("value_error")])
diff --git a/trio/_core/_tests/test_multierror_scripts/ipython_custom_exc.py b/trio/_core/_tests/test_multierror_scripts/ipython_custom_exc.py
index c8086d3a0e..7ccb341dc9 100644
--- a/trio/_core/_tests/test_multierror_scripts/ipython_custom_exc.py
+++ b/trio/_core/_tests/test_multierror_scripts/ipython_custom_exc.py
@@ -3,7 +3,7 @@
# about it.
import sys
-import _common
+import _common # isort: split
def custom_excepthook(*args):
@@ -29,8 +29,8 @@ def custom_exc_hook(etype, value, tb, tb_offset=None):
ip.set_custom_exc((SomeError,), custom_exc_hook)
-import trio
+from trio._core._multierror import MultiError # Bypass deprecation warnings.
# The custom excepthook should run, because Trio was polite and didn't
# override it
-raise trio.MultiError([ValueError(), KeyError()]) # type: ignore[attr-defined]
+raise MultiError([ValueError(), KeyError()])
diff --git a/trio/_core/_tests/test_multierror_scripts/simple_excepthook.py b/trio/_core/_tests/test_multierror_scripts/simple_excepthook.py
index c2297df400..65371107bc 100644
--- a/trio/_core/_tests/test_multierror_scripts/simple_excepthook.py
+++ b/trio/_core/_tests/test_multierror_scripts/simple_excepthook.py
@@ -1,6 +1,6 @@
-import _common
+import _common # isort: split
-import trio
+from trio._core._multierror import MultiError # Bypass deprecation warnings
def exc1_fn():
@@ -18,4 +18,4 @@ def exc2_fn():
# This should be printed nicely, because Trio overrode sys.excepthook
-raise trio.MultiError([exc1_fn(), exc2_fn()]) # type: ignore[attr-defined]
+raise MultiError([exc1_fn(), exc2_fn()])