扭曲的 Python - 在 Delay 的回调函数调用中,在标准输出中未显示引发的异常



测试设置

我有一个异步TwistedHTTP服务器用于处理POST:

class HttpResource(Resource):
    isLeaf = True;
    def render_POST(self, request):
        ...
if __name__ == "__main__":
    factory = Site(HttpResource())
    reactor.listenTCP(8000, factory)
    reactor.run()

我创建了一个简单的test()函数,它引发了一个异常:

def test():
    print "test() called"
    raise Exception("Exception raised!")

捕获程序输出中的异常消息(即"引发异常!")

如果我在render_POST()中调用test(),则会引发异常,并且输出显示预期的消息:

Traceback (most recent call last):
    raise Exception("Exception!")
exceptions.Exception: Exception!

但是,如果在render_POST()中创建一个延迟指向执行test()的回调的指针,则可能仍然会引发异常(客户端收到500内部服务器错误),但不会显示异常错误消息("引发异常!"短语)。

问题

当异常错误消息来自回调中的函数调用时,我如何才能显示它们?

更新1:提供了演示代码(需要Twisted for server.py)
**更新2:演示代码返回"NOT_DONE_YET"

客户.py

import json, urllib2
if __name__ == "__main__":
    while True:
        cmd = raw_input("Enter cmd number (1, 2, or 0): ")
        if cmd == str(1):
            raw_data = {'cmd' : 'cmd1'}
        elif cmd == str(2):
            raw_data = {'cmd' : 'cmd2'}
        elif cmd == str(0):
            break;
        req = urllib2.Request('http://localhost:8000')
        req.add_header('Content-Type', 'application/json')
        response = urllib2.urlopen(req, json.dumps(raw_data))
        print response.read()        

服务器.py

import json, time
from twisted.internet import reactor
from twisted.web.client import getPage
from twisted.web.resource import Resource
from twisted.web.server import Site, NOT_DONE_YET
def test():
    print "test() called"
    time.sleep(1)
    raise Exception("Exception raised!")
def callback(result):
    print "callback() called"
    test()
class HttpResource(Resource):
    isLeaf = True;
    def render_POST(self, request):
        msg = json.loads(request.content.getvalue())
        if msg['cmd'] == 'cmd1': # call test() directly
            test()
        elif msg['cmd'] == 'cmd2': # call test() from callback
            d = getPage('http://www.yahoo.com')
            d.addCallbacks(callback, callback)
        return NOT_DONE_YET
if __name__ == "__main__":
    factory = Site(HttpResource())
    reactor.listenTCP(8000, factory)
    reactor.run()

这里有两个问题与您丢失的回溯有关。

程序的第一个问题是,当您从render_POST返回None时,这会告诉twisted.web您已经处理完请求,应该立即关闭连接。当回调将一些数据推送到HTTP通道时,响应已经发送,数据将被丢弃。

第二个问题是,您从来没有真正将数据推送到连接。当render_POST本身引发异常时,调用它的代码可以捕获该异常。但是,调用render_POST的代码不需要Deferred,即使需要,也不会在此处返回。

还有第三个问题,那就是你使用的是time.sleep,它冻结了你的整个程序,而不是deferLater,它会返回一个Deferred,它只是在以后触发,并允许并发任务继续进行。由于time.sleep完全偏离了要点,并且无论是否包含它,行为都是相同的,因此我们可以忽略它:-)。

有一种方法可以在Twisted内部解决这个问题,但更简单的方法是使用Klein,它可以自动处理Deferred

使用Klein,您的示例是:

import json
from klein import run, route
from twisted.web.client import getPage
def test():
    print "test() called"
    raise Exception("Exception raised!")
def callback(result):
    print "callback() called"
    test()
@route("/", methods=["POST"])
def example(request):
    msg = json.loads(request.content.getvalue())
    if msg['cmd'] == 'cmd1': # call test() directly
        test()
    elif msg['cmd'] == 'cmd2': # call test() from callback
        d = getPage('http://www.yahoo.com')
        return d.addCallbacks(callback, callback)
run("localhost", 8000)

注意return d.addCallbacks;如果你不return,结果会继续失败,或多或少和现在一样。

最新更新