439e412bf6a71dc9585cc32bbe46b96bc985e8c4
[public/pyceo-broken.git] / pylib / csc / common / 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     """Function to read a configuration file into a dictionary."""
62
63     if not included:
64         included = []
65     if filename in included:
66         return {}
67     included.append(filename)
68
69     try:
70         conffile = open(filename)
71     except IOError:
72         raise ConfigurationException('unable to read configuration file: "%s"' % filename)
73     
74     options = {}
75
76     while True:
77
78         line = conffile.readline()
79         if line == '':
80             break
81
82         # remove comments
83         if '#' in line:
84             line = line[:line.find('#')]
85
86         # combine lines when the newline is escaped with \
87         while len(line) > 1 and line[-2] == '\\':
88             line = line[:-2] + line[-1]
89             next = conffile.readline()
90             line += next
91             if next == '':
92                 break
93
94         line = line.strip()
95
96         # process include statements
97         if line.find("include") == 0 and isspace(line[7]):
98
99             filename = line[8:].strip()
100             options.update(read(filename, included))
101             continue
102
103         # split 'key = value' into key and value and strip results
104         pair = map(str.strip, line.split('=', 1))
105         
106         # found key and value
107         if len(pair) == 2:
108             key, val = pair
109
110             # found quoted string?
111             if val[0] == val[-1] == '"':
112                 val = val[1:-1]
113
114             # unquoted, found float?
115             else:
116                 try:
117                     if "." in val:
118                         val = float(val)
119                     else:
120                         val = int(val)
121                 except ValueError:
122                     pass
123             
124             # save key and value
125             options[key] = val
126
127         # found only key, value = None
128         elif len(pair[0]) > 1:
129             key = pair[0]
130             options[key] = None
131
132     return options
133
134
135 def check_string_fields(filename, field_list, cfg):
136     """Function to verify thatfields are strings."""
137
138     for field in field_list:
139         if field not in cfg or type(cfg[field]) is not str:
140             raise ConfigurationException('expected string value for option "%s" in "%s"' % (field, filename))
141
142 def check_integer_fields(filename, field_list, cfg):
143     """Function to verify that fields are integers."""
144
145     for field in field_list:
146         if field not in cfg or type(cfg[field]) not in (int, long):
147             raise ConfigurationException('expected numeric value for option "%s" in "%s"' % (field, filename))
148
149 def check_float_fields(filename, field_list, cfg):
150     """Function to verify that fields are integers or floats."""
151
152     for field in field_list:
153         if field not in cfg or type(cfg[field]) not in (float, long, int):
154             raise ConfigurationException('expected float value for option "%s" in "%s"' % (field, filename))