diff options
-rw-r--r-- | apscheduler/triggers/cron/__init__.py | 4 | ||||
-rw-r--r-- | apscheduler/triggers/cron/expressions.py | 32 | ||||
-rw-r--r-- | apscheduler/triggers/cron/fields.py | 6 | ||||
-rw-r--r-- | docs/modules/triggers/cron.rst | 3 | ||||
-rw-r--r-- | docs/versionhistory.rst | 2 | ||||
-rw-r--r-- | tests/test_triggers.py | 4 |
6 files changed, 46 insertions, 5 deletions
diff --git a/apscheduler/triggers/cron/__init__.py b/apscheduler/triggers/cron/__init__.py index e936190..66424cf 100644 --- a/apscheduler/triggers/cron/__init__.py +++ b/apscheduler/triggers/cron/__init__.py @@ -5,7 +5,7 @@ import six from apscheduler.triggers.base import BaseTrigger from apscheduler.triggers.cron.fields import ( - BaseField, WeekField, DayOfMonthField, DayOfWeekField, DEFAULT_VALUES) + BaseField, MonthField, WeekField, DayOfMonthField, DayOfWeekField, DEFAULT_VALUES) from apscheduler.util import datetime_ceil, convert_to_datetime, datetime_repr, astimezone @@ -34,7 +34,7 @@ class CronTrigger(BaseTrigger): FIELD_NAMES = ('year', 'month', 'day', 'week', 'day_of_week', 'hour', 'minute', 'second') FIELDS_MAP = { 'year': BaseField, - 'month': BaseField, + 'month': MonthField, 'week': WeekField, 'day': DayOfMonthField, 'day_of_week': DayOfWeekField, diff --git a/apscheduler/triggers/cron/expressions.py b/apscheduler/triggers/cron/expressions.py index 131a80f..55a3716 100644 --- a/apscheduler/triggers/cron/expressions.py +++ b/apscheduler/triggers/cron/expressions.py @@ -10,6 +10,7 @@ __all__ = ('AllExpression', 'RangeExpression', 'WeekdayRangeExpression', WEEKDAYS = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'] +MONTHS = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'] class AllExpression(object): @@ -125,6 +126,37 @@ class RangeExpression(AllExpression): return "%s(%s)" % (self.__class__.__name__, ', '.join(args)) +class MonthRangeExpression(RangeExpression): + value_re = re.compile(r'(?P<first>[a-z]+)(?:-(?P<last>[a-z]+))?', re.IGNORECASE) + + def __init__(self, first, last=None): + try: + first_num = MONTHS.index(first.lower()) + 1 + except ValueError: + raise ValueError('Invalid month name "%s"' % first) + + if last: + try: + last_num = MONTHS.index(last.lower()) + 1 + except ValueError: + raise ValueError('Invalid month name "%s"' % last) + else: + last_num = None + + super(MonthRangeExpression, self).__init__(first_num, last_num) + + def __str__(self): + if self.last != self.first and self.last is not None: + return '%s-%s' % (MONTHS[self.first - 1], MONTHS[self.last - 1]) + return MONTHS[self.first - 1] + + def __repr__(self): + args = ["'%s'" % MONTHS[self.first]] + if self.last != self.first and self.last is not None: + args.append("'%s'" % MONTHS[self.last - 1]) + return "%s(%s)" % (self.__class__.__name__, ', '.join(args)) + + class WeekdayRangeExpression(RangeExpression): value_re = re.compile(r'(?P<first>[a-z]+)(?:-(?P<last>[a-z]+))?', re.IGNORECASE) diff --git a/apscheduler/triggers/cron/fields.py b/apscheduler/triggers/cron/fields.py index 811127b..27674c8 100644 --- a/apscheduler/triggers/cron/fields.py +++ b/apscheduler/triggers/cron/fields.py @@ -6,7 +6,7 @@ import six from apscheduler.triggers.cron.expressions import ( AllExpression, RangeExpression, WeekdayPositionExpression, LastDayOfMonthExpression, - WeekdayRangeExpression) + WeekdayRangeExpression, MonthRangeExpression) __all__ = ('MIN_VALUES', 'MAX_VALUES', 'DEFAULT_VALUES', 'BaseField', 'WeekField', @@ -107,3 +107,7 @@ class DayOfWeekField(BaseField): def get_value(self, dateval): return dateval.weekday() + + +class MonthField(BaseField): + COMPILERS = BaseField.COMPILERS + [MonthRangeExpression] diff --git a/docs/modules/triggers/cron.rst b/docs/modules/triggers/cron.rst index 791156d..acc9295 100644 --- a/docs/modules/triggers/cron.rst +++ b/docs/modules/triggers/cron.rst @@ -53,6 +53,9 @@ Expression Field Description ``x,y,z`` any Fire on any matching expression; can combine any number of any of the above expressions ============== ===== ======================================================================================= +.. note:: The ``month`` and ``day_of_week`` fields accept abbreviated English month and weekday names + (``jan`` – ``dec`` and ``mon`` – ``sun``) respectively. + Daylight saving time behavior ----------------------------- diff --git a/docs/versionhistory.rst b/docs/versionhistory.rst index 98b8ea5..141f47c 100644 --- a/docs/versionhistory.rst +++ b/docs/versionhistory.rst @@ -13,6 +13,8 @@ APScheduler, see the :doc:`migration section <migration>`. * Added better validation for the steps and ranges of different expressions in ``CronTrigger`` +* Added support for named months (``january`` – ``december``) in ``CronTrigger`` month expressions + * 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 0632d92..ca07963 100644 --- a/tests/test_triggers.py +++ b/tests/test_triggers.py @@ -116,8 +116,8 @@ class TestCronTrigger(object): assert trigger.get_next_fire_time(None, start_date) == correct_next_date def test_cron_trigger_3(self, timezone): - trigger = CronTrigger(year='2009', month='2', hour='8-10', timezone=timezone) - assert repr(trigger) == ("<CronTrigger (year='2009', month='2', hour='8-10', " + trigger = CronTrigger(year='2009', month='feb-dec', hour='8-10', timezone=timezone) + assert repr(trigger) == ("<CronTrigger (year='2009', month='feb-dec', hour='8-10', " "timezone='Europe/Berlin', jitter='None')>") start_date = timezone.localize(datetime(2009, 1, 1)) correct_next_date = timezone.localize(datetime(2009, 2, 1, 8)) |