Как только выяснилось, что придется писать свой планировщик, мой взгляд сразу обратился на crontab. Но, испугавшись кажущейся сложности его парсинга, я начал придумывать другой, возможно более слабый, но простой в реализации, механизм периодичности задач. В этом неблагодарном деле я не преуспел и, несолоно хлебавши, вернулся к кронтабу. Как оказалось, бояться там было абсолютно нечего.
Формат я сразу несколько упростил для облегчения работы себе любимому:
- не разрешено использовать символьные обозначения месяцев и дней недели (jan - dec, mon - sun), только номера;
- для обозначения воскресенья используется только 7, 0 не используется.
Задачей парсинга крон-выражения является получение из исходной пятисекционной строки кортежа из пяти множеств, содержащих все допустимые номера для каждой секции. Этакая "компиляция" строки в набор чисел. :)
Формально синтаксис секции крона представляет собой список диапазонов с шагом, с несколькими сокращениями и умолчаниями. Поняв это, легко пишем несложную функцию парсинга:
__bounds = {
0: (0, 59),
1: (0, 23),
2: (1, 31),
3: (1, 12),
4: (1, 7)
}
def parse_cron(self, cron):
u"""Парсить строку cron"""
parts = re.split(ur'\s+', cron)
if len(parts) != 5:
raise ValueError(u"Некорректный формат cron, строка '{0}' должна содержать 5 элементов через пробелы.")
compiled = []
for i, part in enumerate(parts):
partcompiled = set()
for item in part.split(u','):
if item:
if u'/' in item:
val, step = item.split(u'/')
step = int(step)
else:
val, step = item, 1
if u'*' == val:
start, stop = self.__bounds[i]
elif u'-' in val:
start, stop = [int(b) for b in val.split(u'-')]
else:
start, stop = int(val), int(val)
stop += 1
partcompiled |= set(range(start, stop, step))
compiled.append(partcompiled)
return tuple(compiled)
Использование полученного кортежа тривиально: проверяемую дату/время разбить на составляющие и проверить вхождение каждой составляющей в соответствующее множество. Для получения номера дня недели следует использовать функцию datetime.isoweekday, ее возвращаемое значение соответствует принятому соглашению о нумерации дней недели.
Я поленился, но можно избавиться от введенных ограничений:
- перед парсингом привести исходную строку к нижнему регистру и заменить в ней символьные название месяцев и дней недели на соответствующие номера;
- после парсинга в последнем множестве (дни недели) заменить 0 на 7, если присутствует.
У кронтаба есть ограничения:
- Точность срабатывания планировщика не выше минуты. Для подавляющего большинства задач это абсолютно некритично, но допускаю существование ситуаций, где это имеет большое значение;
- Нельзя задать период срабатывания планировщика, некратный длительности секции, например выполнять задание каждые 100 минут (длительность секции - 60 мин.). Для задач, где необходимо уметь задавать любой постоянный промежуток между запуском заданий, крон тоже не подойдет.
Комментариев нет:
Отправить комментарий