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.
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.
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.
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/
Départ Céleri , ils ont des tâches périodiques comme cron.
"... 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é.
Dans mes recherches, j’ai constaté une chose: le module sched
de python, qui pourrait être le genre de chose que vous recherchez.
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()
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.
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)
J'ai modifié le script.
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()
Essayez de démarrer la tâche dans la première seconde d'une minute.
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.
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()
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.
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
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.
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)
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.
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 .
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
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.
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.
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)
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.
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."