163 lines
4.3 KiB
Python
163 lines
4.3 KiB
Python
|
"""
|
||
|
Configuration Utility Module
|
||
|
|
||
|
This module contains functions to load and verify very simple configuration
|
||
|
files. Python supports ".ini" files, which suck, so this module is used
|
||
|
instead.
|
||
|
|
||
|
Example Configuration File:
|
||
|
|
||
|
include /path/to/other.cf
|
||
|
|
||
|
# these values are the same:
|
||
|
name_protected = "Michael Spang"
|
||
|
name_unprotected = Michael Spang
|
||
|
|
||
|
# these values are not the same:
|
||
|
yes_no = " yes"
|
||
|
no_yes = yes
|
||
|
|
||
|
# this value is an integer
|
||
|
arbitrary_number=2
|
||
|
|
||
|
# this value is not an integer
|
||
|
arbitrary_string="2"
|
||
|
|
||
|
# this is a key with no value
|
||
|
csclub
|
||
|
|
||
|
# this key contains whitespace
|
||
|
white space = sure, why not
|
||
|
|
||
|
# these two lines are treated as one
|
||
|
long line = first line \\
|
||
|
second line
|
||
|
|
||
|
Resultant Dictionary:
|
||
|
|
||
|
{
|
||
|
'name_protected': 'Michael Spang',
|
||
|
'name_unprotected:' 'Michael Spang',
|
||
|
'yes_no': ' yes',
|
||
|
'no_yes': 'yes',
|
||
|
'arbirary_number': 2,
|
||
|
'arbitrary_string': '2',
|
||
|
'csclub': None,
|
||
|
'white space': 'sure, why not'
|
||
|
'long line': 'first line \\n second line'
|
||
|
|
||
|
... (data from other.cf) ...
|
||
|
}
|
||
|
|
||
|
"""
|
||
|
from curses.ascii import isspace
|
||
|
|
||
|
|
||
|
class ConfigurationException(Exception):
|
||
|
"""Exception class for incomplete and incorrect configurations."""
|
||
|
|
||
|
|
||
|
def read(filename, included=None):
|
||
|
"""
|
||
|
Function to read a configuration file into a dictionary.
|
||
|
|
||
|
Parmaeters:
|
||
|
filename - the file to read
|
||
|
included - files previously read (internal)
|
||
|
|
||
|
Exceptions:
|
||
|
IOError - when the configuration file cannot be read
|
||
|
"""
|
||
|
|
||
|
if not included:
|
||
|
included = []
|
||
|
if filename in included:
|
||
|
return {}
|
||
|
included.append(filename)
|
||
|
|
||
|
conffile = open(filename)
|
||
|
|
||
|
options = {}
|
||
|
|
||
|
while True:
|
||
|
|
||
|
line = conffile.readline()
|
||
|
if line == '':
|
||
|
break
|
||
|
|
||
|
# remove comments
|
||
|
if '#' in line:
|
||
|
line = line[:line.find('#')]
|
||
|
|
||
|
# combine lines when the newline is escaped with \
|
||
|
while len(line) > 1 and line[-2] == '\\':
|
||
|
line = line[:-2] + line[-1]
|
||
|
next = conffile.readline()
|
||
|
line += next
|
||
|
if next == '':
|
||
|
break
|
||
|
|
||
|
line = line.strip()
|
||
|
|
||
|
# process include statements
|
||
|
if line.find("include") == 0 and isspace(line[7]):
|
||
|
|
||
|
filename = line[8:].strip()
|
||
|
options.update(read(filename, included))
|
||
|
continue
|
||
|
|
||
|
# split 'key = value' into key and value and strip results
|
||
|
pair = map(str.strip, line.split('=', 1))
|
||
|
|
||
|
# found key and value
|
||
|
if len(pair) == 2:
|
||
|
key, val = pair
|
||
|
|
||
|
# found quoted string?
|
||
|
if val and val[0] == val[-1] == '"':
|
||
|
val = val[1:-1]
|
||
|
|
||
|
# unquoted, found num?
|
||
|
elif val:
|
||
|
try:
|
||
|
if "." in val:
|
||
|
val = float(val)
|
||
|
elif val[0] == '0':
|
||
|
val = int(val, 8)
|
||
|
else:
|
||
|
val = int(val)
|
||
|
except ValueError:
|
||
|
pass
|
||
|
|
||
|
# save key and value
|
||
|
options[key] = val
|
||
|
|
||
|
# found only key, value = None
|
||
|
elif len(pair[0]) > 1:
|
||
|
key = pair[0]
|
||
|
options[key] = None
|
||
|
|
||
|
return options
|
||
|
|
||
|
|
||
|
def check_string_fields(filename, field_list, cfg):
|
||
|
"""Function to verify thatfields are strings."""
|
||
|
|
||
|
for field in field_list:
|
||
|
if field not in cfg or type(cfg[field]) is not str:
|
||
|
raise ConfigurationException('expected string value for option "%s" in "%s"' % (field, filename))
|
||
|
|
||
|
def check_integer_fields(filename, field_list, cfg):
|
||
|
"""Function to verify that fields are integers."""
|
||
|
|
||
|
for field in field_list:
|
||
|
if field not in cfg or type(cfg[field]) not in (int, long):
|
||
|
raise ConfigurationException('expected numeric value for option "%s" in "%s"' % (field, filename))
|
||
|
|
||
|
def check_float_fields(filename, field_list, cfg):
|
||
|
"""Function to verify that fields are integers or floats."""
|
||
|
|
||
|
for field in field_list:
|
||
|
if field not in cfg or type(cfg[field]) not in (float, long, int):
|
||
|
raise ConfigurationException('expected float value for option "%s" in "%s"' % (field, filename))
|