Added string, bool, int, and float conversion to HookeConfigParser
authorW. Trevor King <wking@drexel.edu>
Tue, 3 Aug 2010 19:36:14 +0000 (15:36 -0400)
committerW. Trevor King <wking@drexel.edu>
Tue, 3 Aug 2010 19:36:14 +0000 (15:36 -0400)
hooke/config.py

index 11ee1a8649d0f954187c435b0e1c9feb887c4978..c1776c8eee6367056490269c1e54a0dc0d6b8fe7 100644 (file)
@@ -39,13 +39,33 @@ specific user files, so the user can override the sysadmin who
 in turn overrides the developer defaults.
 """
 
+CONVERT_FROM_STRING = {
+    'string': lambda x: x,
+    'bool': lambda x: x == 'True',
+    'int': lambda x: int(x),
+    'float': lambda x: float(x),
+    }
+"""Functions converting strings to values, keyed by type.
+"""
+
+CONVERT_TO_STRING = {
+    'string': lambda x: x,
+    'bool': lambda x: str(x),
+    'int': lambda x: str(x),
+    'float': lambda x: str(x),
+    }
+"""Functions converting values to strings, keyed by type.
+"""
+
 class Setting (object):
     """An entry (section or option) in HookeConfigParser.
     """
-    def __init__(self, section, option=None, value=None, help=None, wrap=True):
+    def __init__(self, section, option=None, value=None, type='string',
+                 help=None, wrap=True):
         self.section = section
         self.option = option
-        self.value = str(value)
+        self.value = CONVERT_TO_STRING[type](value)
+        self.type = type
         self.help = help
         self.wrap = wrap
 
@@ -83,7 +103,7 @@ class Setting (object):
 
 DEFAULT_SETTINGS = [
     Setting('conditions', help='Default environmental conditions in case they are not specified in the force curve data.'),
-    Setting('conditions', 'temperature', '301', help='Temperature in Kelvin'),
+    Setting('conditions', 'temperature', value='301', type='float', help='Temperature in Kelvin'),
     # Logging settings
     Setting('loggers', help='Configure loggers, see\nhttp://docs.python.org/library/logging.html#configuration-file-format', wrap=False),
     Setting('loggers', 'keys', 'root, hooke', help='Hooke only uses the hooke logger, but other included modules may also use logging and you can configure their loggers here as well.'),
@@ -127,9 +147,9 @@ class HookeConfigParser (configparser.RawConfigParser):
     Examples
     --------
 
+    >>> import pprint
     >>> import sys
-    >>> c = HookeConfigParser(paths=DEFAULT_PATHS,
-    ...                       default_settings=DEFAULT_SETTINGS)
+    >>> c = HookeConfigParser(default_settings=DEFAULT_SETTINGS)
     >>> c.write(sys.stdout) # doctest: +ELLIPSIS
     # Default environmental conditions in case they are not specified in
     # the force curve data.
@@ -144,6 +164,30 @@ class HookeConfigParser (configparser.RawConfigParser):
     # also use logging and you can configure their loggers here as well.
     keys = root, hooke
     ...
+
+    class:`HookeConfigParser` automatically converts typed settings.
+
+    >>> section = 'test conversion'
+    >>> c = HookeConfigParser(default_settings=[
+    ...         Setting(section),
+    ...         Setting(section, option='my string', value='Lorem ipsum', type='string'),
+    ...         Setting(section, option='my bool', value=True, type='bool'),
+    ...         Setting(section, option='my int', value=13, type='int'),
+    ...         Setting(section, option='my float', value=3.14159, type='float'),
+    ...         ])
+    >>> pprint.pprint(c.items(section))  # doctest: +ELLIPSIS
+    [('my string', 'Lorem ipsum'),
+     ('my bool', True),
+     ('my int', 13),
+     ('my float', 3.1415...)]
+
+    However, the regular `.get()` is not typed.  Users are encouraged
+    to use the standard `.get*()` methods.
+
+    >>> c.get('test conversion', 'my bool')
+    'True'
+    >>> c.getboolean('test conversion', 'my bool')
+    True
     """
     def __init__(self, paths=None, default_settings=None, defaults=None,
                  dict_type=OrderedDict, indent='# ', **kwargs):
@@ -156,6 +200,7 @@ class HookeConfigParser (configparser.RawConfigParser):
         if default_settings == None:
             default_settings = []
         self._default_settings = default_settings
+        self._default_settings_dict = {}
         for key in ['initial_indent', 'subsequent_indent']:
             if key not in kwargs:
                 kwargs[key] = indent
@@ -167,6 +212,8 @@ class HookeConfigParser (configparser.RawConfigParser):
             # reversed cause: http://docs.python.org/library/configparser.html
             # "When adding sections or items, add them in the reverse order of
             # how you want them to be displayed in the actual file."
+            self._default_settings_dict[
+                (setting.section, setting.option)] = setting
             if setting.section not in self.sections():
                 self.add_section(setting.section)
             if setting.option != None:
@@ -258,6 +305,28 @@ class HookeConfigParser (configparser.RawConfigParser):
         if local_fp:
             fp.close()
 
+    def items(self, section, *args, **kwargs):
+        """Return a list of tuples with (name, value) for each option
+        in the section.
+        """
+        # Can't use super() because RawConfigParser is a classic class
+        #return super(HookeConfigParser, self).items(section, *args, **kwargs)
+        items = configparser.RawConfigParser.items(
+            self, section, *args, **kwargs)
+        for i,kv in enumerate(items):
+            key,value = kv
+            setting = self._default_settings_dict[(section, key)]
+            items[i] = (key, CONVERT_FROM_STRING[setting.type](value))
+        return items
+
+    def set(self, section, option, value):
+        """Set an option."""
+        setting = self._default_settings_dict[(section, option)]
+        value = CONVERT_TO_STRING[setting.type](value)
+        # Can't use super() because RawConfigParser is a classic class
+        #return super(HookeConfigParser, self).set(section, option, value)
+        configparser.RawConfigParser.set(self, section, option, value)
+
     def optionxform(self, option):
         """
 
@@ -273,8 +342,7 @@ class TestHookeConfigParser (unittest.TestCase):
         """
         from multiprocessing import Queue
         q = Queue()
-        a = HookeConfigParser(
-            paths=DEFAULT_PATHS, default_settings=DEFAULT_SETTINGS)
+        a = HookeConfigParser(default_settings=DEFAULT_SETTINGS)
         q.put(a)
         b = q.get(a)
         for attr in ['_dict', '_defaults', '_sections', '_config_paths',