Python-我们可以在Python应用程序中使用tempfile和subprocess获取非缓冲的实时输出吗?

4
我正在尝试从Python Windows应用程序中运行一个Python文件。为此,我使用了subprocess。为了在应用程序控制台上获取实时流输出,我尝试了以下语句。
使用PIPE:
p = subprocess.Popen(cmd,
                     stdout=subprocess.PIPE,
                     stderr=subprocess.STDOUT, shell=True)

for line in iter(p.stdout.readline, ''):
    print line

(或)

process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while True:
    out = process.stdout.read(1)
    if out == '' and process.poll() != None:
        break
    if out != '':
        sys.stdout.write(out)
        sys.stdout.flush()

除了上面的代码外,我尝试了很多方法,但结果都像下面一样:

1. Python窗口应用程序运行时间过长

2. 然后应用程序窗口长时间处于“未响应”状态

3. 然后整个输出被打印到控制台上

我知道Python应用程序中正在发生缓冲区溢出,因此我无法获取实时输出。

我已经发布了很多关于这个问题的查询,但仍然没有得到解决方案。

刚刚找到并尝试了tempfile。但我不确定它是否会提供实时流式输出。

我应该尝试这种方式吗?

import tempfile
import subprocess

w = tempfile.NamedTemporaryFile()
p = subprocess.Popen(cmd, shell=True, stdout=w, 
                        stderr=subprocess.STDOUT, bufsize=0)

with open(w.name, 'r') as r:
    for line in r:
        print line
w.close()

有没有其他最佳解决方案来实现 Windows 应用程序上无阻塞、非缓冲的实时输出。

希望能得到任何帮助。

注意

  1. 我想要运行的 Python 文件具有更多的打印语句(即更多内容)。
  2. Windows Server 2012,Python 2.7。
1个回答

6

我理解您的挫败感。看起来您已经接近答案了。

我在 这篇SO文章 的答案基础上进行了补充。但是那个答案没有使用 TemporaryFile,而且我使用了来自 此处 的 tail follow 方法,我发现这种方法可以以最快的速度将大量输出发送到终端。这消除了对 print 的不必要调用。

附注:如果您还有其他异步任务要完成,则可以将导入代码下面的代码封装成一个函数,并使用 gevent 包,并从中导入 sleepPopen、STDOUTgevent.subprocess 中导入。这就是我正在做的事情,这可能会帮助您避免剩余的减速(我提出这个原因只是为了提醒您)。

import sys
from tempfile import TemporaryFile
from time import sleep
from subprocess import Popen, STDOUT

# the temp file will be automatically cleaned up using context manager
with TemporaryFile() as output:
    sub = Popen(cmd, stdout=output, stderr=STDOUT, shell=True)
    # sub.poll returns None until the subprocess ends,
    # it will then return the exit code, hopefully 0 ;)
    while sub.poll() is None:
        where = output.tell()
        lines = output.read()
        if not lines:
            # Adjust the sleep interval to your needs
            sleep(0.1)
            # make sure pointing to the last place we read
            output.seek(where)
        else:
            sys.__stdout__.write(lines)
            sys.__stdout__.flush()
    # A last write needed after subprocess ends
    sys.__stdout__.write(output.read())
    sys.__stdout__.flush()

使用 sys.stdout.flush() 属性会出现错误。AttributeError: 'TextCtrl' 对象没有 'flush' 属性。而且 Python 应用程序的行为没有任何改变 :-( - Nithya
如果您删除sys.stdout.flush()语句会发生什么? - John Lunzer
你的错误表明在程序早期某个时刻重新分配了 sys.stdout,因为 sys.stdout 的原始值__应该__具有 flush 方法。我对发布的代码进行了编辑,将使用 stdout 的原始值。请尝试一下。 - John Lunzer
你的应用程序控制台使用哪个框架/包?这很可能是问题的根源。如果它可以成功地打印到命令提示符,则我提供的代码是有效的。在这一点上,你需要更好地理解控制输出到“应用程序控制台”的数据结构。你最初的问题没有提供有关标准命令提示符以外的控制台的详细信息。你可以编辑你最初的问题,或者接受这个答案并提出一个新的问题,详细说明你的框架作为问题的来源。我建议选择后者。 - John Lunzer
是的,这个可以工作了。我用你的代码找到了在我的应用程序控制台中打印的方法。谢谢。 - Nithya
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接