Added 'list_only' option (and modified 'run()' to respect it).
[python/dscho.git] / Lib / dos-8x3 / configpa.py
blobdd8b6d866726300c035a17abab86967cc109ab55
1 """Configuration file parser.
3 A setup file consists of sections, lead by a "[section]" header,
4 and followed by "name: value" entries, with continuations and such in
5 the style of RFC 822.
7 The option values can contain format strings which refer to other values in
8 the same section, or values in a special [DEFAULT] section.
10 For example:
12 something: %(dir)s/whatever
14 would resolve the "%(dir)s" to the value of dir. All reference
15 expansions are done late, on demand.
17 Intrinsic defaults can be specified by passing them into the
18 ConfigParser constructor as a dictionary.
20 class:
22 ConfigParser -- responsible for for parsing a list of
23 configuration files, and managing the parsed database.
25 methods:
27 __init__(defaults=None)
28 create the parser and specify a dictionary of intrinsic defaults. The
29 keys must be strings, the values must be appropriate for %()s string
30 interpolation. Note that `__name__' is always an intrinsic default;
31 it's value is the section's name.
33 sections()
34 return all the configuration section names, sans DEFAULT
36 options(section)
37 return list of configuration options for the named section
39 read(filenames)
40 read and parse the list of named configuration files
42 get(section, option, raw=0, vars=None)
43 return a string value for the named option. All % interpolations are
44 expanded in the return values, based on the defaults passed into the
45 constructor and the DEFAULT section. Additional substitutions may be
46 provided using the `vars' argument, which must be a dictionary whose
47 contents override any pre-existing defaults.
49 getint(section, options)
50 like get(), but convert value to an integer
52 getfloat(section, options)
53 like get(), but convert value to a float
55 getboolean(section, options)
56 like get(), but convert value to a boolean (currently defined as 0 or
57 1, only)
58 """
60 import sys
61 import string
62 import re
64 DEFAULTSECT = "DEFAULT"
68 # exception classes
69 class Error:
70 def __init__(self, msg=''):
71 self._msg = msg
72 def __repr__(self):
73 return self._msg
75 class NoSectionError(Error):
76 def __init__(self, section):
77 Error.__init__(self, 'No section: %s' % section)
78 self.section = section
80 class DuplicateSectionError(Error):
81 def __init__(self, section):
82 Error.__init__(self, "Section %s already exists" % section)
83 self.section = section
85 class NoOptionError(Error):
86 def __init__(self, option, section):
87 Error.__init__(self, "No option `%s' in section: %s" %
88 (option, section))
89 self.option = option
90 self.section = section
92 class InterpolationError(Error):
93 def __init__(self, reference, option, section, rawval):
94 Error.__init__(self,
95 "Bad value substitution:\n"
96 "\tsection: [%s]\n"
97 "\toption : %s\n"
98 "\tkey : %s\n"
99 "\trawval : %s\n"
100 % (section, option, reference, rawval))
101 self.reference = reference
102 self.option = option
103 self.section = section
105 class MissingSectionHeaderError(Error):
106 def __init__(self, filename, lineno, line):
107 Error.__init__(
108 self,
109 'File contains no section headers.\nfile: %s, line: %d\n%s' %
110 (filename, lineno, line))
111 self.filename = filename
112 self.lineno = lineno
113 self.line = line
115 class ParsingError(Error):
116 def __init__(self, filename):
117 Error.__init__(self, 'File contains parsing errors: %s' % filename)
118 self.filename = filename
119 self.errors = []
121 def append(self, lineno, line):
122 self.errors.append((lineno, line))
123 self._msg = self._msg + '\n\t[line %2d]: %s' % (lineno, line)
127 class ConfigParser:
128 def __init__(self, defaults=None):
129 self.__sections = {}
130 if defaults is None:
131 self.__defaults = {}
132 else:
133 self.__defaults = defaults
135 def defaults(self):
136 return self.__defaults
138 def sections(self):
139 """Return a list of section names, excluding [DEFAULT]"""
140 # self.__sections will never have [DEFAULT] in it
141 return self.__sections.keys()
143 def add_section(self, section):
144 """Create a new section in the configuration.
146 Raise DuplicateSectionError if a section by the specified name
147 already exists.
149 if self.__sections.has_key(section):
150 raise DuplicateSectionError(section)
151 self.__sections[section] = {}
153 def has_section(self, section):
154 """Indicate whether the named section is present in the configuration.
156 The DEFAULT section is not acknowledged.
158 return self.__sections.has_key(section)
160 def options(self, section):
161 try:
162 opts = self.__sections[section].copy()
163 except KeyError:
164 raise NoSectionError(section)
165 opts.update(self.__defaults)
166 return opts.keys()
168 def read(self, filenames):
169 """Read and parse a list of filenames."""
170 if type(filenames) is type(''):
171 filenames = [filenames]
172 for file in filenames:
173 try:
174 fp = open(file, 'r')
175 self.__read(fp)
176 except IOError:
177 pass
179 def get(self, section, option, raw=0, vars=None):
180 """Get an option value for a given section.
182 All % interpolations are expanded in the return values, based on the
183 defaults passed into the constructor, unless the optional argument
184 `raw' is true. Additional substitutions may be provided using the
185 `vars' argument, which must be a dictionary whose contents overrides
186 any pre-existing defaults.
188 The section DEFAULT is special.
190 try:
191 sectdict = self.__sections[section].copy()
192 except KeyError:
193 if section == DEFAULTSECT:
194 sectdict = {}
195 else:
196 raise NoSectionError(section)
197 d = self.__defaults.copy()
198 d.update(sectdict)
199 # Update with the entry specific variables
200 if vars:
201 d.update(vars)
202 option = string.lower(option)
203 try:
204 rawval = d[option]
205 except KeyError:
206 raise NoOptionError(option, section)
207 # do the string interpolation
208 if raw:
209 return rawval
211 value = rawval # Make it a pretty variable name
212 depth = 0
213 while depth < 10: # Loop through this until it's done
214 depth = depth + 1
215 if not string.find(value, "%("):
216 try:
217 value = value % d
218 except KeyError, key:
219 raise InterpolationError(key, option, section, rawval)
220 else:
221 return value
223 def __get(self, section, conv, option):
224 return conv(self.get(section, option))
226 def getint(self, section, option):
227 return self.__get(section, string.atoi, option)
229 def getfloat(self, section, option):
230 return self.__get(section, string.atof, option)
232 def getboolean(self, section, option):
233 v = self.get(section, option)
234 val = string.atoi(v)
235 if val not in (0, 1):
236 raise ValueError, 'Not a boolean: %s' % v
237 return val
240 # Regular expressions for parsing section headers and options. Note a
241 # slight semantic change from the previous version, because of the use
242 # of \w, _ is allowed in section header names.
243 __SECTCRE = re.compile(
244 r'\[' # [
245 r'(?P<header>[-\w]+)' # `-', `_' or any alphanum
246 r'\]' # ]
248 __OPTCRE = re.compile(
249 r'(?P<option>[-.\w]+)' # - . _ alphanum
250 r'[ \t]*[:=][ \t]*' # any number of space/tab,
251 # followed by separator
252 # (either : or =), followed
253 # by any # space/tab
254 r'(?P<value>.*)$' # everything up to eol
257 def __read(self, fp):
258 """Parse a sectioned setup file.
260 The sections in setup file contains a title line at the top,
261 indicated by a name in square brackets (`[]'), plus key/value
262 options lines, indicated by `name: value' format lines.
263 Continuation are represented by an embedded newline then
264 leading whitespace. Blank lines, lines beginning with a '#',
265 and just about everything else is ignored.
267 cursect = None # None, or a dictionary
268 optname = None
269 lineno = 0
270 e = None # None, or an exception
271 while 1:
272 line = fp.readline()
273 if not line:
274 break
275 lineno = lineno + 1
276 # comment or blank line?
277 if string.strip(line) == '' or line[0] in '#;':
278 continue
279 if string.lower(string.split(line)[0]) == 'rem' \
280 and line[0] == "r": # no leading whitespace
281 continue
282 # continuation line?
283 if line[0] in ' \t' and cursect is not None and optname:
284 value = string.strip(line)
285 if value:
286 cursect[optname] = cursect[optname] + '\n ' + value
287 # a section header or option header?
288 else:
289 # is it a section header?
290 mo = self.__SECTCRE.match(line)
291 if mo:
292 sectname = mo.group('header')
293 if self.__sections.has_key(sectname):
294 cursect = self.__sections[sectname]
295 elif sectname == DEFAULTSECT:
296 cursect = self.__defaults
297 else:
298 cursect = {'__name__': sectname}
299 self.__sections[sectname] = cursect
300 # So sections can't start with a continuation line
301 optname = None
302 # no section header in the file?
303 elif cursect is None:
304 raise MissingSectionHeaderError(fp.name, lineno, `line`)
305 # an option line?
306 else:
307 mo = self.__OPTCRE.match(line)
308 if mo:
309 optname, optval = mo.group('option', 'value')
310 optname = string.lower(optname)
311 optval = string.strip(optval)
312 # allow empty values
313 if optval == '""':
314 optval = ''
315 cursect[optname] = optval
316 else:
317 # a non-fatal parsing error occurred. set up the
318 # exception but keep going. the exception will be
319 # raised at the end of the file and will contain a
320 # list of all bogus lines
321 if not e:
322 e = ParsingError(fp.name)
323 e.append(lineno, `line`)
324 # if any parsing errors occurred, raise an exception
325 if e:
326 raise e