Преглед на файлове

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 преди 9 години
родител
ревизия
779924faf6
променени са 2 файла, в които са добавени 21 реда и са изтрити 1 реда
  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):