From 2798e36fd884b1cf2324acfb09a587009b6954ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Mon, 27 Mar 2023 10:33:50 +0200 Subject: [PATCH] Avoid crash when can not get human readable description Catch errors from cron-descriptor and display crontab as it is. Apparently there are crontabs which work in Celery, but cron-descriptor fails to format them. Issue #647 --- django_celery_beat/models.py | 15 ++++++++++++--- t/unit/test_models.py | 37 ++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/django_celery_beat/models.py b/django_celery_beat/models.py index dbfeb7fb..4f3e1578 100644 --- a/django_celery_beat/models.py +++ b/django_celery_beat/models.py @@ -8,7 +8,8 @@ import timezone_field from celery import current_app, schedules -from cron_descriptor import get_description +from cron_descriptor import (FormatException, MissingFieldException, + WrongArgumentException, get_description) from django.conf import settings from django.core.exceptions import MultipleObjectsReturned, ValidationError from django.core.validators import MaxValueValidator, MinValueValidator @@ -316,11 +317,19 @@ class Meta: @property def human_readable(self): - human_readable = get_description('{} {} {} {} {}'.format( + cron_expression = '{} {} {} {} {}'.format( cronexp(self.minute), cronexp(self.hour), cronexp(self.day_of_month), cronexp(self.month_of_year), cronexp(self.day_of_week) - )) + ) + try: + human_readable = get_description(cron_expression) + except ( + MissingFieldException, + FormatException, + WrongArgumentException + ): + return f'{cron_expression} {str(self.timezone)}' return f'{human_readable} {str(self.timezone)}' def __str__(self): diff --git a/t/unit/test_models.py b/t/unit/test_models.py index c28b8489..a745d76c 100644 --- a/t/unit/test_models.py +++ b/t/unit/test_models.py @@ -207,3 +207,40 @@ def test_trigger_update_when_deleted(self): 'The `PeriodicTasks.last_update` has not be update.' ) # Check the `PeriodicTasks` does be updated. + + +class HumanReadableTestCase(TestCase): + def test_good(self): + """Valid crontab display.""" + cron = CrontabSchedule.objects.create( + hour="2", + minute="0", + day_of_week="mon", + ) + self.assertNotEqual( + cron.human_readable, "0 2 * * mon UTC" + ) + + def test_invalid(self): + """Invalid crontab display.""" + cron = CrontabSchedule.objects.create( + hour="2", + minute="0", + day_of_week="monxxx", + ) + self.assertEqual( + cron.human_readable, "0 2 * * monxxx UTC" + ) + + def test_long_name(self): + """Long day name display.""" + # TODO: this should eventually work, but probably needs conversion + # before passing data to cron_description + cron = CrontabSchedule.objects.create( + hour="2", + minute="0", + day_of_week="monday", + ) + self.assertEqual( + cron.human_readable, "0 2 * * monday UTC" + )