diff --git a/docs-requirements.txt b/docs-requirements.txt index 1bf42a9239..a6f9148063 100644 --- a/docs-requirements.txt +++ b/docs-requirements.txt @@ -10,7 +10,7 @@ attrs==23.1.0 # via # -r docs-requirements.in # outcome -babel==2.12.1 +babel==2.13.0 # via sphinx certifi==2023.7.22 # via requests @@ -53,9 +53,9 @@ jinja2==3.1.2 # towncrier markupsafe==2.1.3 # via jinja2 -outcome==1.2.0 +outcome==1.3.0 # via -r docs-requirements.in -packaging==23.1 +packaging==23.2 # via sphinx pycparser==2.21 # via cffi @@ -103,7 +103,7 @@ tomli==2.0.1 # via towncrier towncrier==23.6.0 # via -r docs-requirements.in -urllib3==2.0.5 +urllib3==2.0.7 # via requests zipp==3.17.0 # via diff --git a/docs/source/conf.py b/docs/source/conf.py index b85974a901..06b661f126 100755 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -70,8 +70,6 @@ # aliasing doesn't actually fix the warning for types.FrameType, but displaying # "types.FrameType" is more helpful than just "frame" "FrameType": "types.FrameType", - # unaliasing these makes intersphinx able to resolve them - "Outcome": "outcome.Outcome", "Context": "OpenSSL.SSL.Context", } @@ -82,8 +80,6 @@ def autodoc_process_signature( """Modify found signatures to fix various issues.""" if signature is not None: signature = signature.replace("~_contextvars.Context", "~contextvars.Context") - if name == "trio.lowlevel.start_guest_run": - signature = signature.replace("Outcome", "~outcome.Outcome") if name == "trio.lowlevel.RunVar": # Typevar is not useful here. signature = signature.replace(": ~trio._core._local.T", "") if "_NoValue" in signature: diff --git a/test-requirements.txt b/test-requirements.txt index dd2caf58dc..b5d2c0a4d5 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -6,7 +6,7 @@ # astor==0.8.1 # via -r test-requirements.in -astroid==2.15.8 +astroid==3.0.1 # via pylint async-generator==1.10 # via -r test-requirements.in @@ -14,7 +14,7 @@ attrs==23.1.0 # via # -r test-requirements.in # outcome -black==23.9.1 ; implementation_name == "cpython" +black==23.10.0 ; implementation_name == "cpython" # via -r test-requirements.in build==1.0.3 # via pip-tools @@ -24,9 +24,9 @@ click==8.1.7 # via # black # pip-tools -codespell==2.2.5 +codespell==2.2.6 # via -r test-requirements.in -coverage==7.3.1 +coverage==7.3.2 # via -r test-requirements.in cryptography==41.0.4 # via @@ -50,13 +50,11 @@ iniconfig==2.0.0 # via pytest isort==5.12.0 # via pylint -jedi==0.19.0 +jedi==0.19.1 # via -r test-requirements.in -lazy-object-proxy==1.9.0 - # via astroid mccabe==0.7.0 # via pylint -mypy==1.5.1 ; implementation_name == "cpython" +mypy==1.6.0 ; implementation_name == "cpython" # via -r test-requirements.in mypy-extensions==1.0.0 ; implementation_name == "cpython" # via @@ -65,9 +63,9 @@ mypy-extensions==1.0.0 ; implementation_name == "cpython" # mypy nodeenv==1.8.0 # via pyright -outcome==1.2.0 +outcome==1.3.0 # via -r test-requirements.in -packaging==23.1 +packaging==23.2 # via # black # build @@ -78,7 +76,7 @@ pathspec==0.11.2 # via black pip-tools==7.3.0 # via -r test-requirements.in -platformdirs==3.10.0 +platformdirs==3.11.0 # via # black # pylint @@ -86,17 +84,17 @@ pluggy==1.3.0 # via pytest pycparser==2.21 # via cffi -pylint==2.17.7 +pylint==3.0.1 # via -r test-requirements.in pyopenssl==23.2.0 # via -r test-requirements.in pyproject-hooks==1.0.0 # via build -pyright==1.1.329 +pyright==1.1.331 # via -r test-requirements.in pytest==7.4.2 # via -r test-requirements.in -ruff==0.0.291 +ruff==0.1.0 # via -r test-requirements.in sniffio==1.3.0 # via -r test-requirements.in @@ -130,8 +128,6 @@ typing-extensions==4.8.0 # pylint wheel==0.41.2 # via pip-tools -wrapt==1.15.0 - # via astroid zipp==3.17.0 # via importlib-metadata diff --git a/trio/_core/_tests/test_run.py b/trio/_core/_tests/test_run.py index f67f83a4b8..25e1f65e0a 100644 --- a/trio/_core/_tests/test_run.py +++ b/trio/_core/_tests/test_run.py @@ -17,7 +17,7 @@ ) from contextlib import ExitStack, contextmanager from math import inf -from typing import NoReturn, TypeVar +from typing import Any, NoReturn, TypeVar, cast import outcome import pytest @@ -2121,12 +2121,13 @@ async def detachable_coroutine( nursery.start_soon(detachable_coroutine, outcome.Value(None), "I'm free!") # If we get here then Trio thinks the task has exited... but the coroutine - # is still iterable + # is still iterable. At that point anything can be sent into the coroutine, so the .coro type + # is wrong. assert pdco_outcome is None - assert not_none(task).coro.send("be free!") == "I'm free!" + assert not_none(task).coro.send(cast(Any, "be free!")) == "I'm free!" assert pdco_outcome == outcome.Value("be free!") with pytest.raises(StopIteration): - not_none(task).coro.send(None) + not_none(task).coro.send(cast(Any, None)) # Check the exception paths too task = None @@ -2139,7 +2140,7 @@ async def detachable_coroutine( assert not_none(task).coro.throw(throw_in) == "uh oh" assert pdco_outcome == outcome.Error(throw_in) with pytest.raises(StopIteration): - task.coro.send(None) + task.coro.send(cast(Any, None)) async def bad_detach() -> None: async with _core.open_nursery(): @@ -2190,9 +2191,9 @@ def abort_fn(_: _core.RaiseCancelT) -> _core.Abort: # pragma: no cover await wait_all_tasks_blocked() # Okay, it's detached. Here's our coroutine runner: - assert not_none(task).coro.send("not trio!") == 1 - assert not_none(task).coro.send(None) == 2 - assert not_none(task).coro.send(None) == "byebye" + assert not_none(task).coro.send(cast(Any, "not trio!")) == 1 + assert not_none(task).coro.send(cast(Any, None)) == 2 + assert not_none(task).coro.send(cast(Any, None)) == "byebye" # Now it's been reattached, and we can leave the nursery @@ -2222,7 +2223,7 @@ def abort_fn(_: _core.RaiseCancelT) -> _core.Abort: await wait_all_tasks_blocked() assert task is not None nursery.cancel_scope.cancel() - task.coro.send(None) + task.coro.send(cast(Any, None)) assert abort_fn_called diff --git a/trio/_core/_traps.py b/trio/_core/_traps.py index 760c46bc51..f08fd69090 100644 --- a/trio/_core/_traps.py +++ b/trio/_core/_traps.py @@ -11,7 +11,6 @@ from . import _run if TYPE_CHECKING: - from outcome import Outcome from typing_extensions import TypeAlias from ._run import Task @@ -182,10 +181,12 @@ def abort(inner_raise_cancel): # Not exported in the trio._core namespace, but imported directly by _run. @attr.s(frozen=True) class PermanentlyDetachCoroutineObject: - final_outcome: Outcome = attr.ib() + final_outcome: outcome.Outcome[Any] = attr.ib() -async def permanently_detach_coroutine_object(final_outcome: Outcome) -> Any: +async def permanently_detach_coroutine_object( + final_outcome: outcome.Outcome[Any], +) -> Any: """Permanently detach the current task from the Trio scheduler. Normally, a Trio task doesn't exit until its coroutine object exits. When diff --git a/trio/_file_io.py b/trio/_file_io.py index 4a5650b453..6f30f89dd8 100644 --- a/trio/_file_io.py +++ b/trio/_file_io.py @@ -430,7 +430,7 @@ async def open_file( @overload -async def open_file( +async def open_file( # type: ignore[misc] # Any usage matches builtins.open(). file: _OpenFile, mode: str, buffering: int = -1, diff --git a/trio/_path.py b/trio/_path.py index fb01420a75..219f706825 100644 --- a/trio/_path.py +++ b/trio/_path.py @@ -311,7 +311,7 @@ async def open( ... @overload - async def open( + async def open( # type: ignore[misc] # Any usage matches builtins.open(). self, mode: str, buffering: int = -1, diff --git a/trio/_socket.py b/trio/_socket.py index 540641a628..7f6d9b3581 100644 --- a/trio/_socket.py +++ b/trio/_socket.py @@ -694,7 +694,7 @@ def send(__self, __bytes: Buffer, __flags: int = 0) -> Awaitable[int]: @overload async def sendto( - self, __data: Buffer, __address: tuple[Any, ...] | str | Buffer + self, __data: Buffer, __address: tuple[object, ...] | str | Buffer ) -> int: ... @@ -703,7 +703,7 @@ async def sendto( self, __data: Buffer, __flags: int, - __address: tuple[Any, ...] | str | Buffer, + __address: tuple[object, ...] | str | Buffer, ) -> int: ... @@ -1167,13 +1167,13 @@ def send(__self, __bytes: Buffer, __flags: int = 0) -> Awaitable[int]: @overload async def sendto( - self, __data: Buffer, __address: tuple[Any, ...] | str | Buffer + self, __data: Buffer, __address: tuple[object, ...] | str | Buffer ) -> int: ... @overload async def sendto( - self, __data: Buffer, __flags: int, __address: tuple[Any, ...] | str | Buffer + self, __data: Buffer, __flags: int, __address: tuple[object, ...] | str | Buffer ) -> int: ... @@ -1184,8 +1184,12 @@ async def sendto(self, *args: Any) -> int: # and kwargs are not accepted args_list = list(args) args_list[-1] = await self._resolve_address_nocp(args[-1], local=False) + # args_list is Any, which isn't the signature of sendto(). + # We don't care about invalid types, sendto() will do the checking. return await self._nonblocking_helper( - _core.wait_writable, _stdlib_socket.socket.sendto, *args_list + _core.wait_writable, + _stdlib_socket.socket.sendto, # type: ignore[arg-type] + *args_list, ) ################################################################ diff --git a/trio/_ssl.py b/trio/_ssl.py index 21c4deabb3..81c98a2d3d 100644 --- a/trio/_ssl.py +++ b/trio/_ssl.py @@ -356,7 +356,7 @@ def __init__( self._incoming, self._outgoing, server_side=server_side, - server_hostname=server_hostname, # type: ignore[arg-type] # Typeshed bug, does accept bytes as well (typeshed#10590) + server_hostname=server_hostname, ) # Tracks whether we've already done the initial handshake self._handshook = _Once(self._do_handshake) diff --git a/trio/_tests/test_exports.py b/trio/_tests/test_exports.py index e756e8ad69..4838579fc3 100644 --- a/trio/_tests/test_exports.py +++ b/trio/_tests/test_exports.py @@ -364,6 +364,10 @@ def lookup_symbol(symbol): "__setstate__", "__slots__", "__weakref__", + # ignore errors about dunders inherited from stdlib that tools might + # not see + "__copy__", + "__deepcopy__", } # pypy seems to have some additional dunders that differ diff --git a/trio/_tests/tools/test_gen_exports.py b/trio/_tests/tools/test_gen_exports.py index b4e23916a0..ad337cbe20 100644 --- a/trio/_tests/tools/test_gen_exports.py +++ b/trio/_tests/tools/test_gen_exports.py @@ -24,7 +24,7 @@ ) SOURCE = '''from _run import _public -from somewhere import Thing +from collections import Counter class Test: @_public @@ -34,7 +34,7 @@ def public_func(self): @ignore_this @_public @another_decorator - async def public_async_func(self) -> Thing: + async def public_async_func(self) -> Counter: pass # no doc string def not_public(self): @@ -45,18 +45,18 @@ async def not_public_async(self): ''' IMPORT_1 = """\ -from somewhere import Thing +from collections import Counter """ IMPORT_2 = """\ -from somewhere import Thing +from collections import Counter import os """ IMPORT_3 = """\ from typing import TYPE_CHECKING if TYPE_CHECKING: - from somewhere import Thing + from collections import Counter """ @@ -90,7 +90,7 @@ def test_create_pass_through_args(): @skip_lints -@pytest.mark.parametrize("imports", ["", IMPORT_1, IMPORT_2, IMPORT_3]) +@pytest.mark.parametrize("imports", [IMPORT_1, IMPORT_2, IMPORT_3]) def test_process(tmp_path, imports): try: import black # noqa: F401 @@ -141,7 +141,7 @@ def test_run_black(tmp_path) -> None: @skip_lints def test_run_ruff(tmp_path) -> None: - """Test that processing properly fails if black does.""" + """Test that processing properly fails if ruff does.""" try: import ruff # noqa: F401 except ImportError as error: # pragma: no cover diff --git a/trio/_threads.py b/trio/_threads.py index 7649587751..cea1b24fa4 100644 --- a/trio/_threads.py +++ b/trio/_threads.py @@ -153,7 +153,9 @@ def unprotected_fn(self) -> RetT: return ret def run_sync(self) -> None: - result = outcome.capture(self.context.run, self.unprotected_fn) + # Two paramspecs + overload is a bit too hard for mypy to handle. Tell it what to infer. + runner: Callable[[Callable[[], RetT]], RetT] = self.context.run + result = outcome.capture(runner, self.unprotected_fn) self.queue.put_nowait(result) def run_in_host_task(self, token: TrioToken) -> None: @@ -285,7 +287,7 @@ def do_release_then_return_result() -> RetT: # replace the regular return value, and if the regular return was # already an exception then we want them to chain. try: - return result.unwrap() # type: ignore[no-any-return] # Until outcome is typed + return result.unwrap() finally: limiter.release_on_behalf_of(placeholder) @@ -365,7 +367,7 @@ def abort(raise_cancel: RaiseCancelT) -> trio.lowlevel.Abort: object ] = await trio.lowlevel.wait_task_rescheduled(abort) if isinstance(msg_from_thread, outcome.Outcome): - return msg_from_thread.unwrap() # type: ignore[no-any-return] + return msg_from_thread.unwrap() elif isinstance(msg_from_thread, Run): await msg_from_thread.run() elif isinstance(msg_from_thread, RunSync): @@ -496,7 +498,7 @@ def from_thread_run( else: message_to_trio.run_in_host_task(trio_token) - return message_to_trio.queue.get().unwrap() # type: ignore[no-any-return] + return message_to_trio.queue.get().unwrap() def from_thread_run_sync( @@ -544,4 +546,4 @@ def from_thread_run_sync( else: message_to_trio.run_in_host_task(trio_token) - return message_to_trio.queue.get().unwrap() # type: ignore[no-any-return] + return message_to_trio.queue.get().unwrap() diff --git a/trio/_tools/gen_exports.py b/trio/_tools/gen_exports.py index 153070bfc9..8ed99c9327 100755 --- a/trio/_tools/gen_exports.py +++ b/trio/_tools/gen_exports.py @@ -154,7 +154,8 @@ def run_ruff(file: File, source: str) -> tuple[bool, str]: sys.executable, "-m", "ruff", - "--fix-only", + "check", + "--fix", "--output-format=text", "--stdin-filename", file.path, @@ -165,7 +166,7 @@ def run_ruff(file: File, source: str) -> tuple[bool, str]: encoding="utf8", ) - if result.returncode != 0 or result.stderr: + if result.returncode != 0: return False, f"Failed to run ruff!\n{result.stderr}" return True, result.stdout