From d576ec9a0d80c361e27ee32a1a883b4d8f28c785 Mon Sep 17 00:00:00 2001 From: Nishchay Mahor Date: Sat, 24 Oct 2026 07:51:54 -0700 Subject: [PATCH] fix: reject a non-positive cross_validation period instead of looping forever --- python/prophet/diagnostics.py | 4 ++++ python/prophet/tests/test_diagnostics.py | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/python/prophet/diagnostics.py b/python/prophet/diagnostics.py index 89f740c06..c3cafd97d 100644 --- a/python/prophet/diagnostics.py +++ b/python/prophet/diagnostics.py @@ -76,6 +76,10 @@ def generate_cutoffs( ------- list of pd.Timestamp """ + # Each pass subtracts `period` from the cutoff, so anything but a positive + # period leaves it where it was and the loop below never terminates. + if period <= pd.Timedelta(0): + raise ValueError('period must be positive.') # Last cutoff is 'latest date in data - horizon' date cutoff = df['ds'].max() - horizon if cutoff < df['ds'].min(): diff --git a/python/prophet/tests/test_diagnostics.py b/python/prophet/tests/test_diagnostics.py index f8bd7b507..ac7ce6896 100644 --- a/python/prophet/tests/test_diagnostics.py +++ b/python/prophet/tests/test_diagnostics.py @@ -374,6 +374,19 @@ def test_cross_validation_default_value_check(self, ts_short, backend): assert ((df_cv1["y"] - df_cv2["y"]) ** 2).sum() == pytest.approx(0.0) assert ((df_cv1["yhat"] - df_cv2["yhat"]) ** 2).sum() == pytest.approx(0.0) + @pytest.mark.parametrize("period", ["0 days", "-7 days"]) + def test_cross_validation_nonpositive_period_raises(self, period): + """A non-positive period must not be accepted. + + Each pass of generate_cutoffs subtracts `period` from the cutoff, so + anything but a positive value leaves it where it was: the loop makes no + progress, appends a cutoff every iteration, and never returns. + """ + m = make_cross_validation_model() + + with pytest.raises(ValueError, match="period must be positive"): + diagnostics.cross_validation(m, horizon="2 days", period=period) + def test_cross_validation_custom_cutoffs(self, ts_short, backend): m = Prophet(stan_backend=backend) m.fit(ts_short)