我正在寻找Python中的库,该库将提供atcron之类的功能。 ;这样,我可以在没有cron的机器上运行。

对于不熟悉cron的人:您可以根据以下表达式来计划任务: > cron时间表达语法不太重要,但是我希望具有这种灵活性。

如果没有可以立即为我完成此操作的东西,将不胜感激地收到有关构建基块进行类似操作的建议。
我对启动过程不感兴趣,只是用Python-python函数编写的“作业”。必要时,我认为这将是一个不同的线程,但不会处于不同的过程中。

为此,我正在寻找cron时间表达式的可表达性,但是在Python中。

Cron已经存在了很多年,但是我正在尝试尽可能地便携。我不能依靠它的存在。

评论

我也想知道如何去做。拥有跨平台解决方案比依赖于特定于平台的组件更为有用。

这不是题外话,这是一个非常重要和有用的问题

#1 楼

如果您正在寻找一些轻量级的结帐时间表:

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)


披露:我是该图书馆的作者。

评论


您应该提到自己是日程表的维护者。对我来说效果很好。如果它具有像cron这样的语法和受支持的装饰器,那就更好了(请参阅crython但不要使用此库,因为它不起作用;调度似乎写得不好)。

–蒂姆·卢德温斯基(Tim Ludwinski)
16年8月8日在20:31

有没有办法将参数传递给作业?我想做这样的事情:schedule.every()。hour.do(job(myParam))

– Zen Skunkworx
16年6月18日在17:55



schedule.every()。hour.do(job)是否每时钟小时运行一次?例如01:00、02:00、03:00等?即使开始时间不是一个完整的小时?

– swatek
16年11月10日在10:59

假设此代码在scheduler.py中。这段代码会自动运行吗?

– Kishan Mehta
17 Mar 6 '17 at 11:40

@ darrel-holt和@ zen-skunkworx:do()函数将传递给它的额外参数转发给job函数:schedule.readthedocs.io/en/stable/api.html#schedule.Job.do例如,您可以这样做:schedule.every()。hour.do(job,param1,param2)无需使用lambda。希望这可以帮助 :)

– dbader
17年5月11日在19:04



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


(注:未经彻底测试)在正常的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”变为range(0,5)-即[0,1,2,3,4]。如果您喜欢cron语法,那么解析它应该不会太困难。

评论


您可能想为没有经验的人添加一些导入语句。最后,我将所有类与datedate import *从time import sleep更改为单个文件,并将time.sleep更改为sleep。尼斯,简单优雅的解决方案。谢谢。

– mavnn
09年5月8日在11:17

只是想知道,为什么它比Kronos更受青睐? sched是越野车吗(因为kronos使用sched)?还是刚刚过时?

– Cregox
2010-4-15的20:29

谢谢brian,我在生产中使用了您的解决方案,并且效果很好。但是,正如其他人指出的那样,您的运行代码中存在一个细微的错误。我也发现它的需求过于复杂。

–raph.amiard
2010年11月2日,0:25

这很酷,但仍然不支持斜线符号,每小时,每分钟等执行一次。

–克里斯·科斯顿(Chris Koston)
2012年7月6日在21:16

编写自己的课程的好主意,例如当我在服务器上没有sudo访问权限,因此无法通过pip安装任何内容时:)

–彗星
16年1月22日在14:41

#3 楼

与以上大致相同,但同时使用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()


评论


请注意,datetime.timetuple()将以年,月,日等开头。

– Trey Stout
2011年6月10日17:34

很好但是它仅能完成一次任务吗?

– Murali Mopuru
9月21日下午6:37

看起来您需要gevent.joinall()来运行进程,并且还不确定为什么在run()中有空的while循环

– Murali Mopuru
9月21日晚上10:15

#4 楼

列出的解决方案均未尝试解析复杂的cron计划字符串。所以,这是我的版本,使用拐杖。基本要点:

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)


评论


有人怎么能进入“遗失执行”的精灵? Atm我正在使用“ * * * * *”之类的时间表,然后在“做定期的事情” if中添加一些time.sleep大于1分钟,但是我总是在if语句中看到这些内容。当花费超过1分钟时,我只会看到while循环跳过了缺少的循环执行。

–TPPZ
18年7月2日在9:52



@TPPZ该进程可能已暂停,时钟可能已手动更改或由ntp等更改。等等。Airflow中使用了Croniter,它似乎比Crontab模块和其他模块更具功能。

– dlamblin
18-10-26在8:22

#5 楼

我知道有很多答案,但是另一个解决方案可能是与装饰器搭配使用。这是每天在特定时间重复功能的一个示例。关于使用这种方法的很酷的想法是,您只需要在要调度的功能中添加语法糖: br />
@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


评论


这是一个有吸引力的解决方案,但是它似乎取决于使用time.sleep(1)进行“等待”直到适当的时间;这会对性能产生什么样的影响?

–user5359531
12月17日下午16:42

#6 楼

没有“纯python”方法可以执行此操作,因为某些其他过程将必须启动python才能运行您的解决方案。每个平台都有一种或二十种不同的方式来启动流程和监视其进度。在UNIX平台上,cron是旧标准。在Mac OS X上,还启动了该功能,它将类似cron的启动与看门狗功能结合在一起,如果需要的话,可以使您的进程保持活动。 python运行后,您可以使用sched模块来安排任务。

#7 楼

另一个简单的解决方案是:

评论


发布针对多个问题的复制/粘贴样板/普通答案时要小心,这些问题通常会被社区标记为“垃圾邮件”。如果您这样做,通常意味着问题是重复的,因此应将其标记为:stackoverflow.com/a/12360556/419

–凯夫
2012-09-10 23:22

#8 楼

我喜欢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)               # The process should take at least 60 sec
                                     # to avoid running twice in one minute
    else:
        time.sleep(15)               # Check again in 15 seconds
 


评论


这不是一个好主意,因为您的代码“ print('running backup')”将每隔5秒启动一分钟。因此,在这种情况下,延迟应为60秒。

–n158
19年7月25日在14:15

#9 楼

我不知道是否已经存在类似的东西。使用时间,日期时间和/或日历模块编写自己的代码很容易,请参见http://docs.python.org/library/time.html

对python解决方案的唯一关注是您的工作需要始终运行,并且可能在重新启动后自动“恢复”,您确实需要依赖于系统的解决方案。

评论


自行滚动是一个选择-尽管最好的代码是不必编写的代码。我想复活是我可能需要考虑的事情。

– Jamesh
08年12月17日在9:01