commit efe28987ffd27e69a2c6c566bdad2efc5f16e93e Author: Damian Johnson atagar@torproject.org Date: Sat Nov 12 15:55:09 2011 -0800
Expanding contorller exception types
Exception types pretty much mirror what TorCtl has (protocol error, socket error, and controller closed) with a base type users can catch instead. One difference though is that if stem functions raise a socket.error (without documenting that they do) then that's a bug - those errors should cuase a stem.types.SocketError instead. --- stem/types.py | 34 +++++++++++++++++++++------------- test/integ/message.py | 14 +++++++------- test/integ/system.py | 3 ++- test/unit/types/control_message.py | 2 +- 4 files changed, 31 insertions(+), 22 deletions(-)
diff --git a/stem/types.py b/stem/types.py index e03698c..78e2fd9 100644 --- a/stem/types.py +++ b/stem/types.py @@ -2,8 +2,10 @@ Class representations for a variety of tor objects. These are most commonly return values rather than being instantiated by users directly.
-ProtocolError - Malformed socket data. -ControlSocketClosed - Socket terminated. +ControllerError - Base exception raised when using the controller. + |- ProtocolError - Malformed socket data. + |- SocketError - Socket used for controller communication errored. + +- SocketClosed - Socket terminated.
read_message - Reads a ControlMessage from a control socket. ControlMessage - Message from the control socket. @@ -45,11 +47,18 @@ KEY_ARG = re.compile("^(\S+)=") CONTROL_ESCAPES = {r"\": "\", r""": """, r"'": "'", r"\r": "\r", r"\n": "\n", r"\t": "\t"}
-class ProtocolError(Exception): +class ControllerError(Exception): + "Base error for controller communication issues." + +class ProtocolError(ControllerError): "Malformed content from the control socket." pass
-class ControlSocketClosed(Exception): +class SocketError(ControllerError): + "Error arose while communicating with the control socket." + pass + +class SocketClosed(ControllerError): "Control socket was closed before completing the message." pass
@@ -67,8 +76,7 @@ def read_message(control_file):
Raises: ProtocolError the content from the socket is malformed - ControlSocketClosed if the socket closes before we receive a complete - message + SocketClosed if the socket closes before we receive a complete message """
parsed_content, raw_content = [], "" @@ -79,11 +87,11 @@ def read_message(control_file): # if the control_file has been closed then we will receive: # AttributeError: 'NoneType' object has no attribute 'recv'
- LOGGER.warn("ControlSocketClosed: socket file has been closed") - raise ControlSocketClosed("socket file has been closed") + LOGGER.warn("SocketClosed: socket file has been closed") + raise SocketClosed("socket file has been closed") except socket.error, exc: - LOGGER.warn("ControlSocketClosed: received an exception (%s)" % exc) - raise ControlSocketClosed(exc) + LOGGER.warn("SocketClosed: received an exception (%s)" % exc) + raise SocketClosed(exc)
raw_content += line
@@ -94,8 +102,8 @@ def read_message(control_file): # if the socket is disconnected then the readline() method will provide # empty content
- LOGGER.warn("ControlSocketClosed: empty socket content") - raise ControlSocketClosed("Received empty socket content.") + LOGGER.warn("SocketClosed: empty socket content") + raise SocketClosed("Received empty socket content.") elif len(line) < 4: LOGGER.warn("ProtocolError: line too short (%s)" % line) raise ProtocolError("Badly formatted reply line: too short") @@ -125,7 +133,7 @@ def read_message(control_file):
while True: try: line = control_file.readline() - except socket.error, exc: raise ControlSocketClosed(exc) + except socket.error, exc: raise SocketClosed(exc)
raw_content += line
diff --git a/test/integ/message.py b/test/integ/message.py index 531d4a2..a562f86 100644 --- a/test/integ/message.py +++ b/test/integ/message.py @@ -40,16 +40,16 @@ class TestMessageFunctions(unittest.TestCase): control_socket_file.write("GETINFO version\r\n") control_socket_file.flush()
- self.assertRaises(stem.types.ControlSocketClosed, stem.types.read_message, control_socket_file) + self.assertRaises(stem.types.SocketClosed, stem.types.read_message, control_socket_file)
# Additional socket usage should fail, and pulling more responses will fail # with more closed exceptions.
control_socket_file.write("GETINFO version\r\n") self.assertRaises(socket.error, control_socket_file.flush) - self.assertRaises(stem.types.ControlSocketClosed, stem.types.read_message, control_socket_file) - self.assertRaises(stem.types.ControlSocketClosed, stem.types.read_message, control_socket_file) - self.assertRaises(stem.types.ControlSocketClosed, stem.types.read_message, control_socket_file) + self.assertRaises(stem.types.SocketClosed, stem.types.read_message, control_socket_file) + self.assertRaises(stem.types.SocketClosed, stem.types.read_message, control_socket_file) + self.assertRaises(stem.types.SocketClosed, stem.types.read_message, control_socket_file)
# The socket connection is already broken so calling close shouldn't have # an impact. @@ -57,7 +57,7 @@ class TestMessageFunctions(unittest.TestCase): control_socket.close() control_socket_file.write("GETINFO version\r\n") self.assertRaises(socket.error, control_socket_file.flush) - self.assertRaises(stem.types.ControlSocketClosed, stem.types.read_message, control_socket_file) + self.assertRaises(stem.types.SocketClosed, stem.types.read_message, control_socket_file)
# Closing the file handler, however, will cause a different type of error. # This seems to depend on the python version, in 2.6 we get an @@ -72,8 +72,8 @@ class TestMessageFunctions(unittest.TestCase): # receives: AttributeError: 'NoneType' object has no attribute 'sendall' self.assertRaises(AttributeError, control_socket_file.flush)
- # receives: stem.types.ControlSocketClosed: socket file has been closed - self.assertRaises(stem.types.ControlSocketClosed, stem.types.read_message, control_socket_file) + # receives: stem.types.SocketClosed: socket file has been closed + self.assertRaises(stem.types.SocketClosed, stem.types.read_message, control_socket_file)
def test_invalid_command(self): """ diff --git a/test/integ/system.py b/test/integ/system.py index 4aa5d15..09ffd24 100644 --- a/test/integ/system.py +++ b/test/integ/system.py @@ -49,7 +49,8 @@ class TestSystemFunctions(unittest.TestCase): # tor's pwd will match our process since we started it runner = test.runner.get_runner() self.assertEquals(os.getcwd(), system.get_cwd(runner.get_pid())) - self.assertRaises(IOError, system.get_cwd, 99999) + self.assertEquals(None, system.get_cwd(99999, True)) + self.assertRaises(IOError, system.get_cwd, 99999, False)
def test_get_bsd_jail_id(self): """ diff --git a/test/unit/types/control_message.py b/test/unit/types/control_message.py index 563a0bf..5e9b7bc 100644 --- a/test/unit/types/control_message.py +++ b/test/unit/types/control_message.py @@ -156,7 +156,7 @@ class TestControlMessage(unittest.TestCase):
control_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) control_socket_file = control_socket.makefile() - self.assertRaises(stem.types.ControlSocketClosed, stem.types.read_message, control_socket_file) + self.assertRaises(stem.types.SocketClosed, stem.types.read_message, control_socket_file)
def assert_message_parses(self, controller_reply): """
tor-commits@lists.torproject.org