Add test_config
Content-type: text/plain

Author: andar

Revision: 5606

Log:
        Change the config format 
Add test_config

Diff:
Modified: trunk/deluge/config.py
===================================================================
--- trunk/deluge/config.py      2009-08-01 00:15:05 UTC (rev 5605)
+++ trunk/deluge/config.py      2009-08-01 02:26:26 UTC (rev 5606)
@@ -40,15 +40,15 @@
 This module is used for loading and saving of configuration files.. or anything
 really.
 
-The format of the config file is as follows:
+The format of the config file is two json encoded dicts:
 
-<format version as int>
-<config file version as int>
-<content>
+<version dict>
+<content dict>
 
-The format version is controlled by the Config class.  It should only be 
changed
-when anything below it is changed directly by the Config class.  An example of
-this would be if we changed the serializer for the content to something 
different.
+The version dict contains two keys: file and format.  The format version is 
+controlled by the Config class.  It should only be changed when anything below 
+it is changed directly by the Config class.  An example of this would be if we 
+changed the serializer for the content to something different.
 
 The config file version is changed by the 'owner' of the config file.  This is
 to signify that there is a change in the naming of some config keys or 
something
@@ -90,6 +90,37 @@
     """
     return property(doc=func.__doc__, **func())
 
+def find_json_objects(s):
+    """
+    Find json objects in a string.
+    
+    :param s: the string to find json objects in
+    :type s: string
+    
+    :returns: a list of tuples containing start and end locations of json 
objects in the string `s`
+    :rtype: [(start, end), ...]
+    
+    """
+    objects = []
+    opens = 0
+    start = s.find("{")
+    offset = start
+
+    if start < 0:
+        return []
+
+    for index, c in enumerate(s[offset:]):
+        if c == "{":
+            opens += 1
+        elif c == "}":
+            opens -= 1
+        if opens == 0:
+            objects.append((start, index+offset+1))
+            start = index + offset + 1
+
+    return objects
+    
+    
 class Config(object):
     """
     This class is used to access/create/modify config files
@@ -105,12 +136,14 @@
         self.__change_callbacks = []
 
         # These hold the version numbers and they will be set when loaded
-        self.__format_version = 1
-        self.__file_version = 1
+        self.__version = {
+            "format": 1,
+            "file": 1
+        }
 
         # This will get set with a reactor.callLater whenever a config option
         # is set.
-        self.__save_timer = None
+        self._save_timer = None
 
         if defaults:
             self.__config = defaults
@@ -134,12 +167,14 @@
     def set_item(self, key, value):
         """
         Sets item 'key' to 'value' in the config dictionary, but does not allow
-        changing the item's type unless it is None
+        changing the item's type unless it is None.  If the types do not match,
+        it will attempt to convert it to the set type before raising a 
ValueError.
 
         :param key: string, item to change to change
         :param value: the value to change item to, must be same type as what 
is currently in the config
 
-        :raises ValueError: raised when the type of value is not the same as 
what is currently in the config
+        :raises ValueError: raised when the type of value is not the same as\
+what is currently in the config and it could not convert the value
 
         **Usage**
 
@@ -186,8 +221,8 @@
             pass
 
         # We set the save_timer for 5 seconds if not already set
-        if not self.__save_timer or not self.__save_timer.active():
-            self.__save_timer = reactor.callLater(5, self.save)
+        if not self._save_timer or not self._save_timer.active():
+            self._save_timer = reactor.callLater(5, self.save)
 
     def __getitem__(self, key):
         """
@@ -304,38 +339,39 @@
             filename = self.__config_file
 
         try:
-            data = open(filename, "rb")
+            data = open(filename, "rb").read()
         except IOError, e:
             log.warning("Unable to open config file %s: %s", filename, e)
             return
 
-        try:
-            self.__format_version = int(data.readline())
-        except ValueError:
-            data.seek(0)
-        else:
+        objects = find_json_objects(data)
+        
+        if not len(objects):
+            # No json objects found, try depickling it
             try:
-                self.__file_version = int(data.readline())
-            except ValueError:
-                pass
-
-        fdata = data.read()
-        data.close()
-
-        try:
-            self.__config.update(json.loads(fdata))
-        except Exception, e:
-            log.exception(e)
+                self.__config.update(pickle.loads(data))
+            except Exception, e:
+                log.exception(e)
+                log.warning("Unable to load config file: %s", filename)        
        
+        elif len(objects) == 1:
+            start, end = objects[0]
             try:
-                self.__config.update(pickle.loads(fdata))
+                self.__config.update(json.loads(data[start:end]))
             except Exception, e:
                 log.exception(e)
-                log.warning("Unable to load config file: %s", filename)
-
-
-
+                log.warning("Unable to load config file: %s", filename)        
        
+        elif len(objects) == 2:
+            try:
+                start, end = objects[0]
+                self.__version.update(json.loads(data[start:end]))
+                start, end = objects[1]
+                self.__config.update(json.loads(data[start:end]))
+            except Exception, e:
+                log.exception(e)
+                log.warning("Unable to load config file: %s", filename)        
        
+            
         log.debug("Config %s version: %s.%s loaded: %s", filename,
-            self.__format_version, self.__file_version, self.__config)
+            self.__version["format"], self.__version["file"], self.__config)
 
     def save(self, filename=None):
         """
@@ -351,26 +387,27 @@
         # Check to see if the current config differs from the one on disk
         # We will only write a new config file if there is a difference
         try:
-            data = open(filename, "rb")
-            data.readline()
-            data.readline()
-            loaded_data = json.loads(data.read())
-            data.close()
-            if self.__config == loaded_data:
+            data = open(filename, "rb").read()
+            objects = find_json_objects(data)
+            start, end = objects[0]
+            version = json.loads(data[start:end])
+            start, end = objects[1]
+            loaded_data = json.loads(data[start:end])
+
+            if self.__config == loaded_data and self.__version == version:
                 # The config has not changed so lets just return
-                self.__save_timer = None
+                self._save_timer = None
                 return
         except Exception, e:
             log.warning("Unable to open config file: %s", filename)
 
-        self.__save_timer = None
+        self._save_timer = None
 
         # Save the new config and make sure it's written to disk
         try:
             log.debug("Saving new config file %s", filename + ".new")
             f = open(filename + ".new", "wb")
-            f.write(str(self.__format_version) + "\n")
-            f.write(str(self.__file_version) + "\n")
+            json.dump(self.__version, f, indent=2)            
             json.dump(self.__config, f, indent=2)
             f.flush()
             os.fsync(f.fileno())
@@ -414,9 +451,9 @@
         if output_version in input_range or output_version <= max(input_range):
             raise ValueError("output_version needs to be greater than 
input_range")
 
-        if self.__file_version not in input_range:
+        if self.__version["file"] not in input_range:
             log.debug("File version %s is not in input_range %s, ignoring 
converter function..",
-                self.__file_version, input_range)
+                self.__version["file"], input_range)
             return
 
         try:
@@ -424,10 +461,10 @@
         except Exception, e:
             log.exception(e)
             log.error("There was an exception try to convert config file %s %s 
to %s",
-                self.__config_file, self.__file_version, output_version)
+                self.__config_file, self.__version["file"], output_version)
             raise e
         else:
-            self.__file_version = output_version
+            self.__version["file"] = output_version
             self.save()
 
     @property

Modified: trunk/tests/common.py
===================================================================
--- trunk/tests/common.py       2009-08-01 00:15:05 UTC (rev 5605)
+++ trunk/tests/common.py       2009-08-01 02:26:26 UTC (rev 5606)
@@ -8,6 +8,7 @@
 def set_tmp_config_dir():
     config_directory = tempfile.mkdtemp()
     deluge.configmanager.set_config_dir(config_directory)
+    return config_directory
 
 import gettext
 import locale

Added: trunk/tests/test_config.py
===================================================================
--- trunk/tests/test_config.py                          (rev 0)
+++ trunk/tests/test_config.py  2009-08-01 02:26:26 UTC (rev 5606)
@@ -0,0 +1,66 @@
+from twisted.trial import unittest
+from twisted.python.failure import Failure
+
+import common
+
+from deluge.config import Config
+
+class ConfigTestCase(unittest.TestCase):
+    def test_init(self):
+        defaults = {"string": "foobar", "int": 1, "float": 0.435, "bool": 
True, "tuple": (1, 2)}
+        
+        config = Config("test.conf", defaults=defaults, config_dir=".")
+        self.assertEquals(defaults, config.config)
+        
+        config = Config("test.conf", config_dir=".")
+        self.assertEquals({}, config.config)
+    
+    def test_set_get_item(self):
+        config = Config("test.conf", config_dir=".")
+        config["foo"] = 1
+        self.assertEquals(config["foo"], 1)
+        self.assertRaises(ValueError, config.set_item, "foo", "bar")
+        config["foo"] = 2
+        self.assertEquals(config.get_item("foo"), 2)
+        
+        config._save_timer.cancel()
+
+    def test_load(self):
+        d = {"string": "foobar", "int": 1, "float": 0.435, "bool": True, 
"tuple": (1, 2)}
+
+        def check_config():
+            config = Config("test.conf", config_dir=".")
+            
+            self.assertEquals(config["string"], "foobar")
+            self.assertEquals(config["float"], 0.435)
+                
+        # Test loading an old config from 1.1.x
+        import pickle
+        pickle.dump(d, open("test.conf", "wb"))
+        
+        check_config()
+                
+        # Test opening a previous 1.2 config file of just a json object
+        import json
+        json.dump(d, open("test.conf", "wb"), indent=2)
+
+        check_config()
+        
+        # Test opening a previous 1.2 config file of having the format versions
+        # as ints
+        f = open("test.conf", "wb")
+        f.write(str(1) + "\n")
+        f.write(str(1) + "\n")
+        json.dump(d, f, indent=2)
+        f.close()
+        
+        check_config()
+        
+        # Test the 1.2 config format
+        v = {"format": 1, "file": 1}
+        f = open("test.conf", "wb")
+        json.dump(v, f, indent=2)
+        json.dump(d, f, indent=2)
+        f.close()
+        
+        check_config()



--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"deluge-commit" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/deluge-commit?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to