commit 030579292593a9e8262edfc713e1d86b379dabf5 Author: Damian Johnson atagar@torproject.org Date: Wed Jan 25 08:48:48 2012 -0800
Dropping Config.sync() in favor of config_dict()
The Config class' sync method was a step in the right direction, but the name was confusing and the usage was suboptimal. In the vast majority of cases the caller simply wants a dictionary that stays in sync with the configuration. The config_dict() function is essentially the same as sync but with more succinct calls. --- run_tests.py | 6 ++-- stem/util/conf.py | 70 ++++++++++++++++++++++++++------------------- test/output.py | 6 +-- test/runner.py | 6 +-- test/unit/util/conf.py | 74 ++++++++++++++++++++++++------------------------ 5 files changed, 85 insertions(+), 77 deletions(-)
diff --git a/run_tests.py b/run_tests.py index 6972e16..d4d2fd6 100755 --- a/run_tests.py +++ b/run_tests.py @@ -29,6 +29,7 @@ import test.integ.util.conf import test.integ.util.system import test.integ.version
+import stem.util.conf import stem.util.enum import stem.util.log as log import stem.util.term as term @@ -37,7 +38,7 @@ OPT = "uic:l:t:h" OPT_EXPANDED = ["unit", "integ", "config=", "targets=", "log=", "tor=", "no-color", "help"] DIVIDER = "=" * 70
-CONFIG = { +CONFIG = stem.util.conf.config_dict("test", { "argument.unit": False, "argument.integ": False, "argument.log": None, @@ -48,7 +49,7 @@ CONFIG = { "target.description": {}, "target.prereq": {}, "target.torrc": {}, -} +})
Target = stem.util.enum.Enum(*[(v, v) for v in ( "ONLINE", @@ -183,7 +184,6 @@ if __name__ == '__main__':
# loads and validates our various configurations test_config = stem.util.conf.get_config("test") - test_config.sync(CONFIG)
settings_path = os.path.join(test.runner.STEM_BASE, "test", "settings.cfg") test_config.load(settings_path) diff --git a/stem/util/conf.py b/stem/util/conf.py index 9250053..fe8ab68 100644 --- a/stem/util/conf.py +++ b/stem/util/conf.py @@ -19,6 +19,26 @@ a '|' prefix. For instance... |exclaiming about the wonders |and awe that is pepperjack!
+The Config class acts as a central store for configuration values. Users of +this store have their own dictionaries of config key/value pairs that provide +three things... + + 1. Default values for the configuration keys in case they're either undefined + or of the wrong type. + 2. Types that we should attempt to cast the configuration values to. + 3. An easily accessable container for getting the config values. + +There are many ways of using the Config class but the most common ones are... + +- Call config_dict to get a dictionary that's always synced with with a Config. + +- Make a dictionary and call synchronize() to bring it into sync with the + Config. This does not keep it in sync as the Config changes. See the Config + class' pydocs for an example. + +- Just call the Config's get() or get_value() methods directly. + +config_dict - provides a dictionary that's kept synchronized with a config get_config - Singleton for getting configurations Config - Custom configuration. |- load - reads a configuration file @@ -27,7 +47,6 @@ Config - Custom configuration. |- synchronize - replaces mappings in a dictionary with the config's values |- add_listener - notifies the given listener when an update occures |- clear_listeners - removes any attached listeners - |- sync - keeps a dictionary synchronized with our config |- keys - provides keys in the loaded configuration |- set - sets the given key/value pair |- unused_keys - provides keys that have never been requested @@ -59,14 +78,27 @@ class SyncListener:
self.config_dict[key] = new_value
-# TODO: methods that will be needed if we want to allow for runtime -# customization... -# -# Config.set(key, value) - accepts any type that the get() method does, -# updating our contents with the string conversion -# -# Config.save(path) - writes our current configurations, ideally merging them -# with the file that exists there so commenting and such are preserved +def config_dict(handle, conf_mappings, handler = None): + """ + Makes a dictionary that stays synchronized with a configuration. The process + for staying in sync is similar to the Config class' synchronize() method, + only changing the dictionary's values if we're able to cast to the same type. + + If an handler is provided then this is called just prior to assigning new + values to the config_dict. The handler function is expected to accept the + (key, value) for the new values and return what we should actually insert + into the dictionary. If this returns None then the value is updated as + normal. + + Arguments: + handle (str) - unique identifier for a config instance + conf_mappings (dict) - config key/value mappings used as our defaults + handler (functor) - function referred to prior to assigning values + """ + + selected_config = get_config(handle) + selected_config.add_listener(SyncListener(conf_mappings, handler).update) + return conf_mappings
def get_config(handle): """ @@ -312,26 +344,6 @@ class Config():
self._listeners = []
- def sync(self, config_dict, interceptor = None): - """ - Synchronizes a dictionary with our current configuration (like the - 'synchronize' method), and registers it to be updated whenever our - configuration changes. - - If an interceptor is provided then this is called just prior to assigning - new values to the config_dict. The interceptor function is expected to - accept the (key, value) for the new values and return what we should - actually insert into the dictionary. If this returns None then the value is - updated as normal. - - Arguments: - config_dict (dict) - dictionary to keep synchronized with our - configuration - interceptor (functor) - function referred to prior to assigning values - """ - - self.add_listener(SyncListener(config_dict, interceptor).update) - def keys(self): """ Provides all keys in the currently loaded configuration. diff --git a/test/output.py b/test/output.py index 76156b9..89a6cc0 100644 --- a/test/output.py +++ b/test/output.py @@ -11,11 +11,9 @@ import stem.util.conf import stem.util.enum import stem.util.term as term
-CONFIG = { +CONFIG = stem.util.conf.config_dict("test", { "argument.no_color": False, -} - -stem.util.conf.get_config("test").sync(CONFIG) +})
DIVIDER = "=" * 70 HEADER_ATTR = (term.Color.CYAN, term.Attr.BOLD) diff --git a/test/runner.py b/test/runner.py index 405f96c..d3c6143 100644 --- a/test/runner.py +++ b/test/runner.py @@ -41,14 +41,12 @@ import stem.util.enum import stem.util.term as term import test.output
-CONFIG = { +CONFIG = stem.util.conf.config_dict("test", { "integ.test_directory": "./test/data", "integ.log": "./test/data/log", "test.target.online": False, "test.target.relative_data_dir": False, -} - -stem.util.conf.get_config("test").sync(CONFIG) +})
STATUS_ATTR = (term.Color.BLUE, term.Attr.BOLD) SUBSTATUS_ATTR = (term.Color.BLUE, ) diff --git a/test/unit/util/conf.py b/test/unit/util/conf.py index 7df0402..0a31381 100644 --- a/test/unit/util/conf.py +++ b/test/unit/util/conf.py @@ -16,6 +16,43 @@ class TestConf(unittest.TestCase): test_config.clear() test_config.clear_listeners()
+ def test_config_dict(self): + """ + Tests the config_dict function. + """ + + my_config = { + "bool_value": False, + "int_value": 5, + "str_value": "hello", + "list_value": [], + } + + test_config = stem.util.conf.get_config("unit_testing") + + # checks that sync causes existing contents to be applied + test_config.set("bool_value", "true") + my_config = stem.util.conf.config_dict("unit_testing", my_config) + self.assertEquals(True, my_config["bool_value"]) + + # check a basic synchronize + test_config.set("str_value", "me") + self.assertEquals("me", my_config["str_value"]) + + # synchronize with a type mismatch, should keep the old value + test_config.set("int_value", "7a") + self.assertEquals(5, my_config["int_value"]) + + # changes for a collection + test_config.set("list_value", "a", False) + self.assertEquals(["a"], my_config["list_value"]) + + test_config.set("list_value", "b", False) + self.assertEquals(["a", "b"], my_config["list_value"]) + + test_config.set("list_value", "c", False) + self.assertEquals(["a", "b", "c"], my_config["list_value"]) + def test_clear(self): """ Tests the clear method. @@ -113,43 +150,6 @@ class TestConf(unittest.TestCase): test_config.set("foo", "bar") self.assertEquals(["hello"], listener_received_keys)
- def test_sync(self): - """ - Tests the sync method. - """ - - my_config = { - "bool_value": False, - "int_value": 5, - "str_value": "hello", - "list_value": [], - } - - test_config = stem.util.conf.get_config("unit_testing") - - # checks that sync causes existing contents to be applied - test_config.set("bool_value", "true") - test_config.sync(my_config) - self.assertEquals(True, my_config["bool_value"]) - - # check a basic synchronize - test_config.set("str_value", "me") - self.assertEquals("me", my_config["str_value"]) - - # synchronize with a type mismatch, should keep the old value - test_config.set("int_value", "7a") - self.assertEquals(5, my_config["int_value"]) - - # changes for a collection - test_config.set("list_value", "a", False) - self.assertEquals(["a"], my_config["list_value"]) - - test_config.set("list_value", "b", False) - self.assertEquals(["a", "b"], my_config["list_value"]) - - test_config.set("list_value", "c", False) - self.assertEquals(["a", "b", "c"], my_config["list_value"]) - def test_unused_keys(self): """ Tests the unused_keys method.
tor-commits@lists.torproject.org