changeset 102:c530f6265deb

allow extensible configuration; also start using deepcopy heavily since otherwise you have artefacts
author Jeff Hammel <jhammel@mozilla.com>
date Tue, 01 May 2012 10:33:37 -0700
parents f4590492cb4c
children 982a775bf3d2
files configuration/configuration.py tests/base.json tests/example.py tests/unit.py
diffstat 4 files changed, 63 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/configuration/configuration.py	Mon Apr 30 13:21:48 2012 -0700
+++ b/configuration/configuration.py	Tue May 01 10:33:37 2012 -0700
@@ -225,6 +225,7 @@
 
     options = {}         # configuration basis definition
     load_option = 'load' # where to put the load option
+    extend = set()       # if dicts/lists should be extended
 
     def __init__(self, configuration_providers=configuration_providers, types=types, load=None, dump='--dump', **parser_args):
 
@@ -326,17 +327,24 @@
 
     ### methods for adding configuration
 
+    def default_config(self):
+        """configuration defaults"""
+        defaults = {}
+        for key, value in self.options.items():
+            if 'default' in value:
+                defaults[key] = value['default']
+        return copy.deepcopy(defaults)
+
     def __call__(self, *args):
         """add items to configuration and check it"""
 
+        # start with defaults
+        self.config = self.default_config()
+
+        # add the configuration
         for config in args:
             self.add(config)
 
-        # add defaults if not present
-        for key, value in self.items():
-            if 'default' in value and key not in self.config:
-                self.config[key] = value['default']
-
         # validate total configuration
         self.validate()
         # TODO: configuration should be locked after this is called
@@ -344,9 +352,24 @@
     def add(self, config, check=True):
         """update configuration: not undoable"""
 
-        self.check(config) # check config to be added
-        self.config.update(config)
-        # TODO: option to extend; augment lists/dicts
+        # check config to be added
+        self.check(config)
+
+        # add the configuration
+        for key, value in config.items():
+            value = copy.deepcopy(value)
+            if key in self.extend and key in self.config:
+                type1 = type(self.config[key])
+                type2 = type(value)
+                assert type1 == type2 # XXX hack
+                if type1 == dict:
+                    self.config[key].update(value)
+                elif type1 == list:
+                    self.config[key].extend(value)
+                else:
+                    raise NotImplementedError
+            else:
+                self.config[key] = value
 
     ### methods for optparse
     ### XXX could go in a subclass
--- a/tests/base.json	Mon Apr 30 13:21:48 2012 -0700
+++ b/tests/base.json	Tue May 01 10:33:37 2012 -0700
@@ -3,5 +3,7 @@
     "ts"
   ], 
   "browser_path": "/home/jhammel/firefox/firefox", 
-  "test_timeout": 60
+  "test_timeout": 60,
+  "preferences": {"browser.bookmarks.max_backups": 0,
+                  "browser.cache.disk.smart_size.enabled": false}
 }
\ No newline at end of file
--- a/tests/example.py	Mon Apr 30 13:21:48 2012 -0700
+++ b/tests/example.py	Tue May 01 10:33:37 2012 -0700
@@ -17,9 +17,12 @@
                     'type': bool},
         'test_timeout': {'help': "Time to wait for the browser to output to the log file",
                          'default': 1200},
-        'preferences': {'type': dict,
+        'preferences': {'help': 'profile preferences',
+                        'default': {'browser.bookmarks.max_backups': 0,
+                                    'browser.cache.disk.smart_size.enabled': False},
                         'flags': ['-p', '--pref']}
         }
+    extend = set(['preferences'])
 
 if __name__ == '__main__':
     from pprint import pprint
--- a/tests/unit.py	Mon Apr 30 13:21:48 2012 -0700
+++ b/tests/unit.py	Tue May 01 10:33:37 2012 -0700
@@ -59,6 +59,10 @@
                   'activeTests':  ['ts']}
         example(config)
         config['test_timeout'] = 1200 # default
+        config['preferences'] = {"browser.bookmarks.max_backups": 0,
+                                 "browser.cache.disk.smart_size.enabled": False}
+
+        # ensure they are equal
         self.assertEqual(config, example.config)
         example.serialize(filename)
         self.assertTrue(os.path.exists(filename))
@@ -101,6 +105,7 @@
             missingvalueexception = e
         self.assertTrue(isinstance(e, configuration.MissingValueException))
 
+
     def test_multiple_configurations(self):
         """test having multiple configurations"""
 
@@ -141,5 +146,25 @@
         config['test_timeout'] = 1200
         self.assertEqual(example.config, config)
 
+    def test_extend(self):
+
+        # default preferences
+        example = ExampleConfiguration()
+        default_prefs = {"browser.bookmarks.max_backups": 0,
+                         "browser.cache.disk.smart_size.enabled": False}
+        example.parse_args(['-a', 'ts', '-e', '/opt/bin/firefox'])
+        self.assertEqual(example.config['preferences'], default_prefs)
+
+        # now extend them
+        example = ExampleConfiguration()
+        default_prefs['network.dns.ipv4OnlyDomains'] = 'localhost'
+        tf = tempfile.mktemp()
+        f = file(tf, 'w')
+        f.write(json.dumps({'preferences': {'network.dns.ipv4OnlyDomains': 'localhost'}}))
+        f.close()
+        example.parse_args(['-a', 'ts', '-e', '/opt/bin/firefox', tf])
+        self.assertEqual(example.config['preferences'], default_prefs)
+        os.remove(tf)
+
 if __name__ == '__main__':
     unittest.main()