web-dev-qa-db-fra.com

Comment obtenir un ordonnanceur similaire à Cron en Python?

Je cherche une bibliothèque en Python qui fournira des fonctionnalités similaires à at et cron.

J'aimerais bien avoir une solution Python pure, plutôt que de compter sur des outils installés sur la boîte; De cette façon, je tourne sur des machines sans cron.

Pour ceux qui ne connaissent pas cron: vous pouvez planifier des tâches en fonction d'une expression telle que: 

 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.

La syntaxe d'expression de temps cron est moins importante, mais j'aimerais avoir quelque chose avec ce genre de flexibilité. 

S'il n'y a pas quelque chose qui me permette de faire ça dans les règles de l'art, toute suggestion concernant les éléments constitutifs permettant de faire quelque chose comme celui-ci serait reçue avec reconnaissance.

Éditer Je ne suis pas intéressé par le lancement de processus, mais par des "jobs" également écrits en Python - Fonctions python. Par nécessité, je pense que ce serait un fil différent, mais pas dans un processus différent.

Pour cela, je recherche l'expressivité de l'expression cron time, mais en Python. 

Cron existe depuis des années, mais j'essaie d'être aussi portable que possible. Je ne peux pas compter sur sa présence.

264
jamesh

Si vous cherchez quelque chose de léger à la caisse agenda :

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: Je suis l'auteur de cette bibliothèque.

415
dbader

Vous pouvez simplement utiliser la syntaxe de transmission d'argument Python normale pour spécifier votre crontab. Par exemple, supposons que nous définissions une classe Event comme ci-dessous:

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)

(Remarque: pas complètement testé)

Ensuite, votre CronTab peut être spécifié dans la syntaxe Python normale en tant que:

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

De cette façon, vous obtenez toute la puissance de la mécanique des arguments de Python (mélange d'arguments de position et de mot clé, et possibilité d'utiliser des noms symboliques pour les noms de semaines et de mois).

La classe CronTab serait définie comme dormant simplement par incréments de minute et appelant check () à chaque événement. (Il y a probablement quelques subtilités avec l'heure d'été et les fuseaux horaires dont il faut se méfier). Voici une mise en œuvre rapide:

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)

Quelques points à noter: les jours de la semaine et les mois de Python sont indexés à zéro (contrairement à cron), et cette plage exclut le dernier élément. Par conséquent, une syntaxe telle que "1-5" devient la plage (0,5) - c.-à-d. 3,4]. Si vous préférez la syntaxe cron, son analyse ne devrait toutefois pas être trop difficile.

61
Brian

peut-être que cela n’a été soulevé qu’après que la question a été posée; Je pensais simplement en parler pour plus de détails: https://apscheduler.readthedocs.org/en/latest/

42
ssc

Départ Céleri , ils ont des tâches périodiques comme cron.

28
Vishal

"... Module Crontab pour la lecture et l'écriture de fichiers crontab et l'accès au système cron automatiquement et simplement à l'aide d'une API directe. ..."

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

et aussi APScheduler, un paquet python. Déjà écrit et débogué.

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

19
bootload

Dans mes recherches, j’ai constaté une chose: le module sched de python, qui pourrait être le genre de chose que vous recherchez.

16
Sean

Plus ou moins comme ci-dessus mais en même temps, en utilisant 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 navires avec une capacité de tâche planifiée basée sur Kronos

Je n'ai jamais utilisé Kronos directement, mais la planification de TG comporte de nombreuses fonctionnalités et est solide.

9
James Brady

Aucune des solutions répertoriées ne tente même d'analyser une chaîne de planification complexe cron. Donc, voici ma version, en utilisant croniter . Gist de base:

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

Routines d'assistance:

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

J'ai modifié le script.

  1. Facile à utiliser:

    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. Essayez de démarrer la tâche dans la première seconde d'une minute.

Code sur Github

7
ning

Découvrez luigi ( https://github.com/spotify/luigi ). Il est écrit en python et dispose d'une interface utilisateur Web agréable pour la surveillance des tâches. Il a également un graphique de dépendance. Peut-être exagéré pour ce dont vous avez besoin, mais cela fera probablement l'affaire.

6
amwinter

J'ai un correctif mineur pour la méthode d'exécution de la classe CronTab suggéré par Brian .

Le temps était écoulé d’une seconde, ce qui a entraîné une boucle dure d’une seconde à la fin de chaque minute.

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

Il n’existe pas de méthode "pure python" pour le faire, car un autre processus devrait lancer python pour exécuter votre solution. Chaque plate-forme aura un ou vingt moyens différents de lancer des processus et de suivre leurs progrès. Sur les plates-formes unix, cron est l'ancienne norme. Il existe également sur Mac OS X, launchd, qui associe un lancement similaire à celui de cron et une fonctionnalité de surveillance permettant de garder votre processus actif si vous le souhaitez. Une fois que python est en cours d’exécution, vous pouvez utiliser le module sched module pour planifier des tâches.

3
Nick

Une autre solution triviale serait:

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)

Et la classe aqcron.At est:

# 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

Si vous recherchez un planificateur distribué, vous pouvez consulter https://github.com/sherinkurian/mani - il vous faut redis, bien que ce ne soit peut-être pas ce que vous recherchez. (notez que je suis l'auteur) ceci a été construit pour assurer la tolérance aux pannes en faisant fonctionner l'horloge sur plus d'un nœud.

1
shrnkrn

La solution de Brian fonctionne assez bien. Cependant, comme d'autres l'ont souligné, il existe un bogue subtil dans le code d'exécution. Aussi, j'ai trouvé cela trop compliqué pour les besoins.

Voici mon alternative plus simple et fonctionnelle pour le code d'exécution au cas où quelqu'un en aurait besoin:

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

Juste au cas où, si vous utilisez Windows, il existe un pycron. Départ http://sourceforge.net/projects/pycron/ . Pour Linux, je vais utiliser cron ou sched.

1
JV.

J'ai pris la solution de Brian, apporté quelques modifications, ajouté les débuts d'un analyseur de fichiers crontab standard et mis ce dernier à https://bitbucket.org/dbenamy/devcron .

0
Dan Benamy

Je sais qu'il y a beaucoup de réponses, mais une autre solution pourrait être d'aller avec décorateurs . Ceci est un exemple pour répéter une fonction tous les jours à une heure précise. La bonne façon d’utiliser cette méthode est d’ajouter le sucre syntaxique à la fonction que vous souhaitez planifier:

@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

Et le décorateur ressemblera à:

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

Je ne sais pas si quelque chose comme ça existe déjà. Il serait facile d’écrire les vôtres avec les modules heure, date/heure et/ou calendrier, voir http://docs.python.org/library/time.html

Le seul souci de la solution python est que votre travail doit toujours être en cours d'exécution et éventuellement être automatiquement "ressuscité" après un redémarrage, ce pour quoi vous faites besoin de recourir à des solutions dépendant du système.

0
Davide

Vous pouvez consulter le [1] Crons [2] de PiCloud, mais notez que vos travaux ne seront pas exécutés sur votre propre ordinateur. C'est également un service que vous devrez payer si vous utilisez plus de 20 heures de temps de calcul par mois.

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

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

0
BrainCore

J'aime la façon dont le paquet pycron résout ce problème.

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

Si le script que vous souhaitez exécuter est basé sur le Web, vous pouvez envisager d'utiliser un service tiers, tel que crono , pour configurer vos travaux par programmation.

0
gduverger

Méthode de Crontab sur le serveur.

Nom du fichier Python hello.py

Etape 1: Créer un fichier sh permet de donner le nom s.sh

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

Étape 2: Ouvrez l’éditeur Crontab

crontab -e

Étape 3: Ajouter une heure de planification

Utiliser Formatage Crontab

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

Ce cron fonctionnera "à la minute 2."

0
shaurya uppal