Skip to content

Commit

Permalink
fix: 🐛 do not crash when there is no next rrule occurence
Browse files Browse the repository at this point in the history
  • Loading branch information
PierrickBrun committed Dec 26, 2024
1 parent a982907 commit ebb8db2
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 5 deletions.
2 changes: 2 additions & 0 deletions rq_scheduler/scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,8 @@ def rrule(self, rrule_string, func, args=None, kwargs=None, repeat=None,
Schedule a recurring job via RRule
"""
scheduled_time = get_next_rrule_scheduled_time(rrule_string)
if not scheduled_time:
return None

job = self._create_job(func, args=args, kwargs=kwargs, commit=False,
result_ttl=result_ttl, ttl=ttl, id=id, queue_name=queue_name,
Expand Down
18 changes: 13 additions & 5 deletions rq_scheduler/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,20 @@ def get_next_rrule_scheduled_time(rrule_string):
with a rrule string"""
timezone = dateutil.tz.UTC
ruleset = dateutil.rrule.rrulestr(rrule_string, forceset=True)
if ruleset[0].tzinfo is None:
now = datetime.now() # naive datetime
any_occurence = None
for occur in ruleset:
any_occurence = occur
break
if any_occurence is None:
return None
if any_occurence.tzinfo is None:
now = datetime.now()
else:
now = datetime.now(tz=timezone) # aware datetime
next_time = ruleset.after(now)
return next_time.astimezone(timezone)
now = datetime.now(tz=timezone)
next_occurence = ruleset.after(now)
if next_occurence is None:
return None
return next_occurence.astimezone(timezone)


def setup_loghandlers(level='INFO'):
Expand Down
14 changes: 14 additions & 0 deletions tests/test_scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -926,6 +926,20 @@ def test_rrule_schedules_correctly(self):
expected_scheduled_time = (now + timedelta(hours=1, minutes=5)).astimezone(UTC)
self.assertEqual(to_unix(expected_scheduled_time), to_unix(next_scheduled_time), f"{next_scheduled_time} should be {expected_scheduled_time}")

def test_rrule_without_upcoming_occurences(self):
# Create a job with a rrulejob_string that has no occurence in the future
now = datetime.fromisoformat("2024-12-26T12:00:00")
with freezegun.freeze_time(now):
job = self.scheduler.rrule("RRULE:FREQ=HOURLY;WKST=MO;BYMINUTE=5;BYSECOND=0;UNTIL=20241225T120000Z", say_hello)
with mock.patch.object(self.scheduler, 'enqueue_job', wraps=self.scheduler.enqueue_job) as enqueue_job:
self.assertEqual(0, self.scheduler.count())
self.scheduler.enqueue_jobs()
self.assertEqual(0, enqueue_job.call_count)

jobs = self.scheduler.get_jobs(with_times=True)
for j in jobs:
self.assertFalse()

def test_rrule_sets_timeout(self):
"""
Ensure that a job scheduled via rrule can be created with
Expand Down

0 comments on commit ebb8db2

Please sign in to comment.