Skip to content

Commit b6c0581

Browse files
DDevineauvipy
authored andcommitted
Fix crashes and ensure usage of correct timezone for solar and crontab schedules (#56)
* Return schedule for solar periodic tasks so that Celery Beat does not crash when one is scheduled. * Adding nowfun to solar and crontab schedulers so that the Django timezone is used. * Adding solar schedule tests. * Test environment now needs ephem in order to test Solar schedules. * Fixing typo in package name. * Fixed flake8 errors.
1 parent 4dc0a2a commit b6c0581

File tree

3 files changed

+49
-8
lines changed

3 files changed

+49
-8
lines changed

django_celery_beat/models.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from celery.five import python_2_unicode_compatible
1313

1414
from . import managers
15-
from .utils import now
15+
from .utils import now, make_aware
1616

1717
DAYS = 'days'
1818
HOURS = 'hours'
@@ -60,7 +60,10 @@ class Meta:
6060

6161
@property
6262
def schedule(self):
63-
return schedules.solar(self.event, self.latitude, self.longitude)
63+
return schedules.solar(self.event,
64+
self.latitude,
65+
self.longitude,
66+
nowfun=lambda: make_aware(now()))
6467

6568
@classmethod
6669
def from_schedule(cls, schedule):
@@ -171,7 +174,8 @@ def schedule(self):
171174
hour=self.hour,
172175
day_of_week=self.day_of_week,
173176
day_of_month=self.day_of_month,
174-
month_of_year=self.month_of_year)
177+
month_of_year=self.month_of_year,
178+
nowfun=lambda: make_aware(now()))
175179

176180
@classmethod
177181
def from_schedule(cls, schedule):
@@ -317,6 +321,8 @@ def schedule(self):
317321
return self.interval.schedule
318322
if self.crontab:
319323
return self.crontab.schedule
324+
if self.solar:
325+
return self.solar.schedule
320326

321327

322328
signals.pre_delete.connect(PeriodicTasks.changed, sender=PeriodicTask)

requirements/default.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
celery>=4.0,<5.0
2+
ephem>=3.7.6.0

t/unit/test_schedulers.py

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@
1010
from django.test import RequestFactory
1111

1212
from celery.five import monotonic, text_t
13-
from celery.schedules import schedule, crontab
13+
from celery.schedules import schedule, crontab, solar
1414

1515
from django_celery_beat import schedulers
1616
from django_celery_beat.admin import PeriodicTaskAdmin
1717
from django_celery_beat.models import (
1818
PeriodicTask, PeriodicTasks, IntervalSchedule, CrontabSchedule,
19+
SolarSchedule
1920
)
21+
from django_celery_beat.utils import make_aware
2022

2123
_ids = count(0)
2224

@@ -69,6 +71,11 @@ def create_model_crontab(self, schedule, **kwargs):
6971
crontab.save()
7072
return self.create_model(crontab=crontab, **kwargs)
7173

74+
def create_model_solar(self, schedule, **kwargs):
75+
solar = SolarSchedule.from_schedule(schedule)
76+
solar.save()
77+
return self.create_model(solar=solar, **kwargs)
78+
7279
def create_conf_entry(self):
7380
name = 'thefoo{0}'.format(next(_ids))
7481
return name, dict(
@@ -181,20 +188,27 @@ def setup_scheduler(self, app):
181188
schedule(timedelta(seconds=10)))
182189
self.m1.save()
183190
self.m1.refresh_from_db()
191+
184192
self.m2 = self.create_model_interval(
185193
schedule(timedelta(minutes=20)))
186194
self.m2.save()
187195
self.m2.refresh_from_db()
196+
188197
self.m3 = self.create_model_crontab(
189198
crontab(minute='2,4,5'))
190199
self.m3.save()
191200
self.m3.refresh_from_db()
192201

202+
self.m4 = self.create_model_solar(
203+
solar('solar_noon', 48.06, 12.86))
204+
self.m4.save()
205+
self.m4.refresh_from_db()
206+
193207
# disabled, should not be in schedule
194-
m4 = self.create_model_interval(
208+
m5 = self.create_model_interval(
195209
schedule(timedelta(seconds=1)))
196-
m4.enabled = False
197-
m4.save()
210+
m5.enabled = False
211+
m5.save()
198212

199213
self.s = self.Scheduler(app=self.app)
200214

@@ -206,7 +220,7 @@ def test_constructor(self):
206220
def test_all_as_schedule(self):
207221
sched = self.s.schedule
208222
assert sched
209-
assert len(sched) == 4
223+
assert len(sched) == 5
210224
assert 'celery.backend_cleanup' in sched
211225
for n, e in sched.items():
212226
assert isinstance(e, self.s.Entry)
@@ -379,6 +393,14 @@ def test_PeriodicTask_unicode_crontab(self):
379393
))
380394
assert text_t(p) == '{0}: * 4,5 4,5 * * (m/h/d/dM/MY)'.format(p.name)
381395

396+
def test_PeriodicTask_unicode_solar(self):
397+
p = self.create_model_solar(
398+
solar('solar_noon', 48.06, 12.86), name='solar_event'
399+
)
400+
assert text_t(p) == 'solar_event: {0} ({1}, {2})'.format(
401+
'solar_noon', '48.06', '12.86'
402+
)
403+
382404
def test_PeriodicTask_schedule_property(self):
383405
p1 = self.create_model_interval(schedule(timedelta(seconds=10)))
384406
s1 = p1.schedule
@@ -415,6 +437,18 @@ def test_CrontabSchedule_schedule(self):
415437
assert s.schedule.day_of_month == {1, 16}
416438
assert s.schedule.month_of_year == {1, 7}
417439

440+
def test_SolarSchedule_schedule(self):
441+
s = SolarSchedule(event='solar_noon', latitude=48.06, longitude=12.86)
442+
dt = datetime(day=25, month=7, year=2017, hour=12, minute=0)
443+
dt_lastrun = make_aware(dt)
444+
445+
assert s.schedule is not None
446+
447+
isdue, nextcheck = s.schedule.is_due(dt_lastrun)
448+
assert isdue is False # False means scheduler needs to keep checking.
449+
assert (nextcheck > 0) and (isdue is False) or \
450+
(nextcheck == s.max_interval) and (isdue is True)
451+
418452

419453
@pytest.mark.django_db()
420454
class test_model_PeriodicTasks(SchedulerCase):

0 commit comments

Comments
 (0)