如何在Python simplehttpserver中禁用输出缓冲

时间:2014-07-28 12:12:25

标签: python python-2.7

以下脚本片段是实现simplehttpserver实例的脚本的一部分,该实例在GET请求时触发第三方模块。我能够捕获第三方模块的stdout消息并将它们发送到webbrowser。

目前,当调用的模块完成后,脚本会收集所有stdout消息并将它们转储到客户端....

由于我希望每条消息在发送到stdout时出现在浏览器中,因此需要禁用输出缓冲。

我如何在pythons simplehttpserver中做到这一点?

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

更新

我修改了从类中获取状态消息的方法,而不是使用stdout。我对Martijns如何跟踪变化提出了很好的想法。

当我现在运行服务器时,我意识到我真的需要线程化?似乎脚本在进入while循环之前等待它完成。

我应该更好地在服务器或模块类中实现线程吗?

   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方法中实例化。模块的main方法在一个线程中运行。我使用Martijns的想法来监控进度。

我花了一段时间才弄清楚是否需要在发送到浏览器的状态文本中添加一些额外的字节来强制缓冲区刷新!

感谢您的帮助。

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 个答案:

答案 0 :(得分:0)

您真的想要修复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