diff options
author | Alex Grönholm <alex.gronholm@nextday.fi> | 2017-12-12 00:26:33 +0200 |
---|---|---|
committer | Alex Grönholm <alex.gronholm@nextday.fi> | 2017-12-12 00:26:33 +0200 |
commit | 39cb1a5f65a2d5970b9639e3e324ad7953d261f9 (patch) | |
tree | c511cca2f7af557a008ad9d604efcfe1a80a102d | |
parent | 858204838d78ac15ee6f9461ad26c57bbe96db4a (diff) | |
download | apscheduler-39cb1a5f65a2d5970b9639e3e324ad7953d261f9.tar.gz |
Added support for creating cron triggers from crontab expressions
Fixes #240.
-rw-r--r-- | apscheduler/triggers/cron/__init__.py | 20 | ||||
-rw-r--r-- | docs/modules/triggers/cron.rst | 3 | ||||
-rw-r--r-- | docs/versionhistory.rst | 2 | ||||
-rw-r--r-- | tests/test_triggers.py | 12 |
4 files changed, 37 insertions, 0 deletions
diff --git a/apscheduler/triggers/cron/__init__.py b/apscheduler/triggers/cron/__init__.py index 66424cf..e7f6ae8 100644 --- a/apscheduler/triggers/cron/__init__.py +++ b/apscheduler/triggers/cron/__init__.py @@ -82,6 +82,26 @@ class CronTrigger(BaseTrigger): field = field_class(field_name, exprs, is_default) self.fields.append(field) + @classmethod + def from_crontab(cls, expr, timezone=None): + """ + Create a :class:`~CronTrigger` from a standard crontab expression. + + See https://en.wikipedia.org/wiki/Cron for more information on the format accepted here. + + :param expr: minute, hour, day of month, month, day of week + :param datetime.tzinfo|str timezone: time zone to use for the date/time calculations ( + defaults to scheduler timezone) + :return: a :class:`~CronTrigger` instance + + """ + values = expr.split(' ') + if len(values) != 5: + raise ValueError('Wrong number of fields; got {}, expected 5'.format(len(values))) + + return cls(minute=values[0], hour=values[1], day=values[2], month=values[3], + day_of_week=values[4], timezone=timezone) + def _increment_field_value(self, dateval, fieldnum): """ Increments the designated field and resets all less significant fields to their minimum diff --git a/docs/modules/triggers/cron.rst b/docs/modules/triggers/cron.rst index acc9295..a410bc2 100644 --- a/docs/modules/triggers/cron.rst +++ b/docs/modules/triggers/cron.rst @@ -108,6 +108,9 @@ The :meth:`~apscheduler.schedulers.base.BaseScheduler.scheduled_job` decorator w def some_decorated_task(): print("I am printed at 00:00:00 on the last Sunday of every month!") +To schedule a job using a standard crontab expression:: + + sched.add_job(job_function, CronTrigger.from_crontab('0 0 1-15 may-aug *')) The ``jitter`` option enables you to add a random component to the execution time. This might be useful if you have multiple servers and don't want them to run a job at the exact same moment or if you want to prevent jobs from running diff --git a/docs/versionhistory.rst b/docs/versionhistory.rst index 141f47c..7dc3473 100644 --- a/docs/versionhistory.rst +++ b/docs/versionhistory.rst @@ -15,6 +15,8 @@ APScheduler, see the :doc:`migration section <migration>`. * Added support for named months (``january`` – ``december``) in ``CronTrigger`` month expressions +* Added support for creating a ``CronTrigger`` from a crontab expression + * Fixed memory leak due to a cyclic reference when jobs raise exceptions (thanks to gilbsgilbs for help on solving this) diff --git a/tests/test_triggers.py b/tests/test_triggers.py index ca07963..34372a5 100644 --- a/tests/test_triggers.py +++ b/tests/test_triggers.py @@ -387,6 +387,18 @@ class TestCronTrigger(object): def test_invalid_ranges(self, values, expected): pytest.raises(ValueError, CronTrigger, **values).match(expected) + @pytest.mark.parametrize('expr, expected_repr', [ + ('* * * * *', + "<CronTrigger (month='*', day='*', day_of_week='*', hour='*', minute='*', " + "timezone='Europe/Berlin', jitter='None')>"), + ('0-14 * 14-28 jul fri', + "<CronTrigger (month='jul', day='14-28', day_of_week='fri', hour='*', minute='0-14', " + "timezone='Europe/Berlin', jitter='None')>") + ], ids=['always', 'assorted']) + def test_from_crontab(self, expr, expected_repr, timezone): + trigger = CronTrigger.from_crontab(expr, timezone) + assert repr(trigger) == expected_repr + class TestDateTrigger(object): @pytest.mark.parametrize('run_date,alter_tz,previous,now,expected', [ |