it-swarm.com.de

Wie bekomme ich einen Cron-ähnlichen Scheduler in Python?

Ich suche nach einer Bibliothek in Python, die at und cron-ähnliche Funktionalität bietet.

Ich hätte gerne eine reine Python-Lösung, anstatt sich auf die auf der Box installierten Tools zu verlassen. so laufe ich auf maschinen ohne cron.

Für diejenigen, die mit cron nicht vertraut sind: Sie können Aufgaben basierend auf einem Ausdruck wie dem folgenden planen: 

 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.

Die Syntax von cron time expression ist weniger wichtig, aber ich hätte gerne etwas mit dieser Art von Flexibilität. 

Wenn es nicht etwas gibt, das dies für mich erledigt, würden alle Vorschläge für die Bausteine, etwas Ähnliches zu machen, dankbar angenommen.

Bearbeiten Ich bin nicht daran interessiert, Prozesse zu starten, nur "Jobs", die auch in Python-Python-Funktionen geschrieben sind. Ich denke, dass dies ein anderer Thread wäre, aber nicht in einem anderen Prozess.

Zu diesem Zweck suche ich nach der Ausdruckskraft des cron-Zeitausdrucks, jedoch in Python. 

Cron hat schon seit Jahren, aber ich versuche so tragbar wie möglich zu sein. Ich kann mich nicht auf seine Anwesenheit verlassen.

264
jamesh

Wenn Sie etwas Leichtes suchen Zeitplan :

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)

Disclosure: Ich bin der Autor dieser Bibliothek.

415
dbader

Sie können einfach die normale Python-Argumentübergabesyntax verwenden, um Ihre Crontab anzugeben. Angenommen, wir definieren eine Ereignisklasse wie folgt:

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)

(Hinweis: nicht gründlich getestet)

Dann kann Ihr CronTab in der normalen Python-Syntax folgendermaßen angegeben werden:

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

Auf diese Weise erhalten Sie die volle Leistungsfähigkeit der Argumentmechanik von Python (Mischung von Positions- und Keyword-Argumenten) und kann symbolische Namen für Namen von Wochen und Monaten verwenden.

Die CronTab-Klasse würde so definiert, dass sie einfach in Minutenschritten schläft und check () für jedes Ereignis aufruft. (Wahrscheinlich gibt es einige Feinheiten mit Sommerzeit/Zeitzonen, auf die man achten sollte). Hier ist eine schnelle Implementierung:

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)

Ein paar Dinge zu beachten: Pythons Wochentage/Monate sind (im Gegensatz zu cron) nullindexiert, und dieser Bereich schließt das letzte Element aus. Daher wird die Syntax wie "1-5" zu Bereich (0,5) - dh 3,4]. Wenn Sie die Cron-Syntax bevorzugen, sollte das Parsen jedoch nicht zu schwierig sein.

61
Brian

vielleicht ist das erst nach der Frage gekommen. Ich dachte, ich erwähne es nur der Vollständigkeit halber: https://apscheduler.readthedocs.org/de/latest/

42
ssc

Check out Sellerie , sie haben periodische Aufgaben wie cron.

28
Vishal

"... Crontab-Modul zum Lesen und Schreiben von Crontab-Dateien und zum automatischen und einfachen Zugriff auf das System cron über eine direkte API. ..."

http://pypi.python.org/pypi/python-crontab

und auch APScheduler, ein Python-Paket. Bereits geschrieben und debuggt.

http://packages.python.org/APScheduler/cronschedule.html

19
bootload

Eine Sache, die ich bei meinen Suchanfragen gesehen habe, ist das Python-Modul sched .

16
Sean

Mehr oder weniger dasselbe wie oben, aber gleichzeitig mit 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()
10
Hackeron

TurboGears Schiffe mit geplanter Aufgabenfähigkeit basierend auf Kronos

Ich habe Kronos noch nie direkt verwendet, aber die Terminplanung in TG verfügt über eine anständige Ausstattung und ist solide.

9
James Brady

Keine der aufgeführten Lösungen versucht sogar, eine komplexe Cron-Zeitplanzeichenfolge zu analysieren. Hier ist meine Version mit croniter . Grundlegende Gist:

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()

Hilfsroutinen:

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)
8
rouble

Ich habe das Skript geändert.

  1. Einfach zu verwenden:

    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()
    
  2. Versuchen Sie, die Aufgabe in der ersten Sekunde einer Minute zu starten.

Code auf Github

7
ning

Schauen Sie sich Luigi an ( https://github.com/spotify/luigi ). Es ist in Python geschrieben und verfügt über eine Nice-Web-Benutzeroberfläche für Überwachungsaufgaben. Es hat auch ein Abhängigkeitsgraph. Vielleicht übertrieben für das, was Sie brauchen, aber es wird wahrscheinlich den Trick tun.

6
amwinter

Ich habe ein kleineres Update für die von Brian vorgeschlagene CronTab-Klassenlaufmethode .

Das Timing war um eine Sekunde abgelaufen und führte am Ende jeder Minute zu einer harten Schleife von einer Sekunde.

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()
6
benc

Es gibt keine "reine Python" -Methode, um dies auszuführen, da ein anderer Prozess Python starten müsste, um Ihre Lösung auszuführen. Jede Plattform hat eine oder zwanzig verschiedene Möglichkeiten, Prozesse zu starten und ihren Fortschritt zu überwachen. Auf Unix-Plattformen ist Cron der alte Standard. Unter Mac OS X gibt es auch launchd, das Cron-ähnliches Starten mit Watchdog-Funktionen kombiniert, die Ihren Prozess am Leben halten können, wenn Sie dies möchten. Sobald Python ausgeführt wird, können Sie das Schedul-Modul verwenden, um Tasks zu planen.

3
Nick

Ich weiß, dass es viele Antworten gibt, aber eine andere Lösung könnte darin bestehen, mit Dekorateuren zusammenzuarbeiten. Dies ist ein Beispiel für die tägliche Wiederholung einer Funktion zu einer bestimmten Zeit. Das Coole an dieser Methode ist, dass Sie nur das Syntactic Sugar zu der Funktion hinzufügen müssen, die Sie einplanen möchten:

@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

Und der Dekorateur wird so aussehen:

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
1
Damia Fuentes

Eine andere triviale Lösung wäre:

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)

Und die Klasse aqcron.At ist:

# 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
1
fdb

Wenn Sie nach einem verteilten Scheduler suchen, können Sie https://github.com/sherinkurian/mani auschecken - er muss jedoch erneut erstellt werden, obwohl er möglicherweise nicht das ist, wonach Sie suchen. (Beachten Sie, dass ich der Autor bin.) Dies wurde gebaut, um die Fehlertoleranz sicherzustellen, indem die Uhr auf mehreren Knoten ausgeführt wird.

1
shrnkrn

Brians Lösung funktioniert ziemlich gut. Wie andere bereits erwähnt haben, gibt es jedoch einen subtilen Fehler im Laufcode. Ich fand es auch zu kompliziert für die Bedürfnisse.

Hier ist meine einfachere und funktionellere Alternative für den Laufcode für den Fall, dass jemand ihn benötigt:

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)
1
raph.amiard

Für den Fall, dass Sie Windows verwenden, existiert ein Pycron. Check out http://sourceforge.net/projects/pycron/ . Für Linux werde ich entweder cron oder sched verwenden.

1
JV.

Ich nahm Brians Lösung, nahm ein paar Änderungen vor, fügte die Anfänge eines Standard-Crontab-Dateiparsers hinzu und setzte ihn unter https://bitbucket.org/dbenamy/devcron .

0
Dan Benamy

Ich weiß nicht, ob so etwas schon existiert. Es wäre einfach, eigene Module mit Zeit-, Datums- und/oder Kalendermodulen zu erstellen, siehe http://docs.python.org/library/time.html

Die einzige Sorge für eine Python-Lösung besteht darin, dass Ihr Job immer ausgeführt werden muss und möglicherweise nach einem Neustart automatisch "wiederbelebt" wird, wofür Sie do auf systemabhängige Lösungen angewiesen sind. 

0
Davide

Sie können PiClouds [1] Crons [2] überprüfen, beachten Sie jedoch, dass Ihre Jobs nicht auf Ihrem eigenen Computer ausgeführt werden. Es ist auch ein Service, den Sie bezahlen müssen, wenn Sie mehr als 20 Stunden Rechenzeit pro Monat verwenden.

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

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

0
BrainCore

Mir gefällt, wie das Paket pycron dieses Problem löst.

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)
0
Duffau

Wenn das Skript, das Sie ausführen möchten, webbasiert ist, können Sie einen Drittanbieter-Dienst wie crono verwenden, um Ihre Jobs programmatisch einzurichten.

0
gduverger

Crontab-Methode auf dem Server.

Python-Dateiname hello.py

Schritt 1: Erstellen Sie eine sh-Datei mit dem Namen s.sh

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

Schritt 2: Öffnen Sie den Crontab-Editor

crontab -e

Schritt 3: Zeitplan hinzufügen

Verwenden Sie Crontab-Formatierung

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

Dieser Cron wird "In Minute 2" ausgeführt.

0
shaurya uppal