From a97f905edd2f0d36c715313d12b879f036e3b554 Mon Sep 17 00:00:00 2001 From: CoolCat467 <52022020+CoolCat467@users.noreply.github.com> Date: Sun, 24 Aug 2025 19:38:11 -0500 Subject: [PATCH 01/13] Define `trio.Event.__bool__()` to reduce bugs Closes #3238 --- src/trio/_sync.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/trio/_sync.py b/src/trio/_sync.py index ca373922b0..6f46245e31 100644 --- a/src/trio/_sync.py +++ b/src/trio/_sync.py @@ -1,7 +1,7 @@ from __future__ import annotations import math -from typing import TYPE_CHECKING, Protocol +from typing import TYPE_CHECKING, NoReturn, Protocol import attrs @@ -112,6 +112,14 @@ def statistics(self) -> EventStatistics: """ return EventStatistics(tasks_waiting=len(self._tasks)) + if not TYPE_CHECKING: + + def __bool__(self) -> NoReturn: + """Raise NotImplementedError.""" + raise NotImplementedError( + "Trio events cannot be treated as bools; consider using 'event.is_set()'" + ) + class _HasAcquireRelease(Protocol): """Only classes with acquire() and release() can use the mixin's implementations.""" From 24a8f08bf8eb3245b0d2611054bb9c61d06c1da3 Mon Sep 17 00:00:00 2001 From: CoolCat467 <52022020+CoolCat467@users.noreply.github.com> Date: Sun, 24 Aug 2025 20:46:05 -0500 Subject: [PATCH 02/13] Don't do if type checking block, jedi doesn't like that --- src/trio/_sync.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/trio/_sync.py b/src/trio/_sync.py index 6f46245e31..e5c5725c4d 100644 --- a/src/trio/_sync.py +++ b/src/trio/_sync.py @@ -112,13 +112,11 @@ def statistics(self) -> EventStatistics: """ return EventStatistics(tasks_waiting=len(self._tasks)) - if not TYPE_CHECKING: - - def __bool__(self) -> NoReturn: - """Raise NotImplementedError.""" - raise NotImplementedError( - "Trio events cannot be treated as bools; consider using 'event.is_set()'" - ) + def __bool__(self) -> NoReturn: + """Raise NotImplementedError.""" + raise NotImplementedError( + "Trio events cannot be treated as bools; consider using 'event.is_set()'" + ) class _HasAcquireRelease(Protocol): From 1ea6615510616b4f0b447b93dedd670c526f7e94 Mon Sep 17 00:00:00 2001 From: CoolCat467 <52022020+CoolCat467@users.noreply.github.com> Date: Sun, 24 Aug 2025 22:10:19 -0500 Subject: [PATCH 03/13] Switch to deprecated warning --- src/trio/_sync.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/trio/_sync.py b/src/trio/_sync.py index e5c5725c4d..e95ae05374 100644 --- a/src/trio/_sync.py +++ b/src/trio/_sync.py @@ -1,7 +1,7 @@ from __future__ import annotations import math -from typing import TYPE_CHECKING, NoReturn, Protocol +from typing import TYPE_CHECKING, Protocol import attrs @@ -16,6 +16,7 @@ enable_ki_protection, remove_parking_lot_breaker, ) +from ._depreciate import warn_deprecated from ._util import final if TYPE_CHECKING: @@ -112,11 +113,16 @@ def statistics(self) -> EventStatistics: """ return EventStatistics(tasks_waiting=len(self._tasks)) - def __bool__(self) -> NoReturn: - """Raise NotImplementedError.""" - raise NotImplementedError( - "Trio events cannot be treated as bools; consider using 'event.is_set()'" + def __bool__(self) -> True: + """Return True and raise warning.""" + warn_deprecated( + self.__bool__, + "0.30.1", + issue=3238, + instead=self.is_set, + use_triodeprecationwarning=True, ) + return True class _HasAcquireRelease(Protocol): From 68a4fad041414dc89dea264cea044a45d04d597f Mon Sep 17 00:00:00 2001 From: CoolCat467 <52022020+CoolCat467@users.noreply.github.com> Date: Mon, 25 Aug 2025 09:25:15 -0500 Subject: [PATCH 04/13] Don't use trio's deprecated warning, add test, add newsfragment --- newsfragments/3322.deprecated.txt | 1 + src/trio/_sync.py | 3 +-- src/trio/_tests/test_sync.py | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 newsfragments/3322.deprecated.txt diff --git a/newsfragments/3322.deprecated.txt b/newsfragments/3322.deprecated.txt new file mode 100644 index 0000000000..ca920129e2 --- /dev/null +++ b/newsfragments/3322.deprecated.txt @@ -0,0 +1 @@ +Implement `trio.Event.__bool__` and have it raise a `DeprecationWarning` and tell users to use `trio.Event.is_set` instead. Would be caught earlier with `mypy --enable-error-code=truthy-bool`, this is for users who don't use mypy. diff --git a/src/trio/_sync.py b/src/trio/_sync.py index e95ae05374..8aac8528b1 100644 --- a/src/trio/_sync.py +++ b/src/trio/_sync.py @@ -16,7 +16,7 @@ enable_ki_protection, remove_parking_lot_breaker, ) -from ._depreciate import warn_deprecated +from ._deprecate import warn_deprecated from ._util import final if TYPE_CHECKING: @@ -120,7 +120,6 @@ def __bool__(self) -> True: "0.30.1", issue=3238, instead=self.is_set, - use_triodeprecationwarning=True, ) return True diff --git a/src/trio/_tests/test_sync.py b/src/trio/_tests/test_sync.py index 39f8d21f38..92655bbbc1 100644 --- a/src/trio/_tests/test_sync.py +++ b/src/trio/_tests/test_sync.py @@ -23,6 +23,12 @@ async def test_Event() -> None: assert not e.is_set() assert e.statistics().tasks_waiting == 0 + with pytest.warns( + DeprecationWarning, + match=r"trio\.Event\.__bool__ is deprecated since Trio 0\.30\.1; use trio\.Event\.is_set instead \(https://github.com/python-trio/trio/issues/3238\)", + ): + e.__bool__() + e.set() assert e.is_set() with assert_checkpoints(): From 93950f21531fb98766726529bcba6fb9bce9514d Mon Sep 17 00:00:00 2001 From: A5rocks Date: Tue, 26 Aug 2025 02:04:13 +0900 Subject: [PATCH 05/13] Apply suggestions from code review --- newsfragments/3322.deprecated.txt | 2 +- src/trio/_sync.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/newsfragments/3322.deprecated.txt b/newsfragments/3322.deprecated.txt index ca920129e2..0e1b69f32b 100644 --- a/newsfragments/3322.deprecated.txt +++ b/newsfragments/3322.deprecated.txt @@ -1 +1 @@ -Implement `trio.Event.__bool__` and have it raise a `DeprecationWarning` and tell users to use `trio.Event.is_set` instead. Would be caught earlier with `mypy --enable-error-code=truthy-bool`, this is for users who don't use mypy. +Implement ``bool(trio.Event)`` and have it raise a `DeprecationWarning` and tell users to use `trio.Event.is_set` instead. This is an alternative to ``mypy --enable-error-code=truthy-bool`` for users who don't use type checking. diff --git a/src/trio/_sync.py b/src/trio/_sync.py index 8aac8528b1..2463188588 100644 --- a/src/trio/_sync.py +++ b/src/trio/_sync.py @@ -113,7 +113,7 @@ def statistics(self) -> EventStatistics: """ return EventStatistics(tasks_waiting=len(self._tasks)) - def __bool__(self) -> True: + def __bool__(self) -> Literal[True]: """Return True and raise warning.""" warn_deprecated( self.__bool__, From 68e7242b9da090b3077edf6db4a4837fdde317d5 Mon Sep 17 00:00:00 2001 From: A5rocks Date: Tue, 26 Aug 2025 02:08:23 +0900 Subject: [PATCH 06/13] Add necessary imports --- src/trio/_sync.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/trio/_sync.py b/src/trio/_sync.py index 2463188588..4058349167 100644 --- a/src/trio/_sync.py +++ b/src/trio/_sync.py @@ -1,7 +1,7 @@ from __future__ import annotations import math -from typing import TYPE_CHECKING, Protocol +from typing import TYPE_CHECKING, Protocol, Literal import attrs From 14e16fea4b9e47f69fec48ca2081bbe1c7d5bd32 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 25 Aug 2025 17:08:39 +0000 Subject: [PATCH 07/13] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/trio/_sync.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/trio/_sync.py b/src/trio/_sync.py index 4058349167..89e49d6187 100644 --- a/src/trio/_sync.py +++ b/src/trio/_sync.py @@ -1,7 +1,7 @@ from __future__ import annotations import math -from typing import TYPE_CHECKING, Protocol, Literal +from typing import TYPE_CHECKING, Literal, Protocol import attrs From 3b14df291c3cfb3ab6c8b32b221da92cd4491492 Mon Sep 17 00:00:00 2001 From: CoolCat467 <52022020+CoolCat467@users.noreply.github.com> Date: Mon, 25 Aug 2025 17:05:28 -0500 Subject: [PATCH 08/13] Add `if not TYPE_CHECKING` --- src/trio/_sync.py | 20 +++++++++++--------- src/trio/_tests/test_sync.py | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/trio/_sync.py b/src/trio/_sync.py index 89e49d6187..eddf0094a1 100644 --- a/src/trio/_sync.py +++ b/src/trio/_sync.py @@ -113,15 +113,17 @@ def statistics(self) -> EventStatistics: """ return EventStatistics(tasks_waiting=len(self._tasks)) - def __bool__(self) -> Literal[True]: - """Return True and raise warning.""" - warn_deprecated( - self.__bool__, - "0.30.1", - issue=3238, - instead=self.is_set, - ) - return True + if not TYPE_CHECKING: + + def __bool__(self) -> Literal[True]: + """Return True and raise warning.""" + warn_deprecated( + self.__bool__, + "0.31.0", + issue=3238, + instead=self.is_set, + ) + return True class _HasAcquireRelease(Protocol): diff --git a/src/trio/_tests/test_sync.py b/src/trio/_tests/test_sync.py index 92655bbbc1..6096510d3f 100644 --- a/src/trio/_tests/test_sync.py +++ b/src/trio/_tests/test_sync.py @@ -25,7 +25,7 @@ async def test_Event() -> None: with pytest.warns( DeprecationWarning, - match=r"trio\.Event\.__bool__ is deprecated since Trio 0\.30\.1; use trio\.Event\.is_set instead \(https://github.com/python-trio/trio/issues/3238\)", + match=r"trio\.Event\.__bool__ is deprecated since Trio 0\.31\.0; use trio\.Event\.is_set instead \(https://github.com/python-trio/trio/issues/3238\)", ): e.__bool__() From 4347db2244465558857d7d085560b6bf6b971c79 Mon Sep 17 00:00:00 2001 From: CoolCat467 <52022020+CoolCat467@users.noreply.github.com> Date: Mon, 25 Aug 2025 17:09:24 -0500 Subject: [PATCH 09/13] Revert and use `typing_extensions.deprecated` instead --- pyproject.toml | 2 ++ src/trio/_sync.py | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index bec87cfef0..8241677bcd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,6 +50,8 @@ dependencies = [ # cffi is required on Windows, except on PyPy where it is built-in "cffi>=1.14; os_name == 'nt' and implementation_name != 'pypy'", "exceptiongroup; python_version < '3.11'", + # using typing_extensions.depreciated in _sync.py + "typing_extensions >= 4.15.0", ] dynamic = ["version"] diff --git a/src/trio/_sync.py b/src/trio/_sync.py index eddf0094a1..ec698992b3 100644 --- a/src/trio/_sync.py +++ b/src/trio/_sync.py @@ -4,6 +4,7 @@ from typing import TYPE_CHECKING, Literal, Protocol import attrs +from typing_extensions import deprecated import trio @@ -113,17 +114,16 @@ def statistics(self) -> EventStatistics: """ return EventStatistics(tasks_waiting=len(self._tasks)) - if not TYPE_CHECKING: - - def __bool__(self) -> Literal[True]: - """Return True and raise warning.""" - warn_deprecated( - self.__bool__, - "0.31.0", - issue=3238, - instead=self.is_set, - ) - return True + @deprecated + def __bool__(self) -> Literal[True]: + """Return True and raise warning.""" + warn_deprecated( + self.__bool__, + "0.31.0", + issue=3238, + instead=self.is_set, + ) + return True class _HasAcquireRelease(Protocol): From 2a723ffacdb67c5f699064d446e65c57d82a8ae7 Mon Sep 17 00:00:00 2001 From: CoolCat467 <52022020+CoolCat467@users.noreply.github.com> Date: Mon, 25 Aug 2025 17:23:55 -0500 Subject: [PATCH 10/13] Add `typing_extensions` to requirements --- test-requirements.in | 1 + test-requirements.txt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/test-requirements.in b/test-requirements.in index 90776153c4..1c33b6522c 100644 --- a/test-requirements.in +++ b/test-requirements.in @@ -8,6 +8,7 @@ trustme # for the ssl + DTLS tests pylint # for pylint finding all symbols tests jedi; implementation_name == "cpython" # for jedi code completion tests cryptography>=41.0.0 # cryptography<41 segfaults on pypy3.10 +typing_extensions >= 4.15.0 # Tools black; implementation_name == "cpython" diff --git a/test-requirements.txt b/test-requirements.txt index 3d12554ce6..c11c47e7af 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -187,7 +187,7 @@ types-pyyaml==6.0.12.20250822 # via -r test-requirements.in types-setuptools==80.9.0.20250822 # via types-cffi -typing-extensions==4.14.1 +typing-extensions==4.15.0 # via # -r test-requirements.in # astroid From e34ce248d6d1713a1986d13c93f2f49dda457228 Mon Sep 17 00:00:00 2001 From: CoolCat467 <52022020+CoolCat467@users.noreply.github.com> Date: Mon, 25 Aug 2025 17:29:56 -0500 Subject: [PATCH 11/13] Add message properly --- src/trio/_sync.py | 5 ++++- test-requirements.in | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/trio/_sync.py b/src/trio/_sync.py index ec698992b3..0744f2cea7 100644 --- a/src/trio/_sync.py +++ b/src/trio/_sync.py @@ -114,7 +114,10 @@ def statistics(self) -> EventStatistics: """ return EventStatistics(tasks_waiting=len(self._tasks)) - @deprecated + @deprecated( + "trio.Event.__bool__ is deprecated since Trio 0.31.0, use trio.Event.is_set instead (https://github.com/python-trio/trio/issues/3238)", + stacklevel=2, + ) def __bool__(self) -> Literal[True]: """Return True and raise warning.""" warn_deprecated( diff --git a/test-requirements.in b/test-requirements.in index 1c33b6522c..21ae962140 100644 --- a/test-requirements.in +++ b/test-requirements.in @@ -8,7 +8,6 @@ trustme # for the ssl + DTLS tests pylint # for pylint finding all symbols tests jedi; implementation_name == "cpython" # for jedi code completion tests cryptography>=41.0.0 # cryptography<41 segfaults on pypy3.10 -typing_extensions >= 4.15.0 # Tools black; implementation_name == "cpython" @@ -40,3 +39,4 @@ outcome sniffio # 1.2.1 fixes types exceptiongroup >= 1.2.1; python_version < "3.11" +typing_extensions >= 4.15.0 From c3a0a762c343e5e4876725da18714bc79f38314f Mon Sep 17 00:00:00 2001 From: CoolCat467 <52022020+CoolCat467@users.noreply.github.com> Date: Mon, 25 Aug 2025 17:40:22 -0500 Subject: [PATCH 12/13] `,` -> `;` --- src/trio/_sync.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/trio/_sync.py b/src/trio/_sync.py index 0744f2cea7..03d2c0cc4a 100644 --- a/src/trio/_sync.py +++ b/src/trio/_sync.py @@ -115,7 +115,7 @@ def statistics(self) -> EventStatistics: return EventStatistics(tasks_waiting=len(self._tasks)) @deprecated( - "trio.Event.__bool__ is deprecated since Trio 0.31.0, use trio.Event.is_set instead (https://github.com/python-trio/trio/issues/3238)", + "trio.Event.__bool__ is deprecated since Trio 0.31.0; use trio.Event.is_set instead (https://github.com/python-trio/trio/issues/3238)", stacklevel=2, ) def __bool__(self) -> Literal[True]: From 02c3794913a04a030f1f2ea76fb67b69abd90c3c Mon Sep 17 00:00:00 2001 From: CoolCat467 <52022020+CoolCat467@users.noreply.github.com> Date: Mon, 25 Aug 2025 20:29:00 -0500 Subject: [PATCH 13/13] Remove `typing_extensions` requirement --- pyproject.toml | 2 -- src/trio/_sync.py | 20 ++++++++++++++++++-- test-requirements.in | 1 - 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8241677bcd..bec87cfef0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,8 +50,6 @@ dependencies = [ # cffi is required on Windows, except on PyPy where it is built-in "cffi>=1.14; os_name == 'nt' and implementation_name != 'pypy'", "exceptiongroup; python_version < '3.11'", - # using typing_extensions.depreciated in _sync.py - "typing_extensions >= 4.15.0", ] dynamic = ["version"] diff --git a/src/trio/_sync.py b/src/trio/_sync.py index 03d2c0cc4a..d026f4bc37 100644 --- a/src/trio/_sync.py +++ b/src/trio/_sync.py @@ -1,10 +1,9 @@ from __future__ import annotations import math -from typing import TYPE_CHECKING, Literal, Protocol +from typing import TYPE_CHECKING, Literal, Protocol, TypeVar import attrs -from typing_extensions import deprecated import trio @@ -21,10 +20,27 @@ from ._util import final if TYPE_CHECKING: + from collections.abc import Callable from types import TracebackType + from typing_extensions import deprecated + from ._core import Task from ._core._parking_lot import ParkingLotStatistics +else: + T = TypeVar("T") + + def deprecated( + message: str, + /, + *, + category: type[Warning] | None = DeprecationWarning, + stacklevel: int = 1, + ) -> Callable[[T], T]: + def wrapper(f: T) -> T: + return f + + return wrapper @attrs.frozen diff --git a/test-requirements.in b/test-requirements.in index 21ae962140..90776153c4 100644 --- a/test-requirements.in +++ b/test-requirements.in @@ -39,4 +39,3 @@ outcome sniffio # 1.2.1 fixes types exceptiongroup >= 1.2.1; python_version < "3.11" -typing_extensions >= 4.15.0