[tor-commits] [stem/master] Implement convenience method for handling SAVECONF requests

atagar at torproject.org atagar at torproject.org
Tue Jul 10 16:32:16 UTC 2012


commit e68adb7fdd465e1780bc4ee1243e0c226add77d9
Author: Ravi Chandra Padmala <neenaoffline at 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()
 



More information about the tor-commits mailing list