From dbc3d6b7e8b08224297dd0352b00cfbd949bc60b Mon Sep 17 00:00:00 2001 From: jakkdl Date: Mon, 26 Jun 2023 14:08:36 +0200 Subject: [PATCH 01/11] Add --full-output-file to check_type_completeness.py for use when developing --- trio/_tests/check_type_completeness.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/trio/_tests/check_type_completeness.py b/trio/_tests/check_type_completeness.py index d67d11958e..417c06af6e 100755 --- a/trio/_tests/check_type_completeness.py +++ b/trio/_tests/check_type_completeness.py @@ -75,6 +75,11 @@ def main(args: argparse.Namespace) -> int: if res.stderr: print(res.stderr) + if args.full_output_file is not None: + with open(args.full_output_file, "w") as file: + print(f"Writing full output to {args.full_output_file}") + json.dump(current_result, file, sort_keys=True, indent=2) + last_result = json.loads(RESULT_FILE.read_text()) for key in "errorCount", "warningCount", "informationCount": @@ -153,6 +158,7 @@ def main(args: argparse.Namespace) -> int: parser = argparse.ArgumentParser() parser.add_argument("--overwrite-file", action="store_true", default=False) +parser.add_argument("--full-output-file", type=Path, default=None) args = parser.parse_args() assert __name__ == "__main__", "This script should be run standalone" From 42a45cfdccceb55aec2e2bc2a1daa4e68c8dc3fb Mon Sep 17 00:00:00 2001 From: jakkdl Date: Mon, 26 Jun 2023 14:12:05 +0200 Subject: [PATCH 02/11] _timeouts.py is now type complete --- trio/_tests/verify_types.json | 10 +++------- trio/_timeouts.py | 15 ++++++++------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/trio/_tests/verify_types.json b/trio/_tests/verify_types.json index 7b0c39d20d..569cba20dd 100644 --- a/trio/_tests/verify_types.json +++ b/trio/_tests/verify_types.json @@ -7,11 +7,11 @@ "warningCount": 0 }, "typeCompleteness": { - "completenessScore": 0.8155339805825242, + "completenessScore": 0.8220064724919094, "exportedSymbolCounts": { "withAmbiguousType": 1, - "withKnownType": 504, - "withUnknownType": 113 + "withKnownType": 508, + "withUnknownType": 109 }, "ignoreUnknownTypesFromImports": true, "missingClassDocStringCount": 1, @@ -77,10 +77,6 @@ "trio._core._run.Nursery.__del__", "trio.move_on_at", "trio.move_on_after", - "trio.sleep_forever", - "trio.sleep_until", - "trio.sleep", - "trio.fail_after", "trio._sync.Event", "trio._sync.Event.is_set", "trio._sync.Event.wait", diff --git a/trio/_timeouts.py b/trio/_timeouts.py index ad31e78404..911eb16a56 100644 --- a/trio/_timeouts.py +++ b/trio/_timeouts.py @@ -1,10 +1,11 @@ import math from contextlib import contextmanager +from typing import Iterator, ContextManager import trio -def move_on_at(deadline): +def move_on_at(deadline: float) -> trio.CancelScope: """Use as a context manager to create a cancel scope with the given absolute deadline. @@ -20,7 +21,7 @@ def move_on_at(deadline): return trio.CancelScope(deadline=deadline) -def move_on_after(seconds): +def move_on_after(seconds: float) -> trio.CancelScope: """Use as a context manager to create a cancel scope whose deadline is set to now + *seconds*. @@ -36,7 +37,7 @@ def move_on_after(seconds): return move_on_at(trio.current_time() + seconds) -async def sleep_forever(): +async def sleep_forever() -> None: """Pause execution of the current task forever (or until cancelled). Equivalent to calling ``await sleep(math.inf)``. @@ -45,7 +46,7 @@ async def sleep_forever(): await trio.lowlevel.wait_task_rescheduled(lambda _: trio.lowlevel.Abort.SUCCEEDED) -async def sleep_until(deadline): +async def sleep_until(deadline: float) -> None: """Pause execution of the current task until the given time. The difference between :func:`sleep` and :func:`sleep_until` is that the @@ -65,7 +66,7 @@ async def sleep_until(deadline): await sleep_forever() -async def sleep(seconds): +async def sleep(seconds: float) -> None: """Pause execution of the current task for the given number of seconds. Args: @@ -92,7 +93,7 @@ class TooSlowError(Exception): @contextmanager -def fail_at(deadline): +def fail_at(deadline: float) -> Iterator[trio.CancelScope]: """Creates a cancel scope with the given deadline, and raises an error if it is actually cancelled. @@ -120,7 +121,7 @@ def fail_at(deadline): raise TooSlowError -def fail_after(seconds): +def fail_after(seconds: float) -> ContextManager[trio.CancelScope]: """Creates a cancel scope with the given timeout, and raises an error if it is actually cancelled. From 2b9185fe961a33d2c652ef61ec55be1cd79582bc Mon Sep 17 00:00:00 2001 From: jakkdl Date: Mon, 26 Jun 2023 14:29:33 +0200 Subject: [PATCH 03/11] trio/_core and trio/_tests now type complete --- trio/_core/_exceptions.py | 2 +- trio/_tests/verify_types.json | 29 +++++------------------------ trio/_util.py | 7 +++++-- 3 files changed, 11 insertions(+), 27 deletions(-) diff --git a/trio/_core/_exceptions.py b/trio/_core/_exceptions.py index 6189c484b4..3d5c1d831d 100644 --- a/trio/_core/_exceptions.py +++ b/trio/_core/_exceptions.py @@ -61,7 +61,7 @@ class Cancelled(BaseException, metaclass=NoPublicConstructor): """ - def __str__(self): + def __str__(self) -> str: return "Cancelled" diff --git a/trio/_tests/verify_types.json b/trio/_tests/verify_types.json index 569cba20dd..2023b59885 100644 --- a/trio/_tests/verify_types.json +++ b/trio/_tests/verify_types.json @@ -7,11 +7,11 @@ "warningCount": 0 }, "typeCompleteness": { - "completenessScore": 0.8220064724919094, + "completenessScore": 0.8252427184466019, "exportedSymbolCounts": { "withAmbiguousType": 1, - "withKnownType": 508, - "withUnknownType": 109 + "withKnownType": 510, + "withUnknownType": 107 }, "ignoreUnknownTypesFromImports": true, "missingClassDocStringCount": 1, @@ -46,19 +46,13 @@ ], "otherSymbolCounts": { "withAmbiguousType": 15, - "withKnownType": 231, - "withUnknownType": 236 + "withKnownType": 238, + "withUnknownType": 229 }, "packageName": "trio", "symbols": [ - "trio._core._exceptions.Cancelled", - "trio._core._exceptions.Cancelled.__str__", - "trio._util.NoPublicConstructor", - "trio._util.NoPublicConstructor.__call__", - "trio._util.Final.__new__", "trio.run", "trio.open_nursery", - "trio._core._run.CancelScope", "trio._core._run.CancelScope.cancelled_caught", "trio._core._run.CancelScope.__exit__", "trio._core._run.CancelScope.__repr__", @@ -68,7 +62,6 @@ "trio._core._run._TaskStatusIgnored.__repr__", "trio._core._run._TaskStatusIgnored.started", "trio.current_time", - "trio._core._run.Nursery", "trio._core._run.Nursery.__init__", "trio._core._run.Nursery.child_tasks", "trio._core._run.Nursery.parent_task", @@ -77,18 +70,15 @@ "trio._core._run.Nursery.__del__", "trio.move_on_at", "trio.move_on_after", - "trio._sync.Event", "trio._sync.Event.is_set", "trio._sync.Event.wait", "trio._sync.Event.statistics", - "trio._sync.CapacityLimiter", "trio._sync.CapacityLimiter.__init__", "trio._sync.CapacityLimiter.__repr__", "trio._sync.CapacityLimiter.total_tokens", "trio._sync.CapacityLimiter.borrowed_tokens", "trio._sync.CapacityLimiter.available_tokens", "trio._sync.CapacityLimiter.statistics", - "trio._sync.Semaphore", "trio._sync.Semaphore.__init__", "trio._sync.Semaphore.__repr__", "trio._sync.Semaphore.value", @@ -99,7 +89,6 @@ "trio._sync._LockImpl.locked", "trio._sync._LockImpl.statistics", "trio._sync.StrictFIFOLock", - "trio._sync.Condition", "trio._sync.Condition.__init__", "trio._sync.Condition.locked", "trio._sync.Condition.acquire_nowait", @@ -161,7 +150,6 @@ "trio._path.Path.__bytes__", "trio._path.Path.__truediv__", "trio._path.Path.__rtruediv__", - "trio._path.AsyncAutoWrapperType", "trio._path.AsyncAutoWrapperType.__init__", "trio._path.AsyncAutoWrapperType.generate_forwards", "trio._path.AsyncAutoWrapperType.generate_wraps", @@ -199,7 +187,6 @@ "trio._ssl.SSLListener.__init__", "trio._ssl.SSLListener.accept", "trio._ssl.SSLListener.aclose", - "trio._dtls.DTLSEndpoint", "trio._dtls.DTLSEndpoint.__init__", "trio._dtls.DTLSEndpoint.__del__", "trio._dtls.DTLSEndpoint.close", @@ -250,7 +237,6 @@ "trio.from_thread.run_sync", "trio.lowlevel.cancel_shielded_checkpoint", "trio.lowlevel.currently_ki_protected", - "trio._core._run.Task", "trio._core._run.Task.coro", "trio._core._run.Task.name", "trio._core._run.Task.context", @@ -262,13 +248,11 @@ "trio._core._run.Task.iter_await_frames", "trio.lowlevel.checkpoint", "trio.lowlevel.current_task", - "trio._core._parking_lot.ParkingLot", "trio._core._parking_lot.ParkingLot.__len__", "trio._core._parking_lot.ParkingLot.__bool__", "trio._core._parking_lot.ParkingLot.unpark_all", "trio._core._parking_lot.ParkingLot.repark_all", "trio._core._parking_lot.ParkingLot.statistics", - "trio._core._unbounded_queue.UnboundedQueue", "trio._core._unbounded_queue.UnboundedQueue.__repr__", "trio._core._unbounded_queue.UnboundedQueue.qsize", "trio._core._unbounded_queue.UnboundedQueue.empty", @@ -277,12 +261,10 @@ "trio._core._unbounded_queue.UnboundedQueue.statistics", "trio._core._unbounded_queue.UnboundedQueue.__aiter__", "trio._core._unbounded_queue.UnboundedQueue.__anext__", - "trio._core._local.RunVar", "trio._core._local.RunVar.get", "trio._core._local.RunVar.set", "trio._core._local.RunVar.reset", "trio._core._local.RunVar.__repr__", - "trio._core._entry_queue.TrioToken", "trio._core._entry_queue.TrioToken.run_sync_soon", "trio.lowlevel.current_trio_token", "trio.lowlevel.temporarily_detach_coroutine_object", @@ -325,7 +307,6 @@ "trio.testing.trio_test", "trio.testing.assert_checkpoints", "trio.testing.assert_no_checkpoints", - "trio.testing._sequencer.Sequencer", "trio.testing.check_one_way_stream", "trio.testing.check_two_way_stream", "trio.testing.check_half_closeable_stream", diff --git a/trio/_util.py b/trio/_util.py index b60e0104e8..bd8ee2c341 100644 --- a/trio/_util.py +++ b/trio/_util.py @@ -1,4 +1,5 @@ # Little utilities we use internally +from __future__ import annotations from abc import ABCMeta import os @@ -280,7 +281,9 @@ class SomeClass(metaclass=Final): - TypeError if a subclass is created """ - def __new__(cls, name, bases, cls_namespace): + def __new__( + cls, name: str, bases: tuple[type, ...], cls_namespace: dict[str, t.Any] + ) -> Final: for base in bases: if isinstance(base, Final): raise TypeError( @@ -312,7 +315,7 @@ class SomeClass(metaclass=NoPublicConstructor): - TypeError if a subclass or an instance is created. """ - def __call__(cls, *args, **kwargs): + def __call__(cls, *args: t.Any, **kwargs: t.Any) -> None: raise TypeError( f"{cls.__module__}.{cls.__qualname__} has no public constructor" ) From 0964edd76fc04282c66124ec7aacc0e9268b4713 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Mon, 26 Jun 2023 15:03:47 +0200 Subject: [PATCH 04/11] open_nursery, move_on_at, move_on_after and CancelScope now type complete --- trio/_core/_run.py | 44 +++++++++++++++----------- trio/_tests/check_type_completeness.py | 12 +++++-- trio/_tests/verify_types.json | 20 ++++-------- 3 files changed, 42 insertions(+), 34 deletions(-) diff --git a/trio/_core/_run.py b/trio/_core/_run.py index c07e29ab97..f73a7330da 100644 --- a/trio/_core/_run.py +++ b/trio/_core/_run.py @@ -16,7 +16,8 @@ from heapq import heapify, heappop, heappush from math import inf from time import perf_counter -from typing import TYPE_CHECKING, Any, NoReturn, TypeVar +from typing import TYPE_CHECKING, Any, NoReturn, TypeVar, AsyncContextManager, Iterator +from types import TracebackType import attr from outcome import Error, Outcome, Value, capture @@ -475,15 +476,15 @@ class CancelScope(metaclass=Final): has been entered yet, and changes take immediate effect. """ - _cancel_status = attr.ib(default=None, init=False) - _has_been_entered = attr.ib(default=False, init=False) - _registered_deadline = attr.ib(default=inf, init=False) - _cancel_called = attr.ib(default=False, init=False) - cancelled_caught = attr.ib(default=False, init=False) + _cancel_status: CancelStatus | None = attr.ib(default=None, init=False) + _has_been_entered: bool = attr.ib(default=False, init=False) + _registered_deadline: float = attr.ib(default=inf, init=False) + _cancel_called: bool = attr.ib(default=False, init=False) + cancelled_caught: bool = attr.ib(default=False, init=False) # Constructor arguments: - _deadline = attr.ib(default=inf, kw_only=True) - _shield = attr.ib(default=False, kw_only=True) + _deadline: float = attr.ib(default=inf, kw_only=True) + _shield: bool = attr.ib(default=False, kw_only=True) @enable_ki_protection def __enter__(self): @@ -573,7 +574,12 @@ def _close(self, exc): self._cancel_status = None return exc - def __exit__(self, etype, exc, tb): + def __exit__( + self, + etype: type[BaseException] | None, + exc: BaseException | None, + tb: TracebackType | None, + ) -> bool: # NB: NurseryManager calls _close() directly rather than __exit__(), # so __exit__() must be just _close() plus this logic for adapting # the exception-filtering result to the context manager API. @@ -607,7 +613,7 @@ def __exit__(self, etype, exc, tb): # TODO: check if PEP558 changes the need for this call # https://github.com/python/cpython/pull/3640 - def __repr__(self): + def __repr__(self) -> str: if self._cancel_status is not None: binding = "active" elif self._has_been_entered: @@ -634,7 +640,7 @@ def __repr__(self): @contextmanager @enable_ki_protection - def _might_change_registered_deadline(self): + def _might_change_registered_deadline(self) -> Iterator[None]: try: yield finally: @@ -658,7 +664,7 @@ def _might_change_registered_deadline(self): runner.force_guest_tick_asap() @property - def deadline(self): + def deadline(self) -> float: """Read-write, :class:`float`. An absolute time on the current run's clock at which this scope will automatically become cancelled. You can adjust the deadline by modifying this @@ -684,12 +690,12 @@ def deadline(self): return self._deadline @deadline.setter - def deadline(self, new_deadline): + def deadline(self, new_deadline: float) -> None: with self._might_change_registered_deadline(): self._deadline = float(new_deadline) @property - def shield(self): + def shield(self) -> bool: """Read-write, :class:`bool`, default :data:`False`. So long as this is set to :data:`True`, then the code inside this scope will not receive :exc:`~trio.Cancelled` exceptions from scopes @@ -714,7 +720,7 @@ def shield(self): @shield.setter @enable_ki_protection - def shield(self, new_value): + def shield(self, new_value: bool) -> None: if not isinstance(new_value, bool): raise TypeError("shield must be a bool") self._shield = new_value @@ -722,7 +728,7 @@ def shield(self, new_value): self._cancel_status.recalculate() @enable_ki_protection - def cancel(self): + def cancel(self) -> None: """Cancels this scope immediately. This method is idempotent, i.e., if the scope was already @@ -736,7 +742,7 @@ def cancel(self): self._cancel_status.recalculate() @property - def cancel_called(self): + def cancel_called(self) -> bool: """Readonly :class:`bool`. Records whether cancellation has been requested for this scope, either by an explicit call to :meth:`cancel` or by the deadline expiring. @@ -890,7 +896,9 @@ def __exit__(self): # pragma: no cover assert False, """Never called, but should be defined""" -def open_nursery(strict_exception_groups=None): +def open_nursery( + strict_exception_groups: bool | None = None, +) -> AsyncContextManager[Nursery]: """Returns an async context manager which must be used to create a new `Nursery`. diff --git a/trio/_tests/check_type_completeness.py b/trio/_tests/check_type_completeness.py index 417c06af6e..65d1383b91 100755 --- a/trio/_tests/check_type_completeness.py +++ b/trio/_tests/check_type_completeness.py @@ -77,8 +77,16 @@ def main(args: argparse.Namespace) -> int: if args.full_output_file is not None: with open(args.full_output_file, "w") as file: - print(f"Writing full output to {args.full_output_file}") - json.dump(current_result, file, sort_keys=True, indent=2) + json.dump( + [ + sym + for sym in current_result["typeCompleteness"]["symbols"] + if sym["diagnostics"] + ], + file, + sort_keys=True, + indent=2, + ) last_result = json.loads(RESULT_FILE.read_text()) diff --git a/trio/_tests/verify_types.json b/trio/_tests/verify_types.json index 2023b59885..e54af12444 100644 --- a/trio/_tests/verify_types.json +++ b/trio/_tests/verify_types.json @@ -7,11 +7,11 @@ "warningCount": 0 }, "typeCompleteness": { - "completenessScore": 0.8252427184466019, + "completenessScore": 0.8317152103559871, "exportedSymbolCounts": { "withAmbiguousType": 1, - "withKnownType": 510, - "withUnknownType": 107 + "withKnownType": 514, + "withUnknownType": 103 }, "ignoreUnknownTypesFromImports": true, "missingClassDocStringCount": 1, @@ -45,19 +45,13 @@ } ], "otherSymbolCounts": { - "withAmbiguousType": 15, - "withKnownType": 238, - "withUnknownType": 229 + "withAmbiguousType": 14, + "withKnownType": 244, + "withUnknownType": 224 }, "packageName": "trio", "symbols": [ "trio.run", - "trio.open_nursery", - "trio._core._run.CancelScope.cancelled_caught", - "trio._core._run.CancelScope.__exit__", - "trio._core._run.CancelScope.__repr__", - "trio._core._run.CancelScope.deadline", - "trio._core._run.CancelScope.cancel_called", "trio.current_effective_deadline", "trio._core._run._TaskStatusIgnored.__repr__", "trio._core._run._TaskStatusIgnored.started", @@ -68,8 +62,6 @@ "trio._core._run.Nursery.start_soon", "trio._core._run.Nursery.start", "trio._core._run.Nursery.__del__", - "trio.move_on_at", - "trio.move_on_after", "trio._sync.Event.is_set", "trio._sync.Event.wait", "trio._sync.Event.statistics", From 0ffbb368e5130645f98b37ac3e69a8a7ca836e16 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Mon, 26 Jun 2023 15:09:40 +0200 Subject: [PATCH 05/11] rename --full-output-file to --full-diagnostics-file --- trio/_tests/check_type_completeness.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/trio/_tests/check_type_completeness.py b/trio/_tests/check_type_completeness.py index 65d1383b91..15b2da2d94 100755 --- a/trio/_tests/check_type_completeness.py +++ b/trio/_tests/check_type_completeness.py @@ -75,8 +75,8 @@ def main(args: argparse.Namespace) -> int: if res.stderr: print(res.stderr) - if args.full_output_file is not None: - with open(args.full_output_file, "w") as file: + if args.full_diagnostics_file is not None: + with open(args.full_diagnostics_file, "w") as file: json.dump( [ sym @@ -166,7 +166,7 @@ def main(args: argparse.Namespace) -> int: parser = argparse.ArgumentParser() parser.add_argument("--overwrite-file", action="store_true", default=False) -parser.add_argument("--full-output-file", type=Path, default=None) +parser.add_argument("--full-diagnostics-file", type=Path, default=None) args = parser.parse_args() assert __name__ == "__main__", "This script should be run standalone" From c75db152d9cc539f0aec531df9307aef56473420 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Mon, 26 Jun 2023 18:10:29 +0200 Subject: [PATCH 06/11] Any -> object --- trio/_util.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/trio/_util.py b/trio/_util.py index bd8ee2c341..89a2dea7de 100644 --- a/trio/_util.py +++ b/trio/_util.py @@ -282,7 +282,7 @@ class SomeClass(metaclass=Final): """ def __new__( - cls, name: str, bases: tuple[type, ...], cls_namespace: dict[str, t.Any] + cls, name: str, bases: tuple[type, ...], cls_namespace: dict[str, object] ) -> Final: for base in bases: if isinstance(base, Final): @@ -315,12 +315,12 @@ class SomeClass(metaclass=NoPublicConstructor): - TypeError if a subclass or an instance is created. """ - def __call__(cls, *args: t.Any, **kwargs: t.Any) -> None: + def __call__(cls, *args: object, **kwargs: object) -> None: raise TypeError( f"{cls.__module__}.{cls.__qualname__} has no public constructor" ) - def _create(cls: t.Type[T], *args: t.Any, **kwargs: t.Any) -> T: + def _create(cls: t.Type[T], *args: object, **kwargs: object) -> T: return super().__call__(*args, **kwargs) # type: ignore From 61bbadb15a996510688065c304499e01ccee9423 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Mon, 26 Jun 2023 18:14:02 +0200 Subject: [PATCH 07/11] import Iterator from collections.abc --- trio/_core/_run.py | 3 ++- trio/_timeouts.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/trio/_core/_run.py b/trio/_core/_run.py index f73a7330da..58ee1b4543 100644 --- a/trio/_core/_run.py +++ b/trio/_core/_run.py @@ -16,8 +16,9 @@ from heapq import heapify, heappop, heappush from math import inf from time import perf_counter -from typing import TYPE_CHECKING, Any, NoReturn, TypeVar, AsyncContextManager, Iterator +from typing import TYPE_CHECKING, Any, NoReturn, TypeVar, AsyncContextManager from types import TracebackType +from collections.abc import Iterator import attr from outcome import Error, Outcome, Value, capture diff --git a/trio/_timeouts.py b/trio/_timeouts.py index 911eb16a56..b9fbbe38b0 100644 --- a/trio/_timeouts.py +++ b/trio/_timeouts.py @@ -1,6 +1,7 @@ import math from contextlib import contextmanager -from typing import Iterator, ContextManager +from typing import ContextManager +from collections.abc import Iterator import trio From 88fecfc8a035d4eecea9a1eacc062caf36d43826 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Mon, 26 Jun 2023 18:18:23 +0200 Subject: [PATCH 08/11] typing.[Async]ContextManager -> contextlib.Abstract[Async]ContextManager --- trio/_core/_run.py | 5 +++-- trio/_timeouts.py | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/trio/_core/_run.py b/trio/_core/_run.py index 58ee1b4543..2727fe1e89 100644 --- a/trio/_core/_run.py +++ b/trio/_core/_run.py @@ -16,9 +16,10 @@ from heapq import heapify, heappop, heappush from math import inf from time import perf_counter -from typing import TYPE_CHECKING, Any, NoReturn, TypeVar, AsyncContextManager +from typing import TYPE_CHECKING, Any, NoReturn, TypeVar from types import TracebackType from collections.abc import Iterator +from contextlib import AbstractAsyncContextManager import attr from outcome import Error, Outcome, Value, capture @@ -899,7 +900,7 @@ def __exit__(self): # pragma: no cover def open_nursery( strict_exception_groups: bool | None = None, -) -> AsyncContextManager[Nursery]: +) -> AbstractAsyncContextManager[Nursery]: """Returns an async context manager which must be used to create a new `Nursery`. diff --git a/trio/_timeouts.py b/trio/_timeouts.py index b9fbbe38b0..5cc0d48475 100644 --- a/trio/_timeouts.py +++ b/trio/_timeouts.py @@ -1,6 +1,6 @@ +from __future__ import annotations import math -from contextlib import contextmanager -from typing import ContextManager +from contextlib import contextmanager, AbstractContextManager from collections.abc import Iterator import trio @@ -122,7 +122,7 @@ def fail_at(deadline: float) -> Iterator[trio.CancelScope]: raise TooSlowError -def fail_after(seconds: float) -> ContextManager[trio.CancelScope]: +def fail_after(seconds: float) -> AbstractContextManager[trio.CancelScope]: """Creates a cancel scope with the given timeout, and raises an error if it is actually cancelled. From fb21df861e31030fc5b1fda04265ef8c0174eade Mon Sep 17 00:00:00 2001 From: jakkdl Date: Sat, 1 Jul 2023 12:20:12 +0200 Subject: [PATCH 09/11] workaround for PyCharm --- trio/_timeouts.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/trio/_timeouts.py b/trio/_timeouts.py index 5cc0d48475..738f2652d7 100644 --- a/trio/_timeouts.py +++ b/trio/_timeouts.py @@ -1,7 +1,9 @@ from __future__ import annotations + import math -from contextlib import contextmanager, AbstractContextManager from collections.abc import Iterator +from contextlib import AbstractContextManager, contextmanager +from typing import TYPE_CHECKING import trio @@ -94,7 +96,7 @@ class TooSlowError(Exception): @contextmanager -def fail_at(deadline: float) -> Iterator[trio.CancelScope]: +def _fail_at(deadline: float) -> Iterator[trio.CancelScope]: """Creates a cancel scope with the given deadline, and raises an error if it is actually cancelled. @@ -122,6 +124,17 @@ def fail_at(deadline: float) -> Iterator[trio.CancelScope]: raise TooSlowError +# workaround for PyCharm not being able to infer return type from @contextmanager +# see https://youtrack.jetbrains.com/issue/PY-36444/PyCharm-doesnt-infer-types-when-using-contextlib.contextmanager-decorator +if TYPE_CHECKING: + + def fail_at(deadline: float) -> AbstractContextManager[trio.CancelScope]: + return _fail_at(deadline) + +else: + fail_at = _fail_at + + def fail_after(seconds: float) -> AbstractContextManager[trio.CancelScope]: """Creates a cancel scope with the given timeout, and raises an error if it is actually cancelled. From 5195433ca57e50fde741d6090ba49a6494ca4b26 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Sat, 1 Jul 2023 12:46:18 +0200 Subject: [PATCH 10/11] different workaround --- trio/_timeouts.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/trio/_timeouts.py b/trio/_timeouts.py index 738f2652d7..44dfc7be0f 100644 --- a/trio/_timeouts.py +++ b/trio/_timeouts.py @@ -95,8 +95,9 @@ class TooSlowError(Exception): """ -@contextmanager -def _fail_at(deadline: float) -> Iterator[trio.CancelScope]: +# workaround for PyCharm not being able to infer return type from @contextmanager +# see https://youtrack.jetbrains.com/issue/PY-36444/PyCharm-doesnt-infer-types-when-using-contextlib.contextmanager-decorator +def fail_at(deadline: float) -> AbstractContextManager[trio.CancelScope]: # type: ignore[misc] """Creates a cancel scope with the given deadline, and raises an error if it is actually cancelled. @@ -117,22 +118,14 @@ def _fail_at(deadline: float) -> Iterator[trio.CancelScope]: ValueError: if deadline is NaN. """ - with move_on_at(deadline) as scope: yield scope if scope.cancelled_caught: raise TooSlowError -# workaround for PyCharm not being able to infer return type from @contextmanager -# see https://youtrack.jetbrains.com/issue/PY-36444/PyCharm-doesnt-infer-types-when-using-contextlib.contextmanager-decorator -if TYPE_CHECKING: - - def fail_at(deadline: float) -> AbstractContextManager[trio.CancelScope]: - return _fail_at(deadline) - -else: - fail_at = _fail_at +if not TYPE_CHECKING: + fail_at = contextmanager(fail_at) def fail_after(seconds: float) -> AbstractContextManager[trio.CancelScope]: From 2773e5475866ef107223348df2eed3a6438475eb Mon Sep 17 00:00:00 2001 From: jakkdl Date: Mon, 3 Jul 2023 13:37:04 +0200 Subject: [PATCH 11/11] fix f401 unused import --- trio/_timeouts.py | 1 - 1 file changed, 1 deletion(-) diff --git a/trio/_timeouts.py b/trio/_timeouts.py index 44dfc7be0f..1d03b2f2e3 100644 --- a/trio/_timeouts.py +++ b/trio/_timeouts.py @@ -1,7 +1,6 @@ from __future__ import annotations import math -from collections.abc import Iterator from contextlib import AbstractContextManager, contextmanager from typing import TYPE_CHECKING