如何在Python simplhttpserver中禁用输出缓冲
下面这段代码是一个脚本的一部分,这个脚本实现了一个简单的HTTP服务器实例,当收到GET请求时,它会触发一个第三方模块。我可以捕捉到这个第三方模块的标准输出信息,并把这些信息发送到网页浏览器上。
目前,这个脚本会收集所有的标准输出信息,并在调用的模块完成后一次性发送给客户端……
因为我希望每条信息在发送到标准输出时就能在浏览器中显示,所以需要关闭输出缓冲。
我该如何在Python的简单HTTP服务器中做到这一点呢?
def do_GET(self):
global key
stdout_ = sys.stdout #Keep track of the previous value.
stream = cStringIO.StringIO()
sys.stdout = stream
''' Present frontpage with user authentication. '''
if self.headers.getheader('Authorization') == None:
self.do_AUTHHEAD()
self.wfile.write('no auth header received')
pass
elif self.headers.getheader('Authorization') == 'Basic '+key:
if None != re.search('/api/v1/check/*', self.path):
recordID = self.path.split('/')[-1]
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'GET,POST,PUT,OPTIONS')
self.send_header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Authorization")
self.end_headers()
notStarted = True
while True:
if notStarted is True:
self.moduleXYZ.start()
notStarted is False
if "finished" in stream.getvalue():
sys.stdout = stdout_ # restore the previous stdout.
self.wfile.write(stream.getvalue())
break
更新
我修改了方法,从类中获取状态信息,而不是使用标准输出。我还加入了Martijn的好主意,来跟踪变化。
现在我运行服务器时,发现我真的需要使用线程吗?看起来这个脚本在完成之前会一直等待,然后才进入循环。
我应该在服务器中实现线程,还是在模块类中实现线程呢?
def do_GET(self):
global key
''' Present frontpage with user authentication. '''
if self.headers.getheader('Authorization') == None:
self.do_AUTHHEAD()
self.wfile.write('no auth header received')
pass
elif self.headers.getheader('Authorization') == 'Basic '+key:
if None != re.search('/api/v1/check/*', self.path):
recordID = self.path.split('/')[-1]
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'GET,POST,PUT,OPTIONS')
self.send_header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Authorization")
self.end_headers()
self.moduleABC.startCrawl()
while True:
if self.moduleABC.done:
print "done"
break
output = self.moduleABC.statusMessages
self.wfile.write(output[sent:])
sent = len(output)
else:
self.send_response(403)
self.send_header('Content-Type', 'application/json')
self.end_headers()
更新 2(已解决)
这是我更新后的GET方法。第三方模块的类对象是在GET方法中实例化的。模块的主方法是在一个线程中运行的。我使用了Martijn的想法来监控进度。
我花了一些时间才明白,必须在发送给浏览器的状态文本中附加一些额外的字节,以强制刷新缓冲区!
感谢你们的帮助!
def do_GET(self):
global key
abcd = abcdModule(u"abcd")
''' Present frontpage with user authentication. '''
if self.headers.getheader('Authorization') == None:
self.do_AUTHHEAD()
self.wfile.write('no auth header received')
pass
elif self.headers.getheader('Authorization') == 'Basic '+key:
if None != re.search('/api/v1/check/*', self.path):
recordID = self.path.split('/')[-1]
abcd.setMasterlist([urllib.unquote(recordID)])
abcd.useCaching = False
abcd.maxRecursion = 1
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'GET,POST,PUT,OPTIONS')
self.send_header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Authorization")
self.end_headers()
thread.start_new_thread(abcd.start, ())
sent = 0
while True:
if abcd.done:
print "done"
break
output = abcd.statusMessages
if len(output) == sent + 1:
print abcd.statusMessages[-1]
self.wfile.write(json.dumps(abcd.statusMessages))
self.wfile.write("".join([" " for x in range(1,1000)]))
sent = len(output)
else:
self.send_response(403)
self.send_header('Content-Type', 'application/json')
self.end_headers()
else:
self.do_AUTHHEAD()
self.wfile.write(self.headers.getheader('Authorization'))
self.wfile.write('not authenticated')
pass
return
1 个回答
你真的需要修改 moduleXYZ
,让它不要只通过 stdout
来输出信息。这样做会让这个模块不适合在 多线程 的服务器中使用。例如,如果两个不同的线程同时调用 moduleXYZ
,那么它们的输出会混在一起,变得不可预测。
不过,这里并没有进行 流缓冲。你实际上是把所有的 stdout
信息都捕获到一个 cStringIO
对象中,只有当你在捕获的字符串中看到 "finished"
这个词时,才会输出结果。你应该做的是持续输出这个值,并跟踪你已经发送了多少内容:
self.moduleXYZ.start()
sent = 0
while True:
output = stream.getvalue()
self.wfile.write(output[sent:])
sent = len(output)
if "finished" in output:
sys.stdout = stdout_
break
更好的方法是直接把 stdout
连接到 self.wfile
,让模块直接写入响应;在这种情况下,你需要用不同的方法来检测模块线程是否完成:
old_stdout = sys.stdout
sys.stdout = self.wfile
self.moduleXYZ.start()
while True:
if self.moduleXYZ.done():
sys.stdout = old_stdout
break