From 550ea8b2f58c6cefc30c87f5ad6be8c4d7cb2f33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BC=98=E9=9B=85=E3=81=AE=E5=92=B8=E9=B1=BC?= <3129538298@qq.com> Date: Wed, 26 Aug 2026 17:52:04 +0800 Subject: [PATCH 1/2] fix: reject NaN/Inf --wait so the CLI cannot hang --- src/whoseport/waitarg.py | 18 +++++++++++++++++ tests/test_wait_finite.py | 41 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 src/whoseport/waitarg.py create mode 100644 tests/test_wait_finite.py diff --git a/src/whoseport/waitarg.py b/src/whoseport/waitarg.py new file mode 100644 index 0000000..d91b329 --- /dev/null +++ b/src/whoseport/waitarg.py @@ -0,0 +1,18 @@ +"""Guards for ``--wait`` so a typo cannot hang the CLI forever.""" + +from __future__ import annotations + +import math + + +def require_finite_wait(seconds: float) -> float: + """Return *seconds* if it is a finite, positive duration. + + ``argparse`` ``type=float`` accepts ``nan`` / ``inf``. ``nan <= 0`` is + false, so the old ``<= 0`` check let ``--wait nan`` through and + ``--wait inf`` never hit the timeout. + """ + value = float(seconds) + if not math.isfinite(value) or value <= 0: + raise ValueError("--wait needs a positive number of seconds") + return value diff --git a/tests/test_wait_finite.py b/tests/test_wait_finite.py new file mode 100644 index 0000000..70e045a --- /dev/null +++ b/tests/test_wait_finite.py @@ -0,0 +1,41 @@ +import math + +import pytest + +from whoseport import cli +from whoseport.waitarg import require_finite_wait + + +def test_require_finite_wait_accepts_positive(): + assert require_finite_wait(0.5) == 0.5 + assert require_finite_wait(30) == 30 + + +def test_require_finite_wait_rejects_non_positive_and_non_finite(): + for bad in (0, -1, float("nan"), float("inf"), float("-inf")): + with pytest.raises(ValueError, match="positive"): + require_finite_wait(bad) + + +def test_cli_wait_rejects_nan(): + with pytest.raises(SystemExit) as exc: + cli.main(["8080", "--wait", "nan"]) + assert exc.value.code == 2 + + +def test_cli_wait_rejects_inf(): + with pytest.raises(SystemExit) as exc: + cli.main(["8080", "--wait", "inf"]) + assert exc.value.code == 2 + + +def test_cli_wait_rejects_negative_inf(): + with pytest.raises(SystemExit) as exc: + cli.main(["8080", "--wait", "-inf"]) + assert exc.value.code == 2 + + +def test_math_nan_is_not_less_or_equal_zero(): + # documents why the old `<= 0` guard was not enough + assert not (float("nan") <= 0) + assert math.isnan(float("nan")) From 970383e47fbab354dfa54d1e24f33caeaa494876 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BC=98=E9=9B=85=E3=81=AE=E5=92=B8=E9=B1=BC?= <3129538298@qq.com> Date: Wed, 26 Aug 2026 17:55:46 +0800 Subject: [PATCH 2/2] Install finite --wait guard from package init --- src/whoseport/__init__.py | 3 +++ src/whoseport/waitarg.py | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/whoseport/__init__.py b/src/whoseport/__init__.py index 22c8b8e..00cdff4 100644 --- a/src/whoseport/__init__.py +++ b/src/whoseport/__init__.py @@ -5,7 +5,10 @@ """ from whoseport.core import PortUser, UnsupportedPlatformError, collect +from whoseport.waitarg import install as _install_waitarg __version__ = "0.2.0" +_install_waitarg() + __all__ = ["PortUser", "UnsupportedPlatformError", "__version__", "collect"] diff --git a/src/whoseport/waitarg.py b/src/whoseport/waitarg.py index d91b329..114bf12 100644 --- a/src/whoseport/waitarg.py +++ b/src/whoseport/waitarg.py @@ -16,3 +16,27 @@ def require_finite_wait(seconds: float) -> float: if not math.isfinite(value) or value <= 0: raise ValueError("--wait needs a positive number of seconds") return value + + +def _wait_type(text: str) -> float: + return require_finite_wait(float(text)) + + +def install() -> None: + """Patch ``cli.build_parser`` so ``--wait`` rejects NaN/Inf.""" + import whoseport.cli as cli + + if getattr(cli, "_waitarg_installed", False): + return + + orig = cli.build_parser + + def build_parser(): + parser = orig() + for action in parser._actions: + if "--wait" in getattr(action, "option_strings", ()): + action.type = _wait_type + return parser + + cli.build_parser = build_parser + cli._waitarg_installed = True