如何编写兼容Python 2和Python 3的异常重新引发代码?
我正在尝试让我的WSGI服务器实现能够同时兼容Python 2和Python 3。我之前有这样的代码:
def start_response(status, response_headers, exc_info = None):
if exc_info:
try:
if headers_sent:
# Re-raise original exception if headers sent.
raise exc_info[0], exc_info[1], exc_info[2]
finally:
# Avoid dangling circular ref.
exc_info = None
elif headers_set:
raise AssertionError("Headers already set!")
headers_set[:] = [status, response_headers]
return write
...相关的部分是:
# Re-raise original exception if headers sent.
raise exc_info[0], exc_info[1], exc_info[2]
但是Python 3不再支持那种写法,所以必须改成:
raise exc_info[0].with_traceback(exc_info[1], exc_info[2])
问题是:Python 2的写法在Python 3中会产生解析错误。我该怎么写代码才能让Python 2和Python 3都能理解呢?我试过以下方法,但都不行:
if sys.version_info[0] >= 3:
raise exc_info[0].with_traceback(exc_info[1], exc_info[2])
else:
eval("raise exc_info[0], exc_info[1], exc_info[2]; 1", None, { 'exc_info': exc_info })
2 个回答
51
你可以使用 six
吗?它就是为了解决这个问题而存在的。
import six, sys
six.reraise(*sys.exc_info())
0
你可以尝试一些创意的做法。
在你代码的开头,比如在构造函数那里,检查一下你使用的Python版本。因为你平常用的版本检查方法不管用了,可以试试这个:
try:
eval('a python 3 expression') # something that only works in python3+
python_version = 3
except:
python_version = 2
然后你代码的其他部分就可以很方便地引用这个,知道该用什么。
至于解析错误,你可以在一个函数里使用exec,像这样:
def what_to_run():
if python_version = 3:
return 'raise exc_info[0].with_traceback(exc_info[1], exc_info[2])'
else:
return 'raise exc_info[0], exc_info[1], exc_info[2]'
在你的函数里,你可以写这个:
def start_response(status, response_headers, exc_info = None):
if exc_info:
try:
if headers_sent:
# Re-raise original exception if headers sent.
exec(what_to_run())
finally:
# Avoid dangling circular ref.
exc_info = None
elif headers_set:
raise AssertionError("Headers already set!")
headers_set[:] = [status, response_headers]
return write
虽然有点乱,没经过测试,但它应该能工作,至少你明白这个思路。