[tor-commits] [stem/master] Controller method to check if a config option is set

atagar at torproject.org atagar at torproject.org
Sun Jan 3 23:46:12 UTC 2016


commit 09a468b3077bfdad3f1cab3797cb41515c2cc228
Author: Damian Johnson <atagar at torproject.org>
Date:   Sun Jan 3 15:46:56 2016 -0800

    Controller method to check if a config option is set
    
    Turns out tor's controller interface doesn't have a nice way of doing this.
    Thought we could with GETCONF but no luck.
    
    This reuses the work I reverted from commit e1124d2 for get_custom_conf(), but
    adds caching and only exposes a simple is_set() method.
---
 docs/change_log.rst              |    1 +
 stem/control.py                  |   54 ++++++++++++++++++++++++++++++++++++++
 test/integ/control/controller.py |   29 ++++++++++++++++++++
 3 files changed, 84 insertions(+)

diff --git a/docs/change_log.rst b/docs/change_log.rst
index 80938da..2caf167 100644
--- a/docs/change_log.rst
+++ b/docs/change_log.rst
@@ -48,6 +48,7 @@ The following are only available within Stem's `git repository
   * Added `stem.manual <api/manual.html>`_, which provides information available about Tor from `its manual <https://www.torproject.org/docs/tor-manual.html.en>`_ (:trac:`8251`)
   * :func:`~stem.connection.connect` and :func:`~stem.control.Controller.from_port` now connect to both port 9051 (relay's default) and 9151 (Tor Browser's default) (:trac:`16075`)
   * Added `support for NETWORK_LIVENESS events <api/response.html#stem.response.events.NetworkLivenessEvent>`_ (:spec:`44aac63`)
+  * Added :func:`~stem.control.Controller.is_set` to the :class:`~stem.control.Controller`
   * Added :func:`~stem.control.Controller.is_user_traffic_allowed` to the :class:`~stem.control.Controller`
   * Added the replica attribute to the :class:`~stem.response.events.HSDescEvent` (:spec:`4989e73`)
   * :func:`~stem.process.launch_tor` could leave a lingering process during an unexpected exception (:trac:`17946`)
diff --git a/stem/control.py b/stem/control.py
index 3ba3ed6..96597e8 100644
--- a/stem/control.py
+++ b/stem/control.py
@@ -93,6 +93,7 @@ If you're fine with allowing your script to raise exceptions then this can be mo
     |
     |- get_conf - gets the value of a configuration option
     |- get_conf_map - gets the values of multiple configuration options
+    |- is_set - determines if an option differs from its default
     |- set_conf - sets the value of a configuration option
     |- reset_conf - reverts configuration options to their default values
     |- set_options - sets or resets the values of multiple configuration options
@@ -1986,6 +1987,8 @@ class Controller(BaseController):
 
   def get_conf(self, param, default = UNDEFINED, multiple = False):
     """
+    get_conf(param, default = UNDEFINED, multiple = False)
+
     Queries the current value for a configuration option. Some configuration
     options (like the ExitPolicy) can have multiple values. This provides a
     **list** with all of the values if **multiple** is **True**. Otherwise this
@@ -2033,6 +2036,8 @@ class Controller(BaseController):
 
   def get_conf_map(self, params, default = UNDEFINED, multiple = True):
     """
+    get_conf_map(params, default = UNDEFINED, multiple = True)
+
     Similar to :func:`~stem.control.Controller.get_conf` but queries multiple
     configuration options, providing back a mapping of those options to their
     values.
@@ -2170,6 +2175,54 @@ class Controller(BaseController):
 
     return return_dict
 
+  @with_default()
+  def is_set(self, param, default = UNDEFINED):
+    """
+    is_set(param, default = UNDEFINED)
+
+    Checks if a configuration option differs from its default or not.
+
+    .. versionadded:: 1.5.0
+
+    :param str param: configuration option to check
+    :param object default: response if the query fails
+
+    :returns: **True** if option differs from its default and **False**
+      otherwise
+
+    :raises: :class:`stem.ControllerError` if the call fails and we weren't
+      provided a default response
+    """
+
+    return param in self._get_custom_options()
+
+  def _get_custom_options(self):
+    result = self._get_cache('get_custom_options')
+
+    if not result:
+      config_lines = self.get_info('config-text').splitlines()
+
+      # Tor provides some config options even if they haven't been set...
+      #
+      # https://trac.torproject.org/projects/tor/ticket/2362
+      # https://trac.torproject.org/projects/tor/ticket/17909
+
+      default_lines = (
+        'Log notice stdout',
+        'Log notice file /var/log/tor/log',
+        'DataDirectory /home/%s/.tor' % self.get_user('undefined'),
+        'HiddenServiceStatistics 0',
+      )
+
+      for line in default_lines:
+        if line in config_lines:
+          config_lines.remove(line)
+
+      result = dict([line.split(' ', 1) for line in config_lines])
+      self._set_cache({'get_custom_options': result})
+
+    return result
+
   def set_conf(self, param, value):
     """
     Changes the value of a tor configuration option. Our value can be any of
@@ -2279,6 +2332,7 @@ class Controller(BaseController):
             self._set_cache({'exitpolicy': None})
 
         self._set_cache(to_cache, 'getconf')
+        self._set_cache({'get_custom_options': None})
     else:
       log.debug('%s (failed, code: %s, message: %s)' % (query, response.code, response.message))
 
diff --git a/test/integ/control/controller.py b/test/integ/control/controller.py
index 84bf58a..7cf16ea 100644
--- a/test/integ/control/controller.py
+++ b/test/integ/control/controller.py
@@ -189,6 +189,8 @@ class TestController(unittest.TestCase):
 
         self.assertTrue(isinstance(event, stem.response.events.ConfChangedEvent))
 
+      controller.reset_conf('NodeFamily')
+
   @require_controller
   def test_reattaching_listeners(self):
     """
@@ -468,6 +470,33 @@ class TestController(unittest.TestCase):
       self.assertEqual({}, controller.get_conf_map([], 'la-di-dah'))
 
   @require_controller
+  def test_is_set(self):
+    """
+    Exercises our is_set() method.
+    """
+
+    runner = test.runner.get_runner()
+
+    with runner.get_tor_controller() as controller:
+      custom_options = controller._get_custom_options()
+      self.assertTrue('ControlPort' in custom_options or 'ControlSocket' in custom_options)
+      self.assertEqual('1', custom_options['DownloadExtraInfo'])
+      self.assertEqual('127.0.0.1:1112', custom_options['SocksListenAddress'])
+
+      self.assertTrue(controller.is_set('DownloadExtraInfo'))
+      self.assertTrue(controller.is_set('SocksListenAddress'))
+      self.assertFalse(controller.is_set('CellStatistics'))
+      self.assertFalse(controller.is_set('ConnLimit'))
+
+      # check we update when setting and resetting values
+
+      controller.set_conf('ConnLimit', '1005')
+      self.assertTrue(controller.is_set('ConnLimit'))
+
+      controller.reset_conf('ConnLimit')
+      self.assertFalse(controller.is_set('ConnLimit'))
+
+  @require_controller
   def test_hidden_services_conf(self):
     """
     Exercises the hidden service family of methods (get_hidden_service_conf,





More information about the tor-commits mailing list