1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 """
18 Configfile parsing
19 """
20 __author__ = "André Malo"
21 __docformat__ = "epytext en"
22 __all__ = [
23 'ConfigFileSettings',
24 'Error',
25 'ConfigNotFoundError',
26 'ConfigMissingError',
27 'ConfigInvalidError',
28 'ConfigMappingSectionNotFoundError',
29 'ConfigMappingSpecInvalidError',
30 'ConfigSectionNotFoundError',
31 'ConfigOptionUnknownError',
32 ]
33
34
35 import ConfigParser, sys, os
36 from svnmailer import settings, util
37
38
39
41 """ Base exception for this module """
42 pass
43
45 """ Config file not found """
46 pass
47
49 """ Config not specified and not found on default locations """
50 pass
51
53 """ Config file has errors """
54 pass
55
57 """ Config mapping section was not found """
58 pass
59
61 """ Config mapping spec was not recognized """
62 pass
63
65 """ Specified config section was not found """
66 pass
67
69 """ An unknown option was parsed """
70 pass
71
72
74 """ Provide settings from config
75
76 @cvar MAPSECTION: The mapping section name; if C{None},
77 mapping is effectively disabled
78 @type MAPSECTION: C{str}
79
80 @ivar _config: The config object
81 @type _config: C{ConfigParser.ConfigParser}
82 """
83 __implements__ = [settings.Settings]
84
85 MAPSECTION = "maps"
86
87 - def init(self, *args, **kwargs):
88 """ Implements the C{init} method of L{settings.Settings}
89
90 @exception ConfigInvalidError: invalid config options
91 @exception ConfigMissingError: see L{_loadConfig}
92 @exception ConfigNotFoundError: see L{_loadConfig}
93 @exception ConfigSectionNotFoundError: see L{_passConfig}
94 @exception ConfigOptionUnkownError: see L{_passConfig}
95 @exception ConfigMappingSpecInvalidError: see L{_applyMaps}
96 @exception ConfigMappingSectionNotFoundError: see L{_getPlainMap}
97 """
98 try:
99 self._init(*args, **kwargs)
100 except (ValueError, TypeError, UnicodeError, ConfigParser.Error), exc:
101 raise ConfigInvalidError, str(exc), sys.exc_info()[2]
102
103
104 - def _init(self, options):
105 """ Actual implementation of C{self.init()}
106
107 @param options: runtime options
108 @type options: C{optparse.OptionParser}
109
110 @exception ConfigMissingError: see L{_loadConfig}
111 @exception ConfigNotFoundError: see L{_loadConfig}
112 @exception ConfigSectionNotFoundError: see L{_passConfig}
113 @exception ConfigOptionUnkownError: see L{_passConfig}
114 @exception ConfigMappingSpecInvalidError: see L{_applyMaps}
115 @exception ConfigMappingSectionNotFoundError: see L{_getPlainMap}
116 """
117 self._initRuntime(options)
118 self._loadConfig()
119 self._initGeneral()
120 self._initGroups()
121
122
136
137
139 """ Returns the default group dict
140
141 @param container: The default container
142 @type container: C{svnmailer.settings.GroupSettingsContainer}
143
144 @return: The default dict
145 @rtype: C{dict}
146 """
147 ddict = dict(container._dict_)
148 ddict.update({
149 "_def_for_repos": container.for_repos,
150 "_def_for_paths": container.for_paths,
151 })
152
153 return ddict
154
155
176
177
179 """ Initializes the general config
180
181 @exception ConfigSectionNotFoundError: [general] not found
182 """
183 self.general = self.getGeneralContainer()
184 self._passConfig(self.general, 'general')
185 self._config.remove_section('general')
186
187
208
209
211 """ Passes the options to the specified container
212
213 @param container: The container object
214 @type container: C{svnmailer.util.Struct}
215
216 @param section: The config section name
217 @type section: C{str}
218
219 @exception ConfigSectionNotFoundError: The specified section was
220 not found in the config file
221 @exception ConfigOptionUnkownError: There was an unknown
222 config option in the config file.
223 """
224 try:
225 for option in self._config.options(section):
226
227 if option[:1] in ('_', '-'):
228 raise ConfigOptionUnknownError(
229 "Unknown option '%s' in section [%s]" %
230 (option, section)
231 )
232
233 try:
234 container._set_(
235 option.replace('-', '_'),
236 self._config.get(section, option, raw = True)
237 )
238 except AttributeError:
239 raise ConfigOptionUnknownError(
240 "Unknown option '%s' in section [%s]" %
241 (option, section)
242 )
243 except ConfigParser.NoSectionError, exc:
244 raise ConfigSectionNotFoundError(str(exc))
245
246
248 """ Parse config file
249
250 @return: parsed config
251 @rtype: C{ConfigParser.ConfigParser}
252
253 @exception ConfigNotFoundError: some configfile could not
254 be opened
255 @exception ConfigMissingError: see L{_findConfig}
256 @exception ConfigMappingSpecInvalidError: see L{_applyMaps}
257 @exception ConfigMappingSectionNotFoundError: see L{_getPlainMap}
258 """
259 config_fp = self._findConfig()
260 self._config = self._createConfigParser()
261 try:
262 self._config.readfp(config_fp, config_fp.name)
263 config_fp.close()
264 except IOError, exc:
265 raise ConfigNotFoundError("%s: %s" % (config_fp.name, str(exc)))
266
267 if self._config.has_section("general"):
268 self._applyCharset()
269 self._applyIncludes(config_fp.name)
270
271 self._applyMaps()
272
273
275 """ Returns a ConfigParser instance
276
277 @return: The ConfigParser instance
278 @rtype: C{ConfigParser.ConfigParser}
279 """
280 return ConfigParser.ConfigParser()
281
282
284 """ Finds and opens the main config file
285
286 @param _file: The function to open the file
287 @type _file: C{callable}
288
289 @return: The open descriptor
290 @rtype: file like object
291
292 @exception ConfigMissingError: config neither specified nor
293 on default locations found. Default locations are (tried
294 in that order):
295 - <repos>/conf/mailer.conf
296 - <scriptdir>/mailer.conf
297 - /etc/svn-mailer.conf
298 @exception ConfigNotFoundError: specified configfile could not
299 be opened
300 """
301 import errno
302
303 config_file = self.runtime.config
304 if config_file:
305 try:
306 return config_file == '-' and sys.stdin or _file(config_file)
307 except IOError, exc:
308 raise ConfigNotFoundError("%s: %s" % (config_file, str(exc)))
309
310 for config_file in self._getDefaultConfigFiles():
311 try:
312 return _file(config_file)
313 except IOError, exc:
314
315 if exc[0] != errno.ENOENT:
316 raise ConfigNotFoundError("%s: %s" % (
317 config_file, str(exc)
318 ))
319
320 raise ConfigMissingError("No config file found")
321
322
324 """ Resolves all map definitions
325
326 @TODO: raise an error on unknown options
327
328 @exception ConfigMappingSpecInvalidError: The mapping spec was
329 invalid
330 @exception ConfigMappingSectionNotFoundError: see L{_getPlainMap}
331 """
332 section = self.MAPSECTION
333 if section is None or not self._config.has_section(section):
334 return
335
336 self._maps_ = {}
337 remove_sections = [section]
338 for option in self._config.options(section):
339 if option[:1] in ('_', '-'):
340 raise ConfigOptionUnknownError(
341 "Unknown option '%s' in section [%s]" %
342 (option, section)
343 )
344
345 value = self._config.get(section, option, raw = True)
346 if value[:1] == '[' and value[-1:] == ']':
347 this_section = value[1:-1]
348 self._maps_[option.replace('-', '_')] = \
349 self._getPlainMap(this_section)
350 remove_sections.append(this_section)
351 else:
352 raise ConfigMappingSpecInvalidError(
353 "Invalid mapping specification %r = %r" % (option, value)
354 )
355
356 for name in dict.fromkeys(remove_sections).keys():
357 self._config.remove_section(name)
358
359
361 """ Returns a plain map for a particular section
362
363 @param section: The mapping section
364 @type section: C{str}
365
366 @return: The mapping function
367 @rtype: C{callable}
368
369 @exception ConfigMappingSectionNotFoundError: The specified
370 section was not found
371 """
372 try:
373 mdict = dict([
374 (option, self._config.get(section, option, raw = True))
375 for option in self._config.options(section)
376 ])
377 except ConfigParser.NoSectionError, exc:
378 raise ConfigMappingSectionNotFoundError(str(exc))
379
380 def mapfunc(value):
381 """ Mapping function """
382 return mdict.get(value, value)
383
384 return mapfunc
385
386
388 """ Applies the includes found in [general]
389
390 @param origfile: original filename
391 @type origfile: C{str}
392
393 @param _file: The function to open the file
394 @type _file: C{callable}
395
396 @exception ConfigNotFoundError: Error reading an included file
397 """
398 opt = "include_config"
399 try:
400 try:
401 includes = self._config.get("general", opt, raw = True).strip()
402 except ConfigParser.NoOptionError:
403 opt = "include-config"
404 includes = self._config.get("general", opt, raw = True).strip()
405 except ConfigParser.NoOptionError:
406
407 pass
408 else:
409 self._config.remove_option("general", opt)
410 if not len(includes):
411 return
412
413 origpath = os.path.dirname(os.path.abspath(origfile))
414 includes = [
415 util.filename.toLocale(
416 config_file, self._charset_, self.runtime.path_encoding
417 )
418 for config_file in util.splitCommand(includes) if config_file
419 ]
420
421 for config_file in includes:
422 try:
423 config_fp = _file(os.path.join(origpath, config_file))
424 self._config.readfp(config_fp, config_fp.name)
425 config_fp.close()
426 except IOError, exc:
427 raise ConfigNotFoundError("%s: %s" % (
428 config_file, str(exc)
429 ))
430
431
433 """ Applies the charset found in [general] """
434 opt = "config_charset"
435 try:
436 try:
437 charset = self._config.get("general", opt, raw = True).strip()
438 except ConfigParser.NoOptionError:
439 opt = "config-charset"
440 charset = self._config.get("general", opt, raw = True).strip()
441 except ConfigParser.NoOptionError:
442
443 pass
444 else:
445 self._config.remove_option("general", opt)
446 if charset:
447 self._charset_ = charset
448
449
451 """ Returns the default config files
452
453 @return: The list
454 @rtype: C{list}
455 """
456 argv0 = util.filename.fromLocale(
457 _sys.argv[0], self.runtime.path_encoding
458 )
459 if isinstance(argv0, unicode):
460 candidates = [util.filename.toLocale(
461 name, locale_enc = self.runtime.path_encoding
462 ) for name in [
463 _os.path.join(
464 self.runtime.repository, u'conf', u'mailer.conf'
465 ),
466 _os.path.join(_os.path.dirname(argv0), u'mailer.conf'),
467 u'/etc/svn-mailer.conf',
468 ]
469 ]
470 else:
471
472 candidates = [
473 _os.path.join(self.runtime.repository, 'conf', 'mailer.conf'),
474 _os.path.join(_os.path.dirname(argv0), 'mailer.conf'),
475 _os.path.join(_os.path.sep, "etc", "svn-mailer.conf"),
476 ]
477
478 return candidates
479