commit aeb39b813961d02509a9c3e4e3db585b4190cac1 Author: Ravi Chandra Padmala neenaoffline@gmail.com Date: Thu Aug 23 21:52:01 2012 -0700
Implement wrapper method for Controller to handle SIGNAL requests --- stem/control.py | 31 +++++++++++++++++++++++++++++++ test/integ/control/controller.py | 16 ++++++++++++++++ 2 files changed, 47 insertions(+), 0 deletions(-)
diff --git a/stem/control.py b/stem/control.py index 7edd7bf..adf1d8a 100644 --- a/stem/control.py +++ b/stem/control.py @@ -26,6 +26,7 @@ interacting at a higher level. |- save_conf - saves configuration information to the torrc |- is_feature_enabled - checks if a given controller feature is enabled |- enable_feature - enables a controller feature that has been disabled by default + |- signal - sends a signal to the tor client |- get_version - convenience method to get tor version |- authenticate - convenience method to authenticate the controller +- protocolinfo - convenience method to get the protocol info @@ -1026,6 +1027,35 @@ class Controller(BaseController): raise stem.socket.ProtocolError("USEFEATURE provided an invalid response code: %s" % response.code)
self.enabled_features += [entry.upper() for entry in features] + + def signal(self, signal): + """ + Sends a signal to the Tor client. + + :param str signal: type of signal to be sent. Must be one of the following... + * HUP - Reload configuration + * INT - If server is an OP, exit immediately. If it's an OR, close listeners and exit after ShutdownWaitLength seconds + * USR1 - Dump log information about open connections and circuits + * USR2 - Switch all open logs to loglevel debug + * TERM - Clean up and exit immediately + * RELOAD - equivalent to HUP + * SHUTDOWN - equivalent to INT + * DUMP - . equivalent to USR1 + * DEBUG - . equivalent to USR2 + * HALT - . equivalent to TERM + * NEWNYM - Switch to clean circuits, so new application requests don't share any circuits with old ones and clear the client-side dns cache + * CLEARDNSCACHE - Forget the client-side cached IPs for all hostnames + + :raises: :class:`stem.socket.InvalidArguments` if signal provided wasn't recognized. + """ + + response = self.msg("SIGNAL %s" % signal) + stem.response.convert("SINGLELINE", response) + + if not response.is_ok(): + if response.code == "552": + raise stem.socket.InvalidArguments(response.code, response.message, [signal]) + raise stem.socket.ProtocolError("SIGNAL response contained unrecognized status code")
def _case_insensitive_lookup(entries, key, default = UNDEFINED): """ @@ -1053,3 +1083,4 @@ def _case_insensitive_lookup(entries, key, default = UNDEFINED): if default != UNDEFINED: return default else: raise ValueError("key '%s' doesn't exist in dict: %s" % (key, entries))
+ diff --git a/test/integ/control/controller.py b/test/integ/control/controller.py index 2723e27..450d219 100644 --- a/test/integ/control/controller.py +++ b/test/integ/control/controller.py @@ -348,4 +348,20 @@ class TestController(unittest.TestCase): except stem.socket.InvalidArguments, exc: self.assertEqual(["NOT"], exc.arguments) else: self.fail() + + def test_signal(self): + """ + Test controller.signal with valid and invalid signals. + """ + runner = test.runner.get_runner() + + with runner.get_tor_controller() as controller: + # valid signal + controller.signal("CLEARDNSCACHE") + + # invalid signals + self.assertRaises(stem.socket.InvalidArguments, controller.signal, "FOOBAR") + + controller.signal("INT") + self.assertRaises(stem.socket.SocketClosed, controller.msg, "GETINFO version")