Как получить планировщик Cron в Python?

Я ищу библиотеку в Python, которая будет предоставлять функции at и cron.

Мне бы очень понравилось иметь чистое решение Python, а не полагаться на инструменты, установленные на коробке; таким образом я бегу на машинах без cron.

Для тех, кто не знаком с cron: вы можете планировать задачи на основе выражения типа:

 0 2 * * 7 /usr/bin/run-backup # run the backups at 0200 on Every Sunday
 0 9-17/2 * * 1-5 /usr/bin/purge-temps # run the purge temps command, every 2 hours between 9am and 5pm on Mondays to Fridays.

Синтаксис выражения времени cron менее важен, но я хотел бы иметь что-то с такой гибкостью.

Если для меня не существует чего-то такого, я буду благодарен за любые предложения для строительных блоков, чтобы сделать что-то подобное.

Edit Я не заинтересован в запуске процессов, просто "заданий", также написанных в Python - функции python. По необходимости я думаю, что это будет другой поток, но не в другом процессе.

С этой целью я ищу выразительность выражения времени cron, но в Python.

Крон существует уже много лет, но я стараюсь быть как можно более переносимым. Я не могу полагаться на его присутствие.

Ответ 1

Если вы ищете что-то легкое оформление schedule:

import schedule
import time

def job():
    print("I'm working...")

schedule.every(10).minutes.do(job)
schedule.every().hour.do(job)
schedule.every().day.at("10:30").do(job)

while 1:
    schedule.run_pending()
    time.sleep(1)

Раскрытие информации: Я являюсь автором этой библиотеки.

Ответ 2

Вы можете просто использовать обычный синтаксис передачи аргументов Python, чтобы указать свой crontab. Например, предположим, что мы определяем класс Event, как показано ниже:

from datetime import datetime, timedelta
import time

# Some utility classes / functions first
class AllMatch(set):
    """Universal set - match everything"""
    def __contains__(self, item): return True

allMatch = AllMatch()

def conv_to_set(obj):  # Allow single integer to be provided
    if isinstance(obj, (int,long)):
        return set([obj])  # Single item
    if not isinstance(obj, set):
        obj = set(obj)
    return obj

# The actual Event class
class Event(object):
    def __init__(self, action, min=allMatch, hour=allMatch, 
                       day=allMatch, month=allMatch, dow=allMatch, 
                       args=(), kwargs={}):
        self.mins = conv_to_set(min)
        self.hours= conv_to_set(hour)
        self.days = conv_to_set(day)
        self.months = conv_to_set(month)
        self.dow = conv_to_set(dow)
        self.action = action
        self.args = args
        self.kwargs = kwargs

    def matchtime(self, t):
        """Return True if this event should trigger at the specified datetime"""
        return ((t.minute     in self.mins) and
                (t.hour       in self.hours) and
                (t.day        in self.days) and
                (t.month      in self.months) and
                (t.weekday()  in self.dow))

    def check(self, t):
        if self.matchtime(t):
            self.action(*self.args, **self.kwargs)

(Примечание: не проверено полностью)

Затем ваш CronTab может быть указан в стандартном синтаксисе python как:

c = CronTab(
  Event(perform_backup, 0, 2, dow=6 ),
  Event(purge_temps, 0, range(9,18,2), dow=range(0,5))
)

Таким образом, вы получаете всю мощь механики аргументов Python (смешивая позиционные и ключевые слова args и можете использовать символические имена для имен недель и месяцев).

Класс CronTab будет определяться как просто спящий с шагом в минуту и ​​вызовом check() для каждого события. (Вероятно, есть некоторые тонкости с летним временем/часовыми поясами, чтобы быть осторожными). Вот быстрая реализация:

class CronTab(object):
    def __init__(self, *events):
        self.events = events

    def run(self):
        t=datetime(*datetime.now().timetuple()[:5])
        while 1:
            for e in self.events:
                e.check(t)

            t += timedelta(minutes=1)
            while datetime.now() < t:
                time.sleep((t - datetime.now()).seconds)

Несколько примечаний: дни недели Python/месяцы индексируются с нулевым значением (в отличие от cron), и этот диапазон исключает последний элемент, поэтому синтаксис типа "1-5" становится диапазоном (0,5) - то есть [0,1 2,3,4]. Если вы предпочитаете синтаксис cron, разбор его не должен быть слишком сложным.

Ответ 3

Возможно, это появилось только после того, как был задан вопрос; Я думал, что просто говорю об этом ради полноты: https://apscheduler.readthedocs.org/en/latest/

Ответ 4

Отъезд Celery, у них есть периодические задачи, такие как cron.

Ответ 6

Одна вещь, которую я видел в моих поисках, - это python sched, который может быть тем, что вы ищете.

Ответ 7

Более или менее то же, что и выше, но одновременно с использованием gevent:)

"""Gevent based crontab implementation"""

from datetime import datetime, timedelta
import gevent

# Some utility classes / functions first
def conv_to_set(obj):
    """Converts to set allowing single integer to be provided"""

    if isinstance(obj, (int, long)):
        return set([obj])  # Single item
    if not isinstance(obj, set):
        obj = set(obj)
    return obj

class AllMatch(set):
    """Universal set - match everything"""
    def __contains__(self, item): 
        return True

allMatch = AllMatch()

class Event(object):
    """The Actual Event Class"""

    def __init__(self, action, minute=allMatch, hour=allMatch, 
                       day=allMatch, month=allMatch, daysofweek=allMatch, 
                       args=(), kwargs={}):
        self.mins = conv_to_set(minute)
        self.hours = conv_to_set(hour)
        self.days = conv_to_set(day)
        self.months = conv_to_set(month)
        self.daysofweek = conv_to_set(daysofweek)
        self.action = action
        self.args = args
        self.kwargs = kwargs

    def matchtime(self, t1):
        """Return True if this event should trigger at the specified datetime"""
        return ((t1.minute     in self.mins) and
                (t1.hour       in self.hours) and
                (t1.day        in self.days) and
                (t1.month      in self.months) and
                (t1.weekday()  in self.daysofweek))

    def check(self, t):
        """Check and run action if needed"""

        if self.matchtime(t):
            self.action(*self.args, **self.kwargs)

class CronTab(object):
    """The crontab implementation"""

    def __init__(self, *events):
        self.events = events

    def _check(self):
        """Check all events in separate greenlets"""

        t1 = datetime(*datetime.now().timetuple()[:5])
        for event in self.events:
            gevent.spawn(event.check, t1)

        t1 += timedelta(minutes=1)
        s1 = (t1 - datetime.now()).seconds + 1
        print "Checking again in %s seconds" % s1
        job = gevent.spawn_later(s1, self._check)

    def run(self):
        """Run the cron forever"""

        self._check()
        while True:
            gevent.sleep(60)

import os 
def test_task():
    """Just an example that sends a bell and asd to all terminals"""

    os.system('echo asd | wall')  

cron = CronTab(
  Event(test_task, 22, 1 ),
  Event(test_task, 0, range(9,18,2), daysofweek=range(0,5)),
)
cron.run()

Ответ 8

TurboGears судов с запланированной возможностью задания на основе Kronos

Я никогда не использовал Kronos напрямую, но планирование в TG имеет приличный набор функций и является надежным.

Ответ 9

Ни одно из перечисленных решений даже не пытается проанализировать сложную строку расписания cron. Итак, вот моя версия с использованием croniter. Основная суть:

schedule = "*/5 * * * *" # Run every five minutes

nextRunTime = getNextCronRunTime(schedule)
while True:
     roundedDownTime = roundDownTime()
     if (roundedDownTime == nextRunTime):
         ####################################
         ### Do your periodic thing here. ###
         ####################################
         nextRunTime = getNextCronRunTime(schedule)
     elif (roundedDownTime > nextRunTime):
         # We missed an execution. Error. Re initialize.
         nextRunTime = getNextCronRunTime(schedule)
     sleepTillTopOfNextMinute()

Вспомогательные процедуры:

from croniter import croniter
from datetime import datetime, timedelta

# Round time down to the top of the previous minute
def roundDownTime(dt=None, dateDelta=timedelta(minutes=1)):
    roundTo = dateDelta.total_seconds()
    if dt == None : dt = datetime.now()
    seconds = (dt - dt.min).seconds
    rounding = (seconds+roundTo/2) // roundTo * roundTo
    return dt + timedelta(0,rounding-seconds,-dt.microsecond)

# Get next run time from now, based on schedule specified by cron string
def getNextCronRunTime(schedule):
    return croniter(schedule, datetime.now()).get_next(datetime)

# Sleep till the top of the next minute
def sleepTillTopOfNextMinute():
    t = datetime.utcnow()
    sleeptime = 60 - (t.second + t.microsecond/1000000.0)
    time.sleep(sleeptime)

Ответ 10

Я изменил script.

  • Прост в использовании:

    cron = Cron()
    cron.add('* * * * *'   , minute_task) # every minute
    cron.add('33 * * * *'  , day_task)    # every hour
    cron.add('34 18 * * *' , day_task)    # every day
    cron.run()
    
  • Попробуйте запустить задачу за первую секунду минуты.

Код в Github

Ответ 11

У меня есть незначительное исправление для метода класса CronTab, предложенного Брайаном.

Время было отключено на одну секунду, что привело к односекундной жесткой петле в конце каждой минуты.

class CronTab(object):
    def __init__(self, *events):
        self.events = events

    def run(self):
        t=datetime(*datetime.now().timetuple()[:5])
        while 1:
            for e in self.events:
                e.check(t)

            t += timedelta(minutes=1)
            n = datetime.now()
            while n < t:
                s = (t - n).seconds + 1
                time.sleep(s)
                n = datetime.now()

Ответ 12

Проверьте Луиджи (https://github.com/spotify/luigi). Он написан на python и имеет приятный веб-интерфейс для мониторинга задач. Он также имеет граф зависимостей. Может быть излишним за то, что вам нужно, но это, вероятно, добьется цели.

Ответ 13

Не существует способа "чистого python", так как для запуска другого решения потребуется запуск python. На каждой платформе будет один или два разных способа запуска процессов и мониторинг их прогресса. На платформах unix cron является старым стандартом. В Mac OS X также есть launchd, который объединяет cron-подобный запуск с функциями сторожевого таймера, которые могут поддерживать ваш процесс в ожидании, если вы этого хотите. После запуска python вы можете использовать sched module для планирования задач.

Ответ 14

На всякий случай, если вы используете окна, существует пикрон. Проверьте http://sourceforge.net/projects/pycron/. Для linux я перейду в cron или sched.

Ответ 15

Брайанское решение работает достаточно хорошо. Однако, как указывали другие, в коде запуска есть тонкая ошибка. Также я нашел это слишком сложным для нужд.

Вот моя простая и функциональная альтернатива для кода запуска, если кому-то это понадобится:

def run(self):
    while 1:
        t = datetime.now()
        for e in self.events:
            e.check(t)

        time.sleep(60 - t.second - t.microsecond / 1000000.0)

Ответ 16

Другим тривиальным решением было бы следующее:

from aqcron import At
from time import sleep
from datetime import datetime

# Event scheduling
event_1 = At( second=5 )
event_2 = At( second=[0,20,40] )

while True:
    now = datetime.now()

    # Event check
    if now in event_1: print "event_1"
    if now in event_2: print "event_2"

    sleep(1)

И класс aqcron.At:

# aqcron.py

class At(object):
    def __init__(self, year=None,    month=None,
                 day=None,     weekday=None,
                 hour=None,    minute=None,
                 second=None):
        loc = locals()
        loc.pop("self")
        self.at = dict((k, v) for k, v in loc.iteritems() if v != None)

    def __contains__(self, now):
        for k in self.at.keys():
            try:
                if not getattr(now, k) in self.at[k]: return False
            except TypeError:
                if self.at[k] != getattr(now, k): return False
        return True

Ответ 17

Если вы ищете распределенный планировщик, вы можете проверить https://github.com/sherinkurian/mani - он действительно нуждается в повторном редактировании, хотя, возможно, это не то, что вы ищете. (обратите внимание, что я являюсь автором) это было построено для обеспечения отказоустойчивости за счет запуска часов более чем на одном узле.

Ответ 18

Я знаю, что ответов много, но другим решением может стать использование декораторов. Это пример повторения функции каждый день в определенное время. Хорошая мысль об использовании этого способа заключается в том, что вам нужно только добавить синтаксический сахар к функции, которую вы хотите запланировать:

@repeatEveryDay(hour=6, minutes=30)
def sayHello(name):
    print(f"Hello {name}")

sayHello("Bob") # Now this function will be invoked every day at 6.30 a.m

И декоратор будет выглядеть так:

def repeatEveryDay(hour, minutes=0, seconds=0):
    """
    Decorator that will run the decorated function everyday at that hour, minutes and seconds.
    :param hour: 0-24
    :param minutes: 0-60 (Optional)
    :param seconds: 0-60 (Optional)
    """
    def decoratorRepeat(func):

        @functools.wraps(func)
        def wrapperRepeat(*args, **kwargs):

            def getLocalTime():
                return datetime.datetime.fromtimestamp(time.mktime(time.localtime()))

            # Get the datetime of the first function call
            td = datetime.timedelta(seconds=15)
            if wrapperRepeat.nextSent == None:
                now = getLocalTime()
                wrapperRepeat.nextSent = datetime.datetime(now.year, now.month, now.day, hour, minutes, seconds)
                if wrapperRepeat.nextSent < now:
                    wrapperRepeat.nextSent += td

            # Waiting till next day
            while getLocalTime() < wrapperRepeat.nextSent:
                time.sleep(1)

            # Call the function
            func(*args, **kwargs)

            # Get the datetime of the next function call
            wrapperRepeat.nextSent += td
            wrapperRepeat(*args, **kwargs)

        wrapperRepeat.nextSent = None
        return wrapperRepeat

    return decoratorRepeat

Ответ 19

Я не знаю, существует ли что-то подобное. Было бы легко написать свои собственные с помощью модулей времени, времени и/или календаря, см. http://docs.python.org/library/time.html

Единственная проблема для решения python заключается в том, что ваша работа должна быть всегда запущена и, возможно, автоматически "воскрешена" после перезагрузки, для чего вам сделать нужно полагаться на системно-зависимые решения.

Ответ 20

Я взял решение Брайана, внес несколько изменений, добавил начало стандартного синтаксического анализатора crontab и поместил его в https://bitbucket.org/dbenamy/devcron.

Ответ 21

Вы можете проверить PiCloud [1] Crons [2], но обратите внимание, что ваши задания не будут выполняться на вашей собственной машине. Это также услуга, которую вам нужно будет заплатить, если вы используете более 20 часов вычислительного времени в месяц.

[1] http://www.picloud.com

[2] http://docs.picloud.com/cron.html

Ответ 22

Если скрипт, который вы хотите запустить, основан на сети, вы можете рассмотреть возможность использования сторонних сервисов, таких как crono, для программной настройки ваших заданий.

Ответ 23

Метод Crontab на сервере.

Имя файла Python hello.py

Шаг 1: Создайте файл sh, дайте имя s.sh

python3/home/ubuntu/Shaurya/Folder/hello.py>/home/ubuntu/Shaurya/Folder/log.txt 2> & 1

Шаг 2: Откройте редактор Crontab

crontab -e

Шаг 3: Добавить расписание по времени

Используйте форматирование Crontab

2 * * * * sudo sh/home/ubuntu/Shaurya/Folder/s.sh

Этот cron будет запускаться "В минуту 2".

Ответ 24

Мне нравится, как пакет pycron решает эту проблему.

import pycron
import time

while True:
    if pycron.is_now('0 2 * * 0'):   # True Every Sunday at 02:00
        print('running backup')
    time.sleep(60)

Ответ 25

Jython?

Я только что искал здесь и обнаружил, что это слово не было упомянуто на этой странице. Реальная многопоточность (не многопроцессорная), Futures, ExecutorService и все другие инструменты в панели инструментов concurrency и т.д.