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