Ver Fonte

Try to fix crontab infinite loop (invalid date)

When never occurrence can never be reached (example, April, 31th), trying
to reach the next occurrence would trigger an infinite loop.

Try fixing that by raising a RuntimeError after 2,000 iterations

(Also added a test for crontab leap years in the process)
Romuald Brunet há 9 anos atrás
pai
commit
779924faf6
2 ficheiros alterados com 21 adições e 1 exclusões
  1. 5 1
      celery/schedules.py
  2. 16 0
      celery/tests/app/test_schedules.py

+ 5 - 1
celery/schedules.py

@@ -461,7 +461,7 @@ class crontab(schedule):
             return False
 
         def roll_over():
-            while 1:
+            for _ in range(2000):
                 flag = (datedata.dom == len(days_of_month) or
                         day_out_of_range(datedata.year,
                                          months_of_year[datedata.moy],
@@ -478,6 +478,10 @@ class crontab(schedule):
                         datedata.year += 1
                 else:
                     break
+            else:
+                # Tried 2000 times, we're most likely in an infinite loop
+                raise RuntimeError('unable to rollover, '
+                                   'time specification is probably invalid')
 
         if last_run_at.month in self.month_of_year:
             datedata.dom = bisect(days_of_month, last_run_at.day)

+ 16 - 0
celery/tests/app/test_schedules.py

@@ -434,6 +434,22 @@ class test_crontab_remaining_estimate(AppCase):
         )
         self.assertEqual(next, datetime(2010, 5, 29, 0, 5))
 
+    def test_invalid_specification(self):
+        # *** WARNING ***
+        # This test triggers an infinite loop in case of a regression
+        with self.assertRaises(RuntimeError):
+            next = self.next_ocurrance(
+                self.crontab(day_of_month=31, month_of_year=4),
+                datetime(2010, 1, 28, 14, 30, 15),
+            )
+
+    def test_leapyear(self):
+        next = self.next_ocurrance(
+            self.crontab(minute=30, hour=14, day_of_month=29, month_of_year=2),
+            datetime(2012, 2, 29, 14, 30),
+        )
+        self.assertEqual(next, datetime(2016, 2, 29, 14, 30))
+
 
 class test_crontab_is_due(AppCase):