From: Doug Goldstein Date: Fri, 28 Feb 2014 14:48:26 +0000 (-0600) Subject: define and use errno_from_exception abstraction X-Git-Tag: v4.0.0b1~108^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F996%2Fhead;p=thirdparty%2Ftornado.git define and use errno_from_exception abstraction If an OSError or IOError are instantiated without an errno value, e.g. e = OSError(). The existing code would give an IndexError: tuple index out of range. However there have been cases that the errno attribute wasn't populated so instead of switching to it this introduces a helper function to ensure we get the errno value through whatever means possible. --- diff --git a/tornado/ioloop.py b/tornado/ioloop.py index 77fb46591..80496e193 100644 --- a/tornado/ioloop.py +++ b/tornado/ioloop.py @@ -45,6 +45,7 @@ from tornado.concurrent import TracebackFuture, is_future from tornado.log import app_log, gen_log from tornado import stack_context from tornado.util import Configurable +from tornado.util import errno_from_exception try: import signal @@ -724,9 +725,7 @@ class PollIOLoop(IOLoop): # two ways EINTR might be signaled: # * e.errno == errno.EINTR # * e.args is like (errno.EINTR, 'Interrupted system call') - if (getattr(e, 'errno', None) == errno.EINTR or - (isinstance(getattr(e, 'args', None), tuple) and - len(e.args) == 2 and e.args[0] == errno.EINTR)): + if errno_from_exception(e) == errno.EINTR: continue else: raise @@ -746,7 +745,7 @@ class PollIOLoop(IOLoop): fd_obj, handler_func = self._handlers[fd] handler_func(fd_obj, events) except (OSError, IOError) as e: - if e.args[0] == errno.EPIPE: + if errno_from_exception(e) == errno.EPIPE: # Happens when the client closes the connection pass else: diff --git a/tornado/iostream.py b/tornado/iostream.py index faf657a4c..40e75a534 100644 --- a/tornado/iostream.py +++ b/tornado/iostream.py @@ -40,7 +40,7 @@ from tornado import ioloop from tornado.log import gen_log, app_log from tornado.netutil import ssl_wrap_socket, ssl_match_hostname, SSLCertificateError from tornado import stack_context -from tornado.util import bytes_type +from tornado.util import bytes_type, errno_from_exception try: from tornado.platform.posix import _set_nonblocking @@ -766,8 +766,8 @@ class IOStream(BaseIOStream): # returned immediately when attempting to connect to # localhost, so handle them the same way as an error # reported later in _handle_connect. - if (e.args[0] != errno.EINPROGRESS and - e.args[0] not in _ERRNO_WOULDBLOCK): + if (errno_from_exception(e) != errno.EINPROGRESS and + errno_from_exception(e) not in _ERRNO_WOULDBLOCK): gen_log.warning("Connect error on fd %s: %s", self.socket.fileno(), e) self.close(exc_info=True) @@ -1026,9 +1026,9 @@ class PipeIOStream(BaseIOStream): try: chunk = os.read(self.fd, self.read_chunk_size) except (IOError, OSError) as e: - if e.args[0] in _ERRNO_WOULDBLOCK: + if errno_from_exception(e) in _ERRNO_WOULDBLOCK: return None - elif e.args[0] == errno.EBADF: + elif errno_from_exception(e) == errno.EBADF: # If the writing half of a pipe is closed, select will # report it as readable but reads will fail with EBADF. self.close(exc_info=True) diff --git a/tornado/netutil.py b/tornado/netutil.py index a4699138e..171873e66 100644 --- a/tornado/netutil.py +++ b/tornado/netutil.py @@ -27,7 +27,7 @@ import stat from tornado.concurrent import dummy_executor, run_on_executor from tornado.ioloop import IOLoop from tornado.platform.auto import set_close_exec -from tornado.util import u, Configurable +from tornado.util import u, Configurable, errno_from_exception if hasattr(ssl, 'match_hostname') and hasattr(ssl, 'CertificateError'): # python 3.2+ ssl_match_hostname = ssl.match_hostname @@ -84,7 +84,7 @@ def bind_sockets(port, address=None, family=socket.AF_UNSPEC, backlog=128, flags try: sock = socket.socket(af, socktype, proto) except socket.error as e: - if e.args[0] == errno.EAFNOSUPPORT: + if errno_from_exception(e) == errno.EAFNOSUPPORT: continue raise set_close_exec(sock.fileno()) @@ -133,7 +133,7 @@ if hasattr(socket, 'AF_UNIX'): try: st = os.stat(file) except OSError as err: - if err.errno != errno.ENOENT: + if errno_from_exception(err) != errno.ENOENT: raise else: if stat.S_ISSOCK(st.st_mode): @@ -165,12 +165,12 @@ def add_accept_handler(sock, callback, io_loop=None): except socket.error as e: # EWOULDBLOCK and EAGAIN indicate we have accepted every # connection that is available. - if e.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN): + if errno_from_exception(e) in (errno.EWOULDBLOCK, errno.EAGAIN): return # ECONNABORTED indicates that there was a connection # but it was closed while still in the accept queue. # (observed on FreeBSD). - if e.args[0] == errno.ECONNABORTED: + if errno_from_exception(e) == errno.ECONNABORTED: continue raise callback(connection, address) diff --git a/tornado/process.py b/tornado/process.py index 942c5c3f6..74575d05a 100644 --- a/tornado/process.py +++ b/tornado/process.py @@ -35,6 +35,7 @@ from tornado.iostream import PipeIOStream from tornado.log import gen_log from tornado.platform.auto import set_close_exec from tornado import stack_context +from tornado.util import errno_from_exception try: long # py2 @@ -136,7 +137,7 @@ def fork_processes(num_processes, max_restarts=100): try: pid, status = os.wait() except OSError as e: - if e.errno == errno.EINTR: + if errno_from_exception(e) == errno.EINTR: continue raise if pid not in children: @@ -283,7 +284,7 @@ class Subprocess(object): try: ret_pid, status = os.waitpid(pid, os.WNOHANG) except OSError as e: - if e.args[0] == errno.ECHILD: + if errno_from_exception(e) == errno.ECHILD: return if ret_pid == 0: return diff --git a/tornado/tcpserver.py b/tornado/tcpserver.py index c07737329..81d983ff5 100644 --- a/tornado/tcpserver.py +++ b/tornado/tcpserver.py @@ -27,6 +27,7 @@ from tornado.ioloop import IOLoop from tornado.iostream import IOStream, SSLIOStream from tornado.netutil import bind_sockets, add_accept_handler, ssl_wrap_socket from tornado import process +from tornado.util import errno_from_exception class TCPServer(object): @@ -231,7 +232,7 @@ class TCPServer(object): # SSLIOStream._do_ssl_handshake). # To test this behavior, try nmap with the -sT flag. # https://github.com/facebook/tornado/pull/750 - if err.args[0] in (errno.ECONNABORTED, errno.EINVAL): + if errno_from_exception(err) in (errno.ECONNABORTED, errno.EINVAL): return connection.close() else: raise diff --git a/tornado/util.py b/tornado/util.py index cc5322229..9c3417595 100644 --- a/tornado/util.py +++ b/tornado/util.py @@ -132,6 +132,24 @@ def exec_in(code, glob, loc=None): """) +def errno_from_exception(e): + """Provides the errno from an Exception object. + + There are cases that the errno attribute was not set so we pull + the errno out of the args but if someone instatiates an Exception + without any args you will get a tuple error. So this function + abstracts all that behavior to give you a safe way to get the + errno. + """ + + if hasattr(e, 'errno'): + return e.errno + elif e.args: + return e.args[0] + else: + return None + + class Configurable(object): """Base class for configurable interfaces.