From ef54ef336e145802aa47272118d908a8c497932a Mon Sep 17 00:00:00 2001 From: A5rocks Date: Tue, 3 Mar 2026 00:27:42 -0500 Subject: [PATCH 1/3] Disallow absolute deadlines for relative cancel scopes --- newsfragments/3403.deprecated.rst | 3 +++ src/trio/_core/_run.py | 20 ++++++-------------- src/trio/_tests/test_timeouts.py | 22 +++++++++------------- 3 files changed, 18 insertions(+), 27 deletions(-) create mode 100644 newsfragments/3403.deprecated.rst diff --git a/newsfragments/3403.deprecated.rst b/newsfragments/3403.deprecated.rst new file mode 100644 index 0000000000..300736582b --- /dev/null +++ b/newsfragments/3403.deprecated.rst @@ -0,0 +1,3 @@ +Trying to use absolute deadlines on a `trio.CancelScope` constructed +with a relative deadline now raises, after being deprecated since Trio +0.27.0. diff --git a/src/trio/_core/_run.py b/src/trio/_core/_run.py index 2d649d2917..c731fe59eb 100644 --- a/src/trio/_core/_run.py +++ b/src/trio/_core/_run.py @@ -808,13 +808,9 @@ def deadline(self) -> float: """ if self._relative_deadline != inf: assert self._deadline == inf - warnings.warn( - DeprecationWarning( - "unentered relative cancel scope does not have an absolute deadline. Use `.relative_deadline`", - ), - stacklevel=2, + raise RuntimeError( + "Unentered relative cancel scope does not have an absolute deadline." ) - return current_time() + self._relative_deadline return self._deadline @deadline.setter @@ -823,13 +819,9 @@ def deadline(self, new_deadline: float) -> None: raise ValueError("deadline must not be NaN") if self._relative_deadline != inf: assert self._deadline == inf - warnings.warn( - DeprecationWarning( - "unentered relative cancel scope does not have an absolute deadline. Transforming into an absolute cancel scope. First set `.relative_deadline = math.inf` if you do want an absolute cancel scope.", - ), - stacklevel=2, + raise RuntimeError( + "Unentered relative cancel scope does not have an absolute deadline." ) - self._relative_deadline = inf with self._might_change_registered_deadline(): self._deadline = float(new_deadline) @@ -852,7 +844,7 @@ def relative_deadline(self) -> float: elif self._deadline != inf: assert self._relative_deadline == inf raise RuntimeError( - "unentered non-relative cancel scope does not have a relative deadline", + "Unentered non-relative cancel scope does not have a relative deadline", ) return self._relative_deadline @@ -868,7 +860,7 @@ def relative_deadline(self, new_relative_deadline: float) -> None: elif self._deadline != inf: assert self._relative_deadline == inf raise RuntimeError( - "unentered non-relative cancel scope does not have a relative deadline", + "Unentered non-relative cancel scope does not have a relative deadline", ) else: self._relative_deadline = new_relative_deadline diff --git a/src/trio/_tests/test_timeouts.py b/src/trio/_tests/test_timeouts.py index ff62a0708f..39c619f0de 100644 --- a/src/trio/_tests/test_timeouts.py +++ b/src/trio/_tests/test_timeouts.py @@ -246,26 +246,22 @@ async def test_timeout_deadline_on_entry(mock_clock: _core.MockClock) -> None: async def test_invalid_access_unentered(mock_clock: _core.MockClock) -> None: cs = move_on_after(5) mock_clock.jump(3) - start = _core.current_time() - match_str = "^unentered relative cancel scope does not have an absolute deadline" - with pytest.warns(DeprecationWarning, match=match_str): - assert cs.deadline == start + 5 - mock_clock.jump(1) - # this is hella sketchy, but they *have* been warned - with pytest.warns(DeprecationWarning, match=match_str): - assert cs.deadline == start + 6 + match_str = "^Unentered relative cancel scope does not have an absolute deadline" + with pytest.raises(RuntimeError, match=match_str): + print("SHOULD NOT PRINT! deadline:", cs.deadline) - with pytest.warns(DeprecationWarning, match=match_str): + with pytest.raises(RuntimeError, match=match_str): cs.deadline = 7 - # now transformed into absolute - assert cs.deadline == 7 - assert not cs.is_relative + + # nothing happened! + assert cs.relative_deadline == 5 + assert cs.is_relative cs = move_on_at(5) match_str = ( - "^unentered non-relative cancel scope does not have a relative deadline$" + "^Unentered non-relative cancel scope does not have a relative deadline$" ) with pytest.raises(RuntimeError, match=match_str): assert cs.relative_deadline From 980db2b79d41f6fae86d924a67521f110a15487d Mon Sep 17 00:00:00 2001 From: A5rocks Date: Thu, 12 Mar 2026 06:04:42 +0900 Subject: [PATCH 2/3] PR feedback --- newsfragments/3403.deprecated.rst | 3 +++ src/trio/_tests/test_timeouts.py | 5 ++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/newsfragments/3403.deprecated.rst b/newsfragments/3403.deprecated.rst index 300736582b..d13abb1eaf 100644 --- a/newsfragments/3403.deprecated.rst +++ b/newsfragments/3403.deprecated.rst @@ -1,3 +1,6 @@ Trying to use absolute deadlines on a `trio.CancelScope` constructed with a relative deadline now raises, after being deprecated since Trio 0.27.0. + +To move over, replace the `trio.CancelScope` object instead of mutating +it. diff --git a/src/trio/_tests/test_timeouts.py b/src/trio/_tests/test_timeouts.py index 39c619f0de..13388022c3 100644 --- a/src/trio/_tests/test_timeouts.py +++ b/src/trio/_tests/test_timeouts.py @@ -243,13 +243,12 @@ async def test_timeout_deadline_on_entry(mock_clock: _core.MockClock) -> None: assert rcs is cs -async def test_invalid_access_unentered(mock_clock: _core.MockClock) -> None: +async def test_invalid_access_unentered() -> None: cs = move_on_after(5) - mock_clock.jump(3) match_str = "^Unentered relative cancel scope does not have an absolute deadline" with pytest.raises(RuntimeError, match=match_str): - print("SHOULD NOT PRINT! deadline:", cs.deadline) + assert cs.deadline with pytest.raises(RuntimeError, match=match_str): cs.deadline = 7 From b99abbd4a07f5b0f8105edbd3008191c717ecf4e Mon Sep 17 00:00:00 2001 From: A5rocks Date: Wed, 18 Mar 2026 07:43:37 +0900 Subject: [PATCH 3/3] Update changelog a bit more --- newsfragments/3403.deprecated.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/newsfragments/3403.deprecated.rst b/newsfragments/3403.deprecated.rst index d13abb1eaf..7ce024db63 100644 --- a/newsfragments/3403.deprecated.rst +++ b/newsfragments/3403.deprecated.rst @@ -2,5 +2,5 @@ Trying to use absolute deadlines on a `trio.CancelScope` constructed with a relative deadline now raises, after being deprecated since Trio 0.27.0. -To move over, replace the `trio.CancelScope` object instead of mutating -it. +To switch a cancel scope from being relative to absolute, replace the +`trio.CancelScope` object instead of trying to mutate it.