author | Alberto Bertogli
<albertito@gmail.com> 2007-08-17 20:09:20 UTC |
committer | Alberto Bertogli
<albertito@gmail.com> 2007-08-17 20:09:20 UTC |
parent | 09219f05edcb166e2c29e9d1fadfa03e9415a972 |
configparser.py | +138 | -0 |
diff --git a/configparser.py b/configparser.py new file mode 100644 index 0000000..92e9696 --- /dev/null +++ b/configparser.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python + +""" +A simple, pythonic configuration parser. +Alberto Bertogli (albertito@gmail.com) + +It parses files of the form: + +------------------ 8< ------------------ 8< ------------------ +key1 = value1 +key2 = 3453 + +group1.key1 = 26.56 +group1.key2 = 01.59 +------------------ 8< ------------------ 8< ------------------ + +It knows the available keys, and can check for data types and parse +accordingly. It supports default values. + +Use it like: + +# Define the available keys, their types, and the default values +settings = ( + ('key1', str), + ('key2', int), + ('key3', str, 'default value'), + + ('group1.key1', float), + ('group1.key2', float, 9283), +) + +# Parse the given filename +config = Config('config', settings) + +# Use the results in a python-friendly way +print config.key1 +print config.group1.key2 +""" + + +# Use new style objects everywhere +__metaclass__ = type + +class ConfigError (Exception): + pass + +class Config: + def __init__(self, filename = None, settings = ()): + if not filename: + return + + # settings is a list of either: + # - name or (name,), which means mandatory string + # - (name, class), which means mandatory of the given class + # - (name, class, default_value), which means optional of the + # given class, with the given default + mandatory = {} + optional = {} + have_set = [] + for i in settings: + if isinstance(i, str): + mandatory[i] = str + elif len(i) == 1: + mandatory[i[0]] = str + elif len(i) == 2: + mandatory[i[0]] = i[1] + elif len(i) == 3: + optional[i[0]] = (i[1], i[2]) + + try: + f = open(filename) + except: + desc = "Can't find config file: %s" % filename + raise ConfigError, desc + + count = 0 + for l in f: + count += 1 + l = l.strip() + + if (not l) or l.startswith('#'): + continue + + if '=' in l: + name, val = l.split('=', 1) + name = name.strip() + name = name.replace('\s', '_') + val = val.strip() + if name in mandatory: + val = self._convert_to(name, val, + mandatory[name], count) + elif name in optional: + val = self._convert_to(name, val, + optional[name][0], + count) + + have_set.append(name) + self._setattr(name, val, count) + + for i in mandatory.keys(): + if i not in have_set: + desc = "Mandatory option %s is not set" % i + raise ConfigError, desc + + for (name, (cls, default)) in optional.iteritems(): + if name not in have_set: + self._setattr(name, default, -1) + + + def _convert_to(self, name, val, cls, lineno): + try: + return cls(val) + except: + desc = "Line %d: " % lineno + desc += "Error converting %s " % name + desc += "to mandatory data type %s" % cls + raise ConfigError, desc + + + def _setattr(self, name, val, lineno): + if '.' in name: + parent, name = name.split('.', 1) + try: + obj = getattr(self, parent) + except AttributeError: + obj = Config() + setattr(self, parent, obj) + obj._setattr(name, val, lineno) + else: + obj = getattr(self, name, None) + if isinstance(obj, Config): + desc = "Line %d: " % lineno + desc += "Overriding a group with a value" + raise ConfigError, desc + + setattr(self, name, val) + +