我最近遇到了一个问题,我们遇到间歇性问题,内部网站由于系统调用中断而无法加载。我们正在使用urllib2访问该网站。我不能分享确切的代码,但基本上我们是这样做的:
payload = {'userName': user_name,
'emailAddress': email_address,
'password': password}
headers = {'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': token}
values = json.dumps(payload)
req = urllib2.Request(url, values, headers)
try:
response = urllib2.urlopen(req, timeout=30)
break
except IOError, e:
if e.errno != errno.EINTR:
print e.errno
raise
我们记录了errono和引发的异常。例外是:
IOError: <urlopen error [Errno 4] Interrupted system call>
而且错误是None
。我预计它会是4。
有没有更好的方法在Python 2.7中捕获此错误?我知道PEP475,但我们现在无法升级到Python 3。
答案 0 :(得分:3)
<urlopen error [Errno 4] Interrupted system call>
表示它实际上是来自urllib2
的URLError
,它是IOError
的子类,但完全不同地处理参数。这就是未初始化属性errno
和strerror
的原因。它都传递字符串作为理由:
raise URLError("qop '%s' is not supported." % qop)
并包装来自其他来源的例外:
try:
h.request(req.get_method(), req.get_selector(), req.data, headers)
except socket.error, err: # XXX what error?
h.close()
raise URLError(err)
这就是为什么你不会在通常的地方找到 errno :
>>> try:
urlopen('http://asdf')
except URLError, e:
pass
...
>>> e
URLError(gaierror(-2, 'Name or service not known'),)
>>> e.errno
>>> e.reason
gaierror(-2, 'Name or service not known')
>>> e.reason.errno
-2
这适用于这种情况,但原因属性可以是字符串或socket.error
,has (had) its own problems with errno。
urllib2.py 中URLError
的定义:
class URLError(IOError):
# URLError is a sub-type of IOError, but it doesn't share any of
# the implementation. need to override __init__ and __str__.
# It sets self.args for compatibility with other EnvironmentError
# subclasses, but args doesn't have the typical format with errno in
# slot 0 and strerror in slot 1. This may be better than nothing.
def __init__(self, reason):
self.args = reason,
self.reason = reason
def __str__(self):
return '<urlopen error %s>' % self.reason
这么长的故事,这是一个可怕的混乱。您必须检查e.reason
socket.error
吗?处理那个怪癖。同样, errno 属性可以取消设置,或None
,因为它也可以使用single string argument引发。IOError
还是OSError
(哪个子类EnvironmentError
)的子类?阅读 errno 属性 - 希望最好。对于您的情况,这可能并且可能过于谨慎,但理解边缘是很好的。 Tornado had similar issues正在使用utility function to get errno from exception,但很遗憾,该功能不适用于 URLErrors 。
至少在某些情况下可以涵盖什么:
while True: # or some amount of retries
try:
response = urllib2.urlopen(req, timeout=30)
break
except URLError, e:
if getattr(e.reason, 'errno', None) == errno.EINTR:
# Retry
continue