无法在中断的系统调用中重试

时间:2016-04-12 13:19:24

标签: python

我最近遇到了一个问题,我们遇到间歇性问题,内部网站由于系统调用中断而无法加载。我们正在使用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。

1 个答案:

答案 0 :(得分:3)

<urlopen error [Errno 4] Interrupted system call>表示它实际上是来自urllib2URLError,它是IOError的子类,但完全不同地处理参数。这就是未初始化属性errnostrerror的原因。它都传递字符串作为理由:

        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.errorhas (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

  1. 这只是一根绳子吗?如果是这样,那么任何地方都不会有 errno
  2. socket.error吗?处理那个怪癖。同样, errno 属性可以取消设置,或None,因为它也可以使用single string argument引发。
  3. 它是IOError还是OSError(哪个子类EnvironmentError)的子类?阅读 errno 属性 - 希望最好。
  4. 对于您的情况,这可能并且可能过于谨慎,但理解边缘是很好的。 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