diff --git a/CHANGELOG.md b/CHANGELOG.md index 51f84145c..416901633 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## Unreleased + +### Fixed +- `Interval.range()` now raises `ValueError` for a non-positive `amount` instead of silently producing an infinite (amount <= 0... zero repeats the start forever, negative runs away from the end) or unbounded generator [#1080](https://github.com/python-pendulum/pendulum/issues/1080) + ## [3.3.0] - 2026-10-12 ### Added diff --git a/src/pendulum/interval.py b/src/pendulum/interval.py index c5e0713a7..3bd90b760 100644 --- a/src/pendulum/interval.py +++ b/src/pendulum/interval.py @@ -288,6 +288,10 @@ def in_words(self, locale: str | None = None, separator: str = " ") -> str: return separator.join(parts) def range(self, unit: str, amount: int = 1) -> Iterator[_T]: + if amount <= 0: + msg = f"amount must be a positive integer, got {amount!r}" + raise ValueError(msg) + method = "add" op = operator.le if not self._absolute and self.invert: diff --git a/tests/interval/test_range.py b/tests/interval/test_range.py index 0b883e583..9cd020e85 100644 --- a/tests/interval/test_range.py +++ b/tests/interval/test_range.py @@ -1,5 +1,7 @@ from __future__ import annotations +import pytest + import pendulum from pendulum.interval import Interval @@ -117,3 +119,15 @@ def test_range_amount(): assert_datetime(r[1], 2016, 10, 16, 1, 0, 0) assert_datetime(r[2], 2016, 10, 18, 0, 0, 0) assert_datetime(r[3], 2016, 10, 20, 0, 0, 0) + + +def test_range_zero_amount_raises(): + p = Interval(pendulum.datetime(2016, 1, 1), pendulum.datetime(2016, 2, 1)) + with pytest.raises(ValueError, match="positive integer"): + list(p.range("days", 0)) + + +def test_range_negative_amount_raises(): + p = Interval(pendulum.datetime(2016, 1, 1), pendulum.datetime(2016, 2, 1)) + with pytest.raises(ValueError, match="positive integer"): + list(p.range("days", -1))