Skip to content

Interval.range() hangs forever on a zero (or wrong-sign) step #1080

Description

@carfeii

Affected version: confirmed on release v3.3.0, commit e06dacedc4cdea90fa1286b3965d180ab94cf251.

Summary

Interval.range(unit, amount) accepts any integer amount, including 0, without validation. Because the loop's advance step and its termination check both depend on amount, an amount of 0 produces a generator that yields the same starting value forever and never reaches its stated end. Any caller that consumes the generator without an independent bound (a plain for loop, list(...), or similar) hangs indefinitely, consuming CPU with no progress, until something external kills the process.

Details

src/pendulum/interval.py:

def range(self, unit: str, amount: int = 1) -> Iterator[_T]:
    method = "add"
    op = operator.le
    if not self._absolute and self.invert:
        method = "subtract"
        op = operator.ge

    start, end = self.start, self.end

    i = amount
    while op(start, end):
        yield start

        start = getattr(self.start, method)(**{unit: i})

        i += amount

When amount == 0:

  • i starts at 0 and i += amount keeps it at 0 on every iteration.
  • start = self.start.add(**{unit: i}) recomputes the same value as self.start every time, since the added quantity is always 0.
  • The loop condition op(start, end) (effectively start <= end) stays true forever, because start never advances past end.

The function is a public method on Interval/Period objects (pendulum.interval(start, end).range(unit, amount)), documented as part of the library's date-range iteration API, with no stated restriction that amount must be non-zero.

Steps To Reproduce

(available upon request)

Impact

A caller that builds a date range from a step value it does not fully control (a configuration field, a query parameter, a value derived from user input) and passes it to Interval.range() can be made to hang indefinitely on amount=0, consuming a full CPU core with no progress and no way to recover short of killing the worker from outside. This is a realistic pattern: date-range "step"/"interval" parameters are commonly user- or config-driven (reporting tools, scheduling/trading-strategy libraries that iterate a period at a configurable interval, etc). A related but distinct case, a wrong-sign amount on an interval whose direction it does not match (e.g. amount=-1 on a forward interval), does not hang but instead runs away unbounded in the opposite direction from the stated end, the same missing-validation root cause producing unbounded resource consumption rather than a hang.

Suggested fix: reject amount == 0 with a ValueError, and either require the sign of amount to match the interval's direction or normalize it automatically, with regression tests covering both the zero-step and wrong-sign-step cases.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions