diff --git a/newsfragments/3511.bugfix.rst b/newsfragments/3511.bugfix.rst new file mode 100644 index 0000000000..3201e98898 --- /dev/null +++ b/newsfragments/3511.bugfix.rst @@ -0,0 +1,2 @@ +:func:`trio.sleep` now reports ``NaN`` under the name of its own parameter, raising +```seconds`` must not be NaN`` rather than ``deadline must not be NaN``. diff --git a/src/trio/_tests/test_timeouts.py b/src/trio/_tests/test_timeouts.py index fa974fcf21..d1cd992bda 100644 --- a/src/trio/_tests/test_timeouts.py +++ b/src/trio/_tests/test_timeouts.py @@ -1,5 +1,6 @@ from __future__ import annotations +import re import time from typing import TYPE_CHECKING, Protocol, TypeVar @@ -182,29 +183,25 @@ async def test_timeouts_raise_value_error() -> None: nan = float("nan") - for fun, val in ( - (sleep, -1), - (sleep, nan), - (sleep_until, nan), + # Each message names the parameter that function actually takes, so the + # patterns are exact rather than an alternation over both spellings. + for fun, val, message in ( + (sleep, -1, "`seconds` must be non-negative"), + (sleep, nan, "`seconds` must not be NaN"), + (sleep_until, nan, "deadline must not be NaN"), ): - with pytest.raises( - ValueError, - match=r"^(deadline|`seconds`) must (not )*be (non-negative|NaN)$", - ): + with pytest.raises(ValueError, match=rf"^{re.escape(message)}$"): await fun(val) - for cm, val in ( - (fail_after, -1), - (fail_after, nan), - (fail_at, nan), - (move_on_after, -1), - (move_on_after, nan), - (move_on_at, nan), + for cm, val, message in ( + (fail_after, -1, "`seconds` must be non-negative"), + (fail_after, nan, "`seconds` must not be NaN"), + (fail_at, nan, "deadline must not be NaN"), + (move_on_after, -1, "`seconds` must be non-negative"), + (move_on_after, nan, "`seconds` must not be NaN"), + (move_on_at, nan, "deadline must not be NaN"), ): - with pytest.raises( - ValueError, - match=r"^(deadline|`seconds`) must (not )*be (non-negative|NaN)$", - ): + with pytest.raises(ValueError, match=rf"^{re.escape(message)}$"): with cm(val): pass # pragma: no cover diff --git a/src/trio/_timeouts.py b/src/trio/_timeouts.py index d95cbe4cfc..8e25211586 100644 --- a/src/trio/_timeouts.py +++ b/src/trio/_timeouts.py @@ -103,8 +103,12 @@ async def sleep(seconds: float) -> None: ValueError: if *seconds* is negative or NaN. """ + # Duplicate validation logic, as in move_on_after, so the error names the + # parameter the caller passed rather than the deadline derived from it. if seconds < 0: raise ValueError("`seconds` must be non-negative") + if math.isnan(seconds): + raise ValueError("`seconds` must not be NaN") if seconds == 0: await trio.lowlevel.checkpoint() else: