Fixing library search shit.
[mspang/pyceo.git] / ceo / conf.py
1 """
2 Configuration Utility Module
3
4 This module contains functions to load and verify very simple configuration
5 files. Python supports ".ini" files, which suck, so this module is used
6 instead.
7
8 Example Configuration File:
9
10     include /path/to/other.cf
11
12     # these values are the same:
13     name_protected = "Michael Spang"
14     name_unprotected = Michael Spang
15     
16     # these values are not the same:
17     yes_no = " yes"
18     no_yes =  yes
19     
20     # this value is an integer
21     arbitrary_number=2
22     
23     # this value is not an integer
24     arbitrary_string="2"
25     
26     # this is a key with no value
27     csclub
28
29     # this key contains whitespace
30     white space = sure, why not
31
32     # these two lines are treated as one
33     long line = first line \\
34                 second line
35
36 Resultant Dictionary:
37
38     {
39       'name_protected': 'Michael Spang',
40       'name_unprotected:' 'Michael Spang',
41       'yes_no': ' yes',
42       'no_yes': 'yes',
43       'arbirary_number': 2,
44       'arbitrary_string': '2',
45       'csclub': None,
46       'white space': 'sure, why not'
47       'long line': 'first line \\n               second line' 
48       
49       ... (data from other.cf) ...
50     }
51
52 """
53 from curses.ascii import isspace
54
55
56 class ConfigurationException(Exception):
57     """Exception class for incomplete and incorrect configurations."""
58     
59
60 def read(filename, included=None):
61     """
62     Function to read a configuration file into a dictionary.
63     
64     Parmaeters:
65         filename - the file to read
66         included - files previously read (internal)
67     
68     Exceptions:
69         IOError - when the configuration file cannot be read
70     """
71
72     if not included:
73         included = []
74     if filename in included:
75         return {}
76     included.append(filename)
77
78     conffile = open(filename)
79     
80     options = {}
81
82     while True:
83
84         line = conffile.readline()
85         if line == '':
86             break
87
88         # remove comments
89         if '#' in line:
90             line = line[:line.find('#')]
91
92         # combine lines when the newline is escaped with \
93         while len(line) > 1 and line[-2] == '\\':
94             line = line[:-2] + line[-1]
95             next = conffile.readline()
96             line += next
97             if next == '':
98                 break
99
100         line = line.strip()
101
102         # process include statements
103         if line.find("include") == 0 and isspace(line[7]):
104
105             filename = line[8:].strip()
106             options.update(read(filename, included))
107             continue
108
109         # split 'key = value' into key and value and strip results
110         pair = map(str.strip, line.split('=', 1))
111         
112         # found key and value
113         if len(pair) == 2:
114             key, val = pair
115
116             # found quoted string?
117             if val and val[0] == val[-1] == '"':
118                 val = val[1:-1]
119
120             # unquoted, found num?
121             elif val:
122                 try:
123                     if "." in val:
124                         val = float(val)
125                     elif val[0] == '0':
126                         val = int(val, 8)
127                     else:
128                         val = int(val)
129                 except ValueError:
130                     pass
131             
132             # save key and value
133             options[key] = val
134
135         # found only key, value = None
136         elif len(pair[0]) > 1:
137             key = pair[0]
138             options[key] = None
139
140     return options
141
142
143 def check_string_fields(filename, field_list, cfg):
144     """Function to verify thatfields are strings."""
145
146     for field in field_list:
147         if field not in cfg or type(cfg[field]) is not str:
148             raise ConfigurationException('expected string value for option "%s" in "%s"' % (field, filename))
149
150 def check_integer_fields(filename, field_list, cfg):
151     """Function to verify that fields are integers."""
152
153     for field in field_list:
154         if field not in cfg or type(cfg[field]) not in (int, long):
155             raise ConfigurationException('expected numeric value for option "%s" in "%s"' % (field, filename))
156
157 def check_float_fields(filename, field_list, cfg):
158     """Function to verify that fields are integers or floats."""
159
160     for field in field_list:
161         if field not in cfg or type(cfg[field]) not in (float, long, int):
162             raise ConfigurationException('expected float value for option "%s" in "%s"' % (field, filename))