456 votes

Comment obtenir un planificateur de type Cron en Python ?

Je suis à la recherche d'une bibliothèque en Python qui fournirait at y cron comme une fonctionnalité.

J'aimerais bien avoir une solution purement Python, plutôt que de dépendre d'outils installés sur la boîte ; de cette façon, je fonctionne sur des machines qui n'ont pas de cron.

Pour ceux qui ne connaissent pas cron : vous pouvez programmer des tâches sur la base 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 de l'expression du temps cron est moins importante, mais j'aimerais avoir quelque chose avec ce genre de flexibilité.

S'il n'existe pas de solution prête à l'emploi, toute suggestion concernant les éléments constitutifs d'une telle solution sera accueillie avec gratitude.

Editer Je ne suis pas intéressé par le lancement de processus, mais seulement par des "tâches" également écrites en Python - des fonctions Python. Par nécessité, je pense qu'il s'agirait d'un autre fil de discussion, mais pas d'un autre processus.

A cette fin, je recherche l'expressivité de l'expression cron time, mais en Python.

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

1 votes

J'aimerais également savoir comment procéder. Il serait plus utile d'avoir une solution multiplateforme que de dépendre de composants spécifiques à une plateforme.

27 votes

Ce n'est pas hors sujet, c'est une question très importante et très utile.

2 votes

Je propose d'utiliser céleri . Il s'agit d'une file d'attente de tâches qui se concentre sur le traitement en temps réel, tout en prenant en charge la planification des tâches. Vous pouvez jeter un coup d'œil sur la façon dont nous pouvons gérer les tâches périodiques PS : Je ne peux pas donner d'exemple car cette question n'accepte pas de réponses, je suis d'accord avec @Connor.

789voto

dbader Points 270

Si vous cherchez quelque chose de léger, passez à la caisse calendrier :

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)

Divulgation : Je suis l'auteur de cette bibliothèque.

17 votes

Vous devriez mentionner que vous êtes le responsable de la maintenance de schedule . Cela a bien fonctionné pour moi. Ce serait encore mieux si elle avait une syntaxe de type cron et supportait les décorateurs (voir crython mais n'utilisez pas cette bibliothèque parce qu'elle ne fonctionne pas ; l'ordonnancement ne semble pas être bien écrit).

31 votes

Existe-t-il un moyen de passer un paramètre à la tâche ? J'aimerais faire quelque chose comme ceci : schedule.every().hour.do(job(myParam))

5 votes

Schedule.every().hour.do(job) est-ce que cela s'exécute toutes les heures de l'horloge ? Comme 01:00, 02:00, 03:00, etc. ? même si l'heure de début n'est pas une heure complète ?

77voto

Brian Points 48423

Vous pouvez simplement utiliser la syntaxe normale de passage d'argument de Python pour spécifier votre crontab. Par exemple, supposons que nous définissions une classe d'événement 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)

(Note : pas de test approfondi)

Ensuite, votre CronTab peut être spécifié dans la syntaxe normale de Python comme suit :

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 bénéficiez de toute la puissance des mécanismes d'argumentation de Python (mélange d'arguments positionnels et de mots-clés, 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 minutes 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 implémentation 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/mois de Python sont indexés à zéro (contrairement à cron), et cet intervalle exclut le dernier élément, ce qui explique que la syntaxe comme "1-5" devient range(0,5) - c'est-à-dire [0,1,2,3,4]. Si vous préférez la syntaxe cron, l'analyser ne devrait pas être trop difficile.

0 votes

Vous pouvez ajouter quelques instructions d'importation pour les personnes inexpérimentées. J'ai fini par mettre toutes les classes dans un seul fichier avec from datetime import * from time import sleep et j'ai changé time.sleep en sleep. C'est une solution simple et élégante. Merci beaucoup.

1 votes

Je me demande simplement pourquoi ce système est préféré à Kronos. Est-ce que sched est si bogué (puisque kronos utilise sched) ? Ou est-ce que c'est simplement dépassé ?

0 votes

Merci Brian, j'utilise votre solution en production et cela fonctionne très bien. Cependant, comme d'autres l'ont souligné, il y a un bug subtil dans votre code d'exécution. De plus, je l'ai trouvé trop compliqué pour les besoins.

46voto

ssc Points 1813

Il se peut que cette question n'ait été soulevée qu'après qu'elle a été posée ; j'ai pensé la mentionner pour être complet : https://apscheduler.readthedocs.org/en/latest/

28voto

Vishal Points 4548

Vérifier Céleri Ils ont des tâches périodiques comme cron.

19voto

bootload Points 379

"... Module Crontab pour lire et écrire des fichiers crontab et accéder au système cron automatiquement et simplement en utilisant une API directe. ..."

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

ainsi que APScheduler, un paquetage python. Déjà écrit et débogué.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X