代码之家  ›  专栏  ›  技术社区  ›  jamesh

如何在Python中获得类似cron的调度程序?

  •  264
  • jamesh  · 技术社区  · 16 年前

    我正在寻找一个用python编写的库,它将提供 at cron 类似功能。

    我非常希望有一个纯粹的Python解决方案,而不是依赖安装在盒子上的工具;这样我就可以在没有cron的机器上运行。

    对于那些不熟悉 克朗 :您可以基于以下表达式安排任务:

     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.
    

    cron时间表达式语法不那么重要,但我希望有这种灵活性。

    如果没有什么东西对我来说是开箱即用的,那么对于构建块做出这样的东西的任何建议都将得到感激。

    编辑 我对启动进程不感兴趣,只是“jobs”也用python-python函数编写。必要时,我认为这是一条不同的线,但不是在一个不同的过程中。

    为此,我在寻找cron-time表达式的表现力,但在python中。

    克朗 几年了,但我想尽可能的随身携带。我不能依靠它的存在。

    21 回复  |  直到 6 年前
        1
  •  408
  •   norbertpy dcastro    9 年前

    如果你在找一些轻便的结账方式 schedule :

    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)
    

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

        2
  •  60
  •   Brian    16 年前

    您可以使用普通的python参数传递语法来指定crontab。例如,假设我们定义一个事件类如下:

    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语法将crontab指定为:

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

    这样,您就可以充分利用Python的参数机制(混合位置参数和关键字参数,并可以使用符号名称表示周和月的名称)。

    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语法,那么解析它不应该太困难。

        3
  •  42
  •   Brad    11 年前

    也许这个问题是在被问到之后才出现的,我想我只是为了完整起见才提到它: https://apscheduler.readthedocs.org/en/latest/

        4
  •  28
  •   h7r    10 年前

    退房 Celery 他们有周期性的任务,比如cron。

        5
  •  19
  •   bootload    12 年前

    “……”crontab模块,用于读取和写入crontab文件,并使用直接API自动访问系统cron。……”

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

    还有apscheduler,一个python包。已写入并调试。

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

        6
  •  16
  •   Sean    16 年前

    我在搜索中看到的一件事是python的 sched 模块,这可能是你正在寻找的类型。

        7
  •  10
  •   Hackeron    15 年前

    或多或少与上述相同,但同时使用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()
    
        8
  •  9
  •   James Brady    16 年前

    TurboGears 具有计划任务能力的船舶基于 Kronos

    我从来没有直接使用过Kronos,但是TG中的调度有一套很好的特性,而且是可靠的。

        9
  •  8
  •   rouble    6 年前

    所有列出的解决方案都没有尝试解析复杂的cron调度字符串。这是我的版本,使用 croniter . 基本要旨:

    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)
    
        10
  •  7
  •   Gilles 'SO- stop being evil'    10 年前

    我已经修改了脚本。

    1. 易于使用:

      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. 尝试在一分钟的第一秒钟内开始任务。

    Code on Github

        11
  •  6
  •   Community CDub    8 年前

    我有一个小补丁 CronTab class run method suggested by Brian .

    计时结束了一秒钟,进入了一秒钟,每分钟结束时都会有一个硬循环。

    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()
    
        12
  •  6
  •   amwinter    11 年前

    看看路易吉( https://github.com/spotify/luigi )它是用python编写的,有一个很好的用于监视任务的Web UI。它也有一个依赖关系图。可能会因为你需要的东西而被过度杀戮,但它可能会起作用。

        13
  •  3
  •   Nick    16 年前

    没有一种“纯Python”方法可以做到这一点,因为其他一些进程必须启动Python才能运行您的解决方案。每个平台将有一种或二十种不同的方法来启动流程并监控其进度。在UNIX平台上,cron是旧标准。在MacOSX上还有launchd,它结合了类似cron的启动和看门狗的功能,如果你想要的话,可以让你的进程保持活动。一旦python运行,那么可以使用 sched module 安排任务。

        14
  •  1
  •   JV.    16 年前

    以防万一,如果您使用的是Windows,就存在一个pycron。退房 http://sourceforge.net/projects/pycron/ . 对于Linux,我将使用cron或sched。

        15
  •  1
  •   Community CDub    8 年前

    Brian's solution 工作得很好。然而,正如其他人指出的,运行代码中有一个微妙的错误。而且我发现它对于需求来说过于复杂。

    以下是我对运行代码的简单和实用的替代方案,以防有人需要它:

    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)
    
        16
  •  1
  •   fdb    12 年前

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

    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)
    

    aqcron.at类是:

    # 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
    
        17
  •  1
  •   shrnkrn    7 年前

    如果要查找分布式调度程序,可以签出 https://github.com/sherinkurian/mani -但它确实需要redis,所以可能不是你想要的。(注意我是作者) 这是为了通过在多个节点上运行时钟来确保容错性而构建的。

        18
  •  0
  •   Davide    16 年前

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

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

        19
  •  0
  •   Dan Benamy    14 年前

    我采用了Brian的解决方案,做了一些修改,添加了标准crontab文件解析器的开头,并将其放在 https://bitbucket.org/dbenamy/devcron .

        20
  •  0
  •   BrainCore    12 年前

    您可以查看picloud的[1]crons[2],但请注意,您的作业不会在您自己的计算机上运行。如果你一个月使用超过20小时的计算时间,这也是一项你需要支付的服务。

    〔1〕 http://www.picloud.com

    〔2〕 http://docs.picloud.com/cron.html

        21
  •  0
  •   gduverger    6 年前

    如果要运行的脚本是基于Web的,则可以考虑使用第三方服务,例如 crono ,以编程方式设置作业。