Как только выяснилось, что придется писать свой планировщик, мой взгляд сразу обратился на 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 мин.). Для задач, где необходимо уметь задавать любой постоянный промежуток между запуском заданий, крон тоже не подойдет.
Комментариев нет:
Отправить комментарий