Twisted Python - 在 deferred 的回调函数调用中,引发的异常未显示在标准输出中
Twisted Python - In deferred's callback's function call, raised exception not displayed in stdout
测试设置
我有一个用于处理的异步 Twisted HTTP 服务器 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!")
在程序输出中捕获异常消息(即"Exception raised!")
如果我在 render_POST() 中调用 test(),则会引发异常并按预期输出显示消息:
Traceback (most recent call last):
raise Exception("Exception!")
exceptions.Exception: Exception!
但是,如果在 render_POST() 中我创建了一个指向执行 test() 的回调的延迟指向,则可能仍会引发异常(客户端收到 500 内部服务器错误)但没有异常错误消息("Exception raised!" 短语)显示。
问题
当回调中的函数调用引起异常错误消息时,如何显示它们?
更新 1:提供了演示代码(server.py 需要 Twisted)
**更新 2:演示代码 returns "NOT_DONE_YET"
client.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()
server.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
return None
时,这告诉 twisted.web 您已完成请求处理并且连接已完成应立即关闭。当您的回调开始将一些数据推送到 HTTP 通道时,响应已经发送并且数据被丢弃。
第二个问题是您实际上根本没有将数据推送到连接。当 render_POST 本身引发异常时,调用它的代码可以捕获该异常。但是,调用 render_POST
的代码并不期望 Deferred
,即使它期望,您也不会 return 在这里。
还有第三个问题,就是你使用的 time.sleep
会冻结你的整个程序,而不是 deferLater
会 return 一个 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
结果它将继续以与现在相同的方式继续失败。
测试设置
我有一个用于处理的异步 Twisted HTTP 服务器 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!")
在程序输出中捕获异常消息(即"Exception raised!")
如果我在 render_POST() 中调用 test(),则会引发异常并按预期输出显示消息:
Traceback (most recent call last):
raise Exception("Exception!")
exceptions.Exception: Exception!
但是,如果在 render_POST() 中我创建了一个指向执行 test() 的回调的延迟指向,则可能仍会引发异常(客户端收到 500 内部服务器错误)但没有异常错误消息("Exception raised!" 短语)显示。
问题
当回调中的函数调用引起异常错误消息时,如何显示它们?
更新 1:提供了演示代码(server.py 需要 Twisted)
**更新 2:演示代码 returns "NOT_DONE_YET"
client.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()
server.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
return None
时,这告诉 twisted.web 您已完成请求处理并且连接已完成应立即关闭。当您的回调开始将一些数据推送到 HTTP 通道时,响应已经发送并且数据被丢弃。
第二个问题是您实际上根本没有将数据推送到连接。当 render_POST 本身引发异常时,调用它的代码可以捕获该异常。但是,调用 render_POST
的代码并不期望 Deferred
,即使它期望,您也不会 return 在这里。
还有第三个问题,就是你使用的 time.sleep
会冻结你的整个程序,而不是 deferLater
会 return 一个 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
结果它将继续以与现在相同的方式继续失败。