Python 自定义计划程序具有顺序+;带有超时/终止开关的半连续脚本?

Python 自定义计划程序具有顺序+;带有超时/终止开关的半连续脚本?,python,python-2.7,subprocess,scheduler,execfile,Python,Python 2.7,Subprocess,Scheduler,Execfile,下面是我的代码的一大部分,基本上如果你向下滚动到execute\u subscripts()函数,你会看到我有两个脚本通过execfile运行,它们运行得很好,它们显示打印,它们将回溯错误保存到错误文件中 我正在尝试将第二个脚本转换为一个不需要等到自己完成后再转到下一个脚本的脚本 如您所见,我尝试将子流程与Popen一起使用,以启动一个无声的隐藏窗口。。。但是它似乎没有运行,我不知道如何正确使用p.communicate()函数来检索回溯和/或打印 我还…需要帮助创建某种类型的超时/终止开关,以

下面是我的代码的一大部分,基本上如果你向下滚动到
execute\u subscripts()
函数,你会看到我有两个脚本通过
execfile
运行,它们运行得很好,它们显示
打印
,它们将
回溯
错误保存到错误文件中

我正在尝试将第二个脚本转换为一个不需要等到自己完成后再转到下一个脚本的脚本

如您所见,我尝试将
子流程
Popen
一起使用,以启动一个无声的隐藏窗口。。。但是它似乎没有运行,我不知道如何正确使用
p.communicate()
函数来检索
回溯
和/或
打印

我还…需要帮助创建某种类型的超时/终止开关,以便在通过
Popen
execfile
路由的下标未在5分钟内完成时,它可以跳过该循环,或者在立即再次失败时重试并跳过

我知道我可能不应该在《泰晤士报》上使用
strftime
。。。。然而,这一部分对我来说很好,所以我认为没有必要改变它

from datetime import date, timedelta
from sched import scheduler
from time import time, sleep, strftime
import random
import traceback
import subprocess

s = scheduler(time, sleep)
random.seed()

def periodically(runtime, intsmall, intlarge, function):

     ## Get current time
    currenttime = strftime('%H:%M:%S')

    ## If currenttime is anywhere between 23:40 and 23:50 then...
    if currenttime > '23:40:00' and currenttime < '23:50:00':

        ## Open the error logging file as the variable "errors"
        errors = open('MISC/ERROR(S).txt', 'a')

        ## Try to...
        try:
            ## Call the clear subscript.
            execfile("SUBSCRIPTS/CLEAR.py", {})
        ## On exception (fail)...
        except Exception:
            ## Write the entire traceback error to file...
            errors.write(traceback.format_exc() + '\n')
            errors.write("\n\n")

        ## Close and exit the error logging file. 
        errors.close()

        ## Update time
        currenttime = strftime('%H:%M:%S')

    ## Idle time
    while currenttime >= '23:40:00' and currenttime <= '23:59:59' or currenttime >= '00:00:00' and currenttime <= '11:30:00':

        ## Update time
        currenttime = strftime('%H:%M:%S')
        print currenttime, "Idling..."
        sleep(10)

        ## Update time
        currenttime = strftime('%H:%M:%S')

    ## Initiate the scheduler.
    runtime += random.randrange(intsmall, intlarge)
    s.enter(runtime, 1, function, ())
    s.run()

def execute_subscripts():

    st = time()
    print "Running..."
    errors = open('MISC/ERROR(S).txt', 'a')

    try: 
        execfile("SUBSCRIPTS/TESTSCRIPT.py", {})
    except Exception:
        errors.write(traceback.format_exc() + '\n')
        errors.write("\n\n")

    try: 
        execfile("SUBSCRIPTS/TEST.py", {})
    except Exception:
        errors.write(traceback.format_exc() + '\n')
        errors.write("\n\n")
##    subprocess.Popen(["pythonw", "SUBSCRIPTS/TEST.py", "0"], shell=True)

    try: 
        execfile("SUBSCRIPTS/TESTSCRIPTTest.py", {})
    except Exception:
        errors.write(traceback.format_exc() + '\n')
        errors.write("\n\n")

    try: 
        execfile("SUBSCRIPTS/TESTTESTTEST.py", {})
    except Exception:
        errors.write(traceback.format_exc() + '\n')
        errors.write("\n\n")

    errors.close()
    print """The whole routine took %.3f seconds""" % (time() - st)

while True:
    periodically(50, -25, +90, execute_subscripts)

希望这有助于直观地表示我想要实现的内容

正如您在问题中所指出的,您实际上提出了两个不同的问题(在后台运行,并强制执行超时)。幸运的是,两者的简短答案是一样的:

使用

Plumbum极大地简化了python脚本中类似shell脚本的元素,除其他外,还为在后台运行命令和强制超时提供了干净的接口

下面是一个使用铅的例子

在本例中,所有子流程都将运行相同的脚本--
subscript1.py
。它进行一些打印,一些睡眠,有时随机失败

subscript1.py

import os, sys, time, random
print '[pid=%s] STARTING %s' % (os.getpid(), sys.argv[0])
for i in range(3):
    t = random.randint(1,5)
    print '[pid=%s] sleeping for %s seconds' % (os.getpid(), t)
    time.sleep(t)
# fail randomly
if t == 5:
    raise RuntimeError('random error...')
print '[pid=%s] DONE %s' % (os.getpid(), sys.argv[0])
import os, sys, time
from plumbum import FG, BG, ProcessExecutionError, ProcessTimedOut
from plumbum.cmd import python

cmd = python['subscript1.py']  # create the command to run (several times)

def run_subscript(cmd, is_bg = False):
    print '[pid=%s] main running command: %s (is_bg=%s)' % (os.getpid(), cmd, is_bg)
    if is_bg:
        return (cmd > sys.stdout) & BG  # run in background
    else:
        try:
            return cmd & FG  # run in foreground
        except ProcessExecutionError, e:
            print >>sys.stderr, e

# run a process in the foreground        
run_subscript(cmd, is_bg = False)

# run two processes in the background, and one in the foreground
bg_proc1 = run_subscript(cmd, is_bg = True)
time.sleep(1)
bg_proc2 = run_subscript(cmd, is_bg = True)
time.sleep(1)
run_subscript(cmd, is_bg = False)

# wait for the background processes to finish
for bg_proc in ( bg_proc1, bg_proc2 ):
    try:
        bg_proc.wait()
    except ProcessExecutionError, e:
        print >>sys.stderr, e

# run a foreground process, which will time out
print '[pid=%s] main running command: %s (will time out)' % (os.getpid(), cmd)
try:
    cmd.run(timeout = 2)
except ProcessTimedOut, e:
    # command timed out
    print >>sys.stderr, e
except ProcessExecutionError, e:
    # command failed (but did not time out)
    print >>sys.stderr, e

现在,下面的主脚本,
main.py
,演示了如何在前台和后台运行子进程(有超时和无超时),等待后台进程完成,以及如何处理子进程错误和超时

main.py

import os, sys, time, random
print '[pid=%s] STARTING %s' % (os.getpid(), sys.argv[0])
for i in range(3):
    t = random.randint(1,5)
    print '[pid=%s] sleeping for %s seconds' % (os.getpid(), t)
    time.sleep(t)
# fail randomly
if t == 5:
    raise RuntimeError('random error...')
print '[pid=%s] DONE %s' % (os.getpid(), sys.argv[0])
import os, sys, time
from plumbum import FG, BG, ProcessExecutionError, ProcessTimedOut
from plumbum.cmd import python

cmd = python['subscript1.py']  # create the command to run (several times)

def run_subscript(cmd, is_bg = False):
    print '[pid=%s] main running command: %s (is_bg=%s)' % (os.getpid(), cmd, is_bg)
    if is_bg:
        return (cmd > sys.stdout) & BG  # run in background
    else:
        try:
            return cmd & FG  # run in foreground
        except ProcessExecutionError, e:
            print >>sys.stderr, e

# run a process in the foreground        
run_subscript(cmd, is_bg = False)

# run two processes in the background, and one in the foreground
bg_proc1 = run_subscript(cmd, is_bg = True)
time.sleep(1)
bg_proc2 = run_subscript(cmd, is_bg = True)
time.sleep(1)
run_subscript(cmd, is_bg = False)

# wait for the background processes to finish
for bg_proc in ( bg_proc1, bg_proc2 ):
    try:
        bg_proc.wait()
    except ProcessExecutionError, e:
        print >>sys.stderr, e

# run a foreground process, which will time out
print '[pid=%s] main running command: %s (will time out)' % (os.getpid(), cmd)
try:
    cmd.run(timeout = 2)
except ProcessTimedOut, e:
    # command timed out
    print >>sys.stderr, e
except ProcessExecutionError, e:
    # command failed (but did not time out)
    print >>sys.stderr, e
输出:

% python main.py
[pid=77311] main running command: /usr/local/bin/python subscript1.py (is_bg=False)
[pid=77314] STARTING subscript1.py
[pid=77314] sleeping for 1 seconds
[pid=77314] sleeping for 5 seconds
[pid=77314] sleeping for 3 seconds
[pid=77314] DONE subscript1.py
[pid=77311] main running command: /usr/local/bin/python subscript1.py (is_bg=True)
[pid=77316] STARTING subscript1.py
[pid=77316] sleeping for 5 seconds
[pid=77311] main running command: /usr/local/bin/python subscript1.py (is_bg=True)
[pid=77317] STARTING subscript1.py
[pid=77317] sleeping for 1 seconds
[pid=77311] main running command: /usr/local/bin/python subscript1.py (is_bg=False)
[pid=77317] sleeping for 5 seconds
[pid=77318] STARTING subscript1.py
[pid=77318] sleeping for 5 seconds
[pid=77316] sleeping for 2 seconds
[pid=77316] sleeping for 4 seconds
[pid=77317] sleeping for 5 seconds
[pid=77318] sleeping for 2 seconds
[pid=77318] sleeping for 3 seconds
[pid=77316] DONE subscript1.py
[pid=77318] DONE subscript1.py
Command line: ['/usr/local/bin/python', 'subscript1.py']
Exit code: 1
Stderr:  | Traceback (most recent call last):
         |   File "subscript1.py", line 13, in <module>
         |     raise RuntimeError('random error...')
         | RuntimeError: random error...
[pid=77311] main running command: /usr/local/bin/python subscript1.py (will time out)
('Process did not terminate within 2 seconds', ['/usr/local/bin/python', 'subscript1.py'])
%python main.py
[pid=77311]主运行命令:/usr/local/bin/python subscript1.py(is_bg=False)
[pid=77314]正在启动subscript1.py
[pid=77314]睡眠1秒钟
[pid=77314]睡眠5秒
[pid=77314]睡眠3秒
[pid=77314]完成下标1.py
[pid=77311]主运行命令:/usr/local/bin/python subscript1.py(is_bg=True)
[pid=77316]开始下标1.py
[pid=77316]睡眠5秒
[pid=77311]主运行命令:/usr/local/bin/python subscript1.py(is_bg=True)
[pid=77317]开始下标1.py
[pid=77317]睡眠1秒钟
[pid=77311]主运行命令:/usr/local/bin/python subscript1.py(is_bg=False)
[pid=77317]睡眠5秒
[pid=77318]开始订阅1.py
[pid=77318]睡眠5秒
[pid=77316]睡眠2秒
[pid=77316]睡眠4秒
[pid=77317]睡眠5秒
[pid=77318]睡眠2秒
[pid=77318]睡眠3秒
[pid=77316]完成下标1.py
[pid=77318]完成订阅1.py
命令行:['/usr/local/bin/python','subscript1.py']
退出代码:1
Stderr:|回溯(最近一次呼叫最后一次):
|文件“subscript1.py”,第13行,在
|引发运行时错误('随机错误…')
|运行时错误:随机错误。。。
[pid=77311]主运行命令:/usr/local/bin/python subscript1.py(将超时)
('Process未在2秒内终止',['/usr/local/bin/python','subscript1.py'])
编辑:

我现在意识到我的示例代码并没有演示在后台运行命令并对其强制超时。为此,只需使用
cmd.bgrun(…)
而不是
cmd.run(…)

您收到的错误与重定向有关,并且必须与您在Windows上运行的事实有关。这可能是plumbum在Windows上的兼容性问题,或者我的代码可能不完美,也就是说,可能有另一种方法可以使用plumbum使其工作。不幸的是,我没有windows机器来测试它


我希望这能有所帮助。

如果我了解您要做的事情,subprocess.Popen()就是最好的选择。下面是一个简单的类,我认为它可以提供您想要的所有功能:

from time import sleep
import subprocess
import datetime
import os

class Worker:

    def __init__(self, cmd):

        print datetime.datetime.now(), ":: starting subprocess :: %s"%cmd
        self.cmd = cmd
        self.log = "[running :: %s]\n"%cmd
        self.subp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        self.start_time = datetime.datetime.now()

    def wait_to_finish(self, timeout_seconds = None):

        while True:
            retcode = self.subp.poll()
            if retcode is not None:
                self.get_process_output()
                self.log += "\n[subprocess finished, return code: %d]\n"%retcode
                print datetime.datetime.now(), ":: subprocess %s exited, retcode=%d"%(self.cmd, retcode)
                return
            else:
                # process hasn't finished yet
                sleep(1)
                if timeout_seconds is not None:
                    cur_time = datetime.datetime.now()
                    if (cur_time - self.start_time).seconds > timeout_seconds:
                        print datetime.datetime.now(), ":: subprocess %s :: killing after %d seconds"%(self.cmd, timeout_seconds)
                        self.kill()
                        return

    def still_running(self):
        return (self.subp.poll() is None)

    def kill(self):
        self.subp.terminate()
        self.get_process_output()
        self.log += "\n[subprocess killed by explicit request]\n"
        return

    def get_process_output(self):
        out, err = self.subp.communicate()
        self.log += out
        self.log += err
您发出命令,然后类在后台启动它。然后,您可以等待它完成,并选择超时(从进程启动时开始计算)。您可以获得进程输出,如果需要,可以显式地终止进程

下面是一个显示其功能的快速示例:

# Start two subprocesses in the background
worker1 = Worker([r'c:\python26\python.exe', 'sub1.py'])
worker2 = Worker([r'c:\python26\python.exe', 'sub2.py'])

# Wait for both to finish, kill after 10 seconds timeout
worker1.wait_to_finish(timeout_seconds = 10)
worker2.wait_to_finish(timeout_seconds = 10)

# Start another subprocess giving it 5 seconds to finish
worker3 = Worker([r'c:\python26\python.exe', 'sub3.py'])
worker3.wait_to_finish(timeout_seconds = 5)

print "----LOG1----\n" + worker1.log
print "----LOG2----\n" + worker2.log
print "----LOG3----\n" + worker3.log
sub1.py:

from time import sleep
print "sub1 output: start"
sleep(5)
print "sub1 output: finish"
sub2.py:

print "sub2 output: start"
erroneous_command()
sub3.py:

from time import sleep
import sys
print "sub3 output: start, sleeping 15 sec"
sys.stdout.flush()
sleep(15)
print "sub3 output: finish"
以下是输出:

2013-11-06 15:31:17.296000 :: starting subprocess :: ['c:\\python26\\python.exe', 'sub1.py']
2013-11-06 15:31:17.300000 :: starting subprocess :: ['c:\\python26\\python.exe', 'sub2.py']
2013-11-06 15:31:23.306000 :: subprocess ['c:\\python26\\python.exe', 'sub1.py'] exited, retcode=0
2013-11-06 15:31:23.309000 :: subprocess ['c:\\python26\\python.exe', 'sub2.py'] exited, retcode=1
2013-11-06 15:31:23.310000 :: starting subprocess :: ['c:\\python26\\python.exe', 'sub3.py']
2013-11-06 15:31:29.314000 :: subprocess ['c:\\python26\\python.exe', 'sub3.py'] :: killing after 5 seconds
----LOG1----
[running :: ['c:\\python26\\python.exe', 'sub1.py']]
sub1 output: start
sub1 output: finish

[subprocess finished, return code: 0]

----LOG2----
[running :: ['c:\\python26\\python.exe', 'sub2.py']]
sub2 output: start
Traceback (most recent call last):
  File "sub2.py", line 2, in <module>
    erroneous_command()
NameError: name 'erroneous_command' is not defined

[subprocess finished, return code: 1]

----LOG3----
[running :: ['c:\\python26\\python.exe', 'sub3.py']]
sub3 output: start, sleeping 15 sec

[subprocess killed by explicit request]
2) 如果您有多个定义良好的脚本序列,希望每10秒“触发并忘记”,则将每个序列放入自己的.py脚本(使用“导入工作者”)中,并每10秒启动所有序列,同时定期检查哪些序列已退出以收集其日志


3) 如果您的序列是动态定义的,并且您更喜欢“触发并忘记”方法,那么线程将是最好的方法。

超时对任何事情都有效。@kazagistar我得到跟踪:
def timeout(秒=10,错误消息=os.strerror(errno.ETIME)):AttributeError:“module”对象没有属性“ETIME”
,因为我在Windows上,还有其他选择吗?我测试了代码,不知怎的,你得到的输出比我大。我正在运行完全相同的代码,并且我的错误被排除了。我已经在主帖子中添加了一份打印和回溯的副本,以及我想要实现的一点描述(我不认为plumbum(一个多么奇怪的名字)可以实现我想要的…)好的,我会检查错误。
while True:
    # check time
    # check currently running processes :: workerX.still_running()
    #   -> if some are past their timeout, kill them workerX.kill()
    # start new subprocesses according to your scheduling logic
    sleep(1)