Python: Detect stationary date steps in calendar arithmetic generators to avoid infinite loops
Date-range generators accepting compound calendar steps (year, month, day) tuples can loop forever, and static input validation cannot fix it: a step like (0, 1, -31) is nonzero in every component but perfectly stationary (Jan 1 -> +1 month -> Feb 1 -> -31 days -> Jan 1). Worse, stationarity can be late-onset: (0, 1, -28) advances from Jan 1 (+3 days/iter) but becomes a fixed point when the sequence reaches Feb 1 in a non-leap year (Feb 1 -> Mar 1 -> -28 days -> Feb 1). No enumeration of 'bad steps' at the top of the function can catch these, because whether a month/day step advances depends on which month the cursor is currently in.
Replace input validation with a runtime progress invariant, checked in two places: (1) pre-loop, probe one advance from start: if probe == start raise ValueError('step does not advance'), and if stop is finite and (probe > start) != (stop > start) return immediately, which gives range(1, 5, -1)-style empty for wrong-direction steps before yielding anything; (2) in-loop after each advance: if now == prev raise (catches late-onset stationarity), and if abs(stop - now) >= abs(stop - prev) return (catches mid-sequence oscillation; benign for plain overshoot because the loop's finished() predicate would terminate with identical yields anyway). The abs-distance form works for both date and datetime cursors since subtraction yields timedelta. Shipped in boltons daterange (PR mahmoud/boltons#443); regression tests pin (0,1,-31) raising and step=-1 toward a later stop yielding [].