]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
IOStreams now save the exception object that caused them to close.
authorBen Darnell <ben@bendarnell.com>
Sun, 27 May 2012 22:04:03 +0000 (15:04 -0700)
committerBen Darnell <ben@bendarnell.com>
Sun, 27 May 2012 22:04:03 +0000 (15:04 -0700)
Closes #494.

tornado/iostream.py
tornado/test/iostream_test.py

index 9e95a06672810145df8bed07a9b96e4b366a01b5..df1f27f674d50dee9fae2c3f56609992a29a64ef 100644 (file)
@@ -21,6 +21,7 @@ from __future__ import absolute_import, division, with_statement
 import collections
 import errno
 import logging
+import os
 import socket
 import sys
 import re
@@ -48,6 +49,9 @@ class IOStream(object):
     and may either be connected before passing it to the IOStream or
     connected with IOStream.connect.
 
+    When a stream is closed due to an error, the IOStream's `error`
+    attribute contains the exception object.
+
     A very simple (and broken) HTTP client using this class::
 
         from tornado import ioloop
@@ -84,6 +88,7 @@ class IOStream(object):
         self.io_loop = io_loop or ioloop.IOLoop.instance()
         self.max_buffer_size = max_buffer_size
         self.read_chunk_size = read_chunk_size
+        self.error = None
         self._read_buffer = collections.deque()
         self._write_buffer = collections.deque()
         self._read_buffer_size = 0
@@ -214,6 +219,8 @@ class IOStream(object):
     def close(self):
         """Close this stream."""
         if self.socket is not None:
+            if any(sys.exc_info()):
+                self.error = sys.exc_info()[1]
             if self._read_until_close:
                 callback = self._read_callback
                 self._read_callback = None
@@ -264,6 +271,9 @@ class IOStream(object):
             if not self.socket:
                 return
             if events & self.io_loop.ERROR:
+                errno = self.socket.getsockopt(socket.SOL_SOCKET,
+                                               socket.SO_ERROR)
+                self.error = socket.error(errno, os.strerror(errno))
                 # We may have queued up a user callback in _handle_read or
                 # _handle_write, so don't close the IOStream until those
                 # callbacks have had a chance to run.
index ac8c207aa1c09592f9e7d8c3bdfbca9230278086..6340eda9eed1ff4baa20ea5cb802bd742c055324 100644 (file)
@@ -5,6 +5,7 @@ from tornado.iostream import IOStream
 from tornado.testing import AsyncHTTPTestCase, LogTrapTestCase, get_unused_port
 from tornado.util import b
 from tornado.web import RequestHandler, Application
+import errno
 import socket
 import time
 
@@ -91,6 +92,16 @@ class TestIOStream(AsyncHTTPTestCase, LogTrapTestCase):
         stream.connect(("localhost", port), connect_callback)
         self.wait()
         self.assertFalse(self.connect_called)
+        self.assertTrue(isinstance(stream.error, socket.error), stream.error)
+        self.assertEqual(stream.error.args[0], errno.ECONNREFUSED)
+
+    def test_gaierror(self):
+        # Test that IOStream sets its exc_info on getaddrinfo error
+        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
+        stream = IOStream(s, io_loop=self.io_loop)
+        stream.set_close_callback(self.stop)
+        stream.connect(('adomainthatdoesntexist.asdf', 54321))
+        self.assertTrue(isinstance(stream.error, socket.gaierror), stream.error)
 
     def test_connection_closed(self):
         # When a server sends a response and then closes the connection,