我想捕获一个长期运行的输出,但冗长的脚本(目前是最快的),并打印在网页上的进展.我在这里面临着两个问题
首先:我需要异步运行该脚本,并在它仍在运行时捕获输出.我发现PYTEST上有--capture=tee-sys
的旗帜,我想是为了这个目的,但我找不到它是如何工作的.
一种更通用的方法是使用subprocess.Popen
,它似乎已经处理了异步部分,并且输出可以通过管道传输.然而,如果我用Popen.communicat()
捕获,我要么阻塞线程,直到进程结束,我不能实时更新,要么我必须不断地捕获TimeoutExpired
异常并重新启动通信,这感觉像是一种解决办法,但可能会起作用.但有没有更简单的方法来实现这一点呢?
第二:我需要在没有收到请求的情况下更新前端.起初,我甚至认为有一个终端风格的输出会很酷,并找到了类似ttyd的东西,但这似乎有点夸张,如果我没有弄错的话,将允许用户向终端输入我不想要的东西.现在我还找到了this answer,这建议使用iframe和flask documentation中的stream_template_context()
,然而,如果我在这两种情况下都没有弄错,我似乎需要使用子进程收集的数据在运行中构建一个有效的html页面,这感觉很容易出错.我还找到了flask-socketio,这是我想要避免的,因为Falsk服务器应该运行在定制的Linux上,我需要首先添加这个模块.这最终应该是可行的,但如果有更简单的方法,我更愿意这样做.我曾考虑过从前端的Java脚本轮询,但这看起来又像是一种变通办法.在这里最好的做法是什么?
对于这两个部分,我都很高兴有更多的资源可以阅读最佳实践等内容.
经过更多的研究,我得到了一个如下所示的最小工作示例:
from flask import Flask, Response, render_template
from subprocess import Popen, PIPE
app = Flask(__name__)
@app.route('/content')
def content():
# start subprocess
def inner():
# proc = Popen(['/usr/local/bin/pytest', 'test1.py'], stdout=PIPE)
with Popen(['/usr/local/bin/pytest', 'test1.py'], stdout=PIPE) as proc:
while proc.poll() is None:
line = proc.stdout.readline()
# yield line.decode() + '<br/>\n'
yield str(line) + '<br/>\n'
return Response(inner(), mimetype='text/html')
@app.route('/')
def index():
return render_template('test.html')
if __name__ == '__main__':
app.run(debug=True)
其中test.py
只定义了一系列测试函数,每个Hibernate
秒和assert True
只是为了获得所需的样本输出,
和test.html
分:
<!doctype html>
<head>
<title>Title</title>
</head>
<body>
<div>
<iframe frameborder="1"
width="52%"
height="500px"
style='background: transparent;'
src="{{ url_for('content')}}">
</iframe>
</div>
</body>
然而,像这样使用proc.stdout.readline()
的subprocess documentation个州是不受欢迎的,因为它会导致子进程死锁.
此外,在子进程终止后,我有时会收到几行,有时会有很多空行.我使用str(line)
使它们可见,因为字节字符串中的b''
就在那里.
你知道接下来该怎么做吗?