commit e68adb7fdd465e1780bc4ee1243e0c226add77d9 Author: Ravi Chandra Padmala neenaoffline@gmail.com Date: Wed Jun 27 18:34:15 2012 +0530
Implement convenience method for handling SAVECONF requests
This also changes the Exception structure a bit. I have added an InsatisfiableRequest class which will be raised when a valid request couldn't be satisfied by Tor.
I've also added a super class for non-socket non-protocol errors called OperationFailed. Any error that was raised by an error response returned by Tor (i.e., one with an error code & a message) should subclass this.
The exceptions now look like
ControllerError - Base exception raised when using the controller. |- ProtocolError - Malformed socket data. |- OperationFailed - Tor was unable to successfully complete the operation. | |- UnsatisfiableRequest - Tor was unable to satisfy a valid request. | +- InvalidRequest - Invalid request. | +- InvalidArguments - Invalid request parameters. +- SocketError - Communication with the socket failed. +- SocketClosed - Socket has been shut down. --- stem/control.py | 20 ++++++++++++++++++++ stem/socket.py | 28 ++++++++++++++++++++-------- test/integ/control/controller.py | 20 ++++++++++++++++++++ 3 files changed, 60 insertions(+), 8 deletions(-)
diff --git a/stem/control.py b/stem/control.py index 51804cc..e2bf282 100644 --- a/stem/control.py +++ b/stem/control.py @@ -20,6 +20,7 @@ interacting at a higher level. |- reset_conf - reverts configuration options to their default values |- set_options - sets or resets the values of multiple configuration options |- load_conf - loads configuration information as if it was in the torrc + |- save_conf - saves configuration information to the torrc |- get_version - convenience method to get tor version |- authenticate - convenience method to authenticate the controller +- protocolinfo - convenience method to get the protocol info @@ -777,6 +778,25 @@ class Controller(BaseController): raise stem.socket.InvalidRequest(response.code, response.message) elif not response.is_ok(): raise stem.socket.ProtocolError("+LOADCONF Received unexpected response\n%s" % str(response)) + + def save_conf(self): + """ + Saves the current configuration options into the active torrc file. + + :raises: + :class:`stem.socket.ControllerError` if the call fails + :class:`stem.socket.OperationFailed` if the client is unable to save the configuration file + """ + + response = self.msg("SAVECONF") + stem.response.convert("SINGLELINE", response) + + if response.is_ok(): + return True + elif response.code == "551": + raise stem.socket.OperationFailed(response.code, response.message) + else: + raise stem.socket.ProtocolError("SAVECONF returned unexpected response code")
def _case_insensitive_lookup(entries, key, default = UNDEFINED): """ diff --git a/stem/socket.py b/stem/socket.py index 7d40056..5492e30 100644 --- a/stem/socket.py +++ b/stem/socket.py @@ -28,8 +28,10 @@ as instances of the :class:`stem.response.ControlMessage` class.
ControllerError - Base exception raised when using the controller. |- ProtocolError - Malformed socket data. - |- InvalidRequest - Invalid request. - | +- InvalidArguments - Invalid request parameters. + |- OperationFailed - Tor was unable to successfully complete the operation. + | |- UnsatisfiableRequest - Tor was unable to satisfy a valid request. + | +- InvalidRequest - Invalid request. + | +- InvalidArguments - Invalid request parameters. +- SocketError - Communication with the socket failed. +- SocketClosed - Socket has been shut down. """ @@ -550,9 +552,9 @@ class ControllerError(Exception): class ProtocolError(ControllerError): "Malformed content from the control socket."
-class InvalidRequest(ControllerError): +class OperationFailed(ControllerError): """ - Base Exception class for invalid requests + Base exception class for failed operations that return an error code
:var str code: error code returned by Tor :var str message: error message returned by Tor or a human readable error message @@ -560,21 +562,31 @@ class InvalidRequest(ControllerError):
def __init__(self, code = None, message = None): """ - Initializes an InvalidRequest object. + Initializes an OperationFailed object.
:param str code: error code returned by Tor :param str message: error message returned by Tor or a human readable error message
- :returns: object of InvalidRequest class + :returns: object of OperationFailed class """
- super(InvalidRequest, self).__init__() + super(ControllerError, self).__init__() self.code = code self.message = message
+class UnsatisfiableRequest(OperationFailed): + """ + Exception raised if Tor was unable to process our request. + """ + +class InvalidRequest(OperationFailed): + """ + Exception raised when the request was invalid or malformed. + """ + class InvalidArguments(InvalidRequest): """ - Exception class for invalid requests which contain invalid arguments. + Exception class for requests which had invalid arguments.
:var str code: error code returned by Tor :var str message: error message returned by Tor or a human readable error message diff --git a/test/integ/control/controller.py b/test/integ/control/controller.py index 857799d..54f2626 100644 --- a/test/integ/control/controller.py +++ b/test/integ/control/controller.py @@ -296,4 +296,24 @@ class TestController(unittest.TestCase): finally: # reload original valid config controller.load_conf(oldconf) + + def test_saveconf(self): + + if test.runner.require_control(self): return + + runner = test.runner.get_runner() + + # only testing for success, since we need to run out of disk space to test + # for failure + with runner.get_tor_controller() as controller: + oldconf = runner.get_torrc_contents() + + try: + controller.set_conf("ContactInfo", "confsaved") + controller.save_conf() + with file(runner.get_torrc_path()) as torrcfile: + self.assertTrue("\nContactInfo confsaved\n" in torrcfile.read()) + finally: + controller.load_conf(oldconf) + controller.save_conf()
tor-commits@lists.torproject.org