1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16  """ 
 17  Runtime settings for the svnmailer 
 18  ================================== 
 19   
 20  This module defines one public class, called L{Settings}. This class is the 
 21  storage container for all settings used by the svnmailer. L{Settings} is an 
 22  abstract class. There is just one method that must be implemented -- 
 23  L{Settings.init}. This method is responsible for filling the container 
 24  properly. An implementor of the L{Settings} class can be found in the 
 25  L{svnmailer.config} module. 
 26   
 27  This module further defines the Settings subcontainers 
 28  L{GroupSettingsContainer}, L{GeneralSettingsContainer} and 
 29  L{RuntimeSettingsContainer}, but you should not instantiate them directly -- 
 30  L{Settings} provides methods that return instances of these containers. 
 31  """ 
 32  __author__    = "André Malo" 
 33  __docformat__ = "epytext en" 
 34  __all__       = ['Settings', 'modes'] 
 35   
 36   
 37  from svnmailer import typedstruct, struct_accessors 
 38   
 39   
 41      """ Generic token container 
 42   
 43          @ivar valid_tokens: The valid mode tokens (str, str, ...) 
 44          @type valid_tokens: C{tuple} 
 45      """ 
 46      valid_tokens = () 
 47   
 49          """ Initialization """ 
 50          self.valid_tokens = args 
 51          for token in self.valid_tokens: 
 52              setattr(self, token.encode('us-ascii'), token) 
   53   
 54  modes = _Tokens('commit', 'propchange', 'lock', 'unlock') 
 55  xpath = _Tokens(u'yes', u'no', u'ignore') 
 56  showenc = _Tokens(u'yes', u'no', u'nondefault') 
 57   
 58   
 60      """ Define the members of the group settings 
 61   
 62          The following members are defined: 
 63              - C{_name}: Name of the group 
 64              - C{_def_for_repos}: default for_repos regex 
 65              - C{_def_for_paths}: default for_paths regex 
 66              - C{for_repos}: Repository regex 
 67              - C{for_paths}: Path regex (inside the repos) 
 68              - C{exclude_paths}: Exclude path regex to prevent for_paths from 
 69                  being applied 
 70              - C{ignore_if_other_matches}: this group will be ignored if there 
 71                  are any other groups selected for a particular path 
 72              - C{show_nonmatching_paths}: How to deal with paths that are not 
 73                  matched by the group 
 74              - C{commit_subject_template}: Subject template for commit mail 
 75              - C{propchange_subject_template}: Subject template for revpropchanges 
 76              - C{lock_subject_template}: Subject template for locks 
 77              - C{unlock_subject_template}: Subject template for unlocks 
 78              - C{commit_subject_prefix}: Subject prefix for commit mail 
 79              - C{propchange_subject_prefix}: Subject prefix for revpropchanges 
 80              - C{lock_subject_prefix}: Subject prefix for locks 
 81              - C{unlock_subject_prefix}: Subject prefix for unlocks 
 82              - C{max_subject_length}: Maximum subject length 
 83              - C{from_addr}: C{From:} address format string 
 84              - C{to_addr}: C{To:} address format string 
 85              - C{to_fake}: C{To:} non-address format string 
 86              - C{bcc_addr}: C{Bcc:} address format string 
 87              - C{reply_to_addr}: C{Reply-To:} address format string 
 88              - C{diff_command}: The diff command to use 
 89              - C{generate_diffs}: List of actions for which diffs are generated 
 90              - C{browser_base_url}: type and format string of the repository 
 91                  browser base url 
 92              - C{custom_header}: custom header name and format template 
 93              - C{to_newsgroup}: The newsgroup where the notification should be 
 94                  posted to 
 95              - C{long_news_action}: The action to take on huge commit postings 
 96              - C{long_mail_action}: The action to take on huge commit mails 
 97              - C{mail_transfer_encoding}: Content-Transfer-Encoding for mails 
 98              - C{news_transfer_encoding}: Content-Transfer-Encoding for news 
 99              - C{mail_type}: The mail construction type 
100              - C{extract_x509_author}: Treat author as x509 subject and try to 
101                  extract author's real name and email address 
102              - C{cia_project_name}: The project name used for CIA notifications 
103              - C{cia_project_module}: The project module used for CIA 
104                  notifications 
105              - C{cia_project_branch}: The project branch used for CIA 
106                  notifications 
107              - C{cia_project_submodule}: The project submodule used for CIA 
108                  notifications 
109              - C{cia_project_path}: The project path, which will be stripped from 
110                  the absolute node path 
111              - C{apply_charset_property}: Should svnmailer:content-charset 
112                  properties be recognized? 
113              - C{show_applied_charset}: Show the encoding of the files in the 
114                  diff? 
115              - C{viewcvs_base_url}: (I{deprecated}, use C{browser_base_url} 
116                  instead) format string for the viewcvs URL 
117   
118          @param space: The namespace to pollute 
119          @type space: C{dict} 
120   
121          @return: The members definition 
122          @rtype: C{dict} 
123      """ 
124      args = { 
125          'space'      : space, 
126          'typemap'    : struct_accessors.typemap, 
127          'the_members': { 
128              '_name'                      : 'unicode', 
129              '_def_for_repos'             : 'regex', 
130              '_def_for_paths'             : 'regex', 
131              'for_repos'                  : ('regex',      {'map': True}), 
132              'for_paths'                  : ('regex',      {'map': True}), 
133              'exclude_paths'              : ('regex',      {'map': True}), 
134              'ignore_if_other_matches'    : 'humanbool', 
135              'show_nonmatching_paths'     : ('token', 
136                                             {'map': True, 
137                                              'allowed': xpath.valid_tokens}), 
138              'commit_subject_template'    : ('unicode',    {'map': True}), 
139              'propchange_subject_template': ('unicode',    {'map': True}), 
140              'lock_subject_template'      : ('unicode',    {'map': True}), 
141              'unlock_subject_template'    : ('unicode',    {'map': True}), 
142              'commit_subject_prefix'      : ('unicode', 
143                                             {'subst': True, 'map': True}), 
144              'propchange_subject_prefix'  : ('unicode', 
145                                             {'subst': True, 'map': True}), 
146              'lock_subject_prefix'        : ('unicode', 
147                                             {'subst': True, 'map': True}), 
148              'unlock_subject_prefix'      : ('unicode', 
149                                             {'subst': True, 'map': True}), 
150              'max_subject_length'         : 'int', 
151              'from_addr'                  : ('tokenlist', 
152                                             {'subst': True, 'map': True}), 
153              'to_addr'                    : ('tokenlist', 
154                                             {'subst': True, 'map': True}), 
155              'to_fake'                    : ('unicode', 
156                                             {'subst': True, 'map': True}), 
157              'bcc_addr'                   : ('tokenlist', 
158                                             {'subst': True, 'map': True}), 
159              'reply_to_addr'              : ('unicode', 
160                                             {'subst': True, 'map': True}), 
161              'to_newsgroup'               : ('tokenlist', 
162                                             {'subst': True, 'map': True}), 
163              'diff_command'               : ('unicommand', {'map': True}), 
164              'generate_diffs'             : 'tokenlist', 
165              'browser_base_url'           : ('unicode', 
166                                             {'subst': True, 'map': True}), 
167              'long_mail_action'           : ('mailaction', {'map': True}), 
168              'long_news_action'           : ('mailaction', {'map': True}), 
169              'mail_type'                  : ('unicode',    {'map': True}), 
170              'mail_transfer_encoding'     : 'unicode', 
171              'news_transfer_encoding'     : 'unicode', 
172              'custom_header'              : ('unicode', 
173                                             {'subst': True, 'map': True}), 
174              'extract_x509_author'        : 'humanbool', 
175              'cia_rpc_server'             : ('unicode',    {'map': True}), 
176              'cia_project_name'           : ('unicode', 
177                                             {'subst': True, 'map': True}), 
178              'cia_project_module'         : ('unicode', 
179                                             {'subst': True, 'map': True}), 
180              'cia_project_branch'         : ('unicode', 
181                                             {'subst': True, 'map': True}), 
182              'cia_project_submodule'      : ('unicode', 
183                                             {'subst': True, 'map': True}), 
184              'cia_project_path'           : ('unicode', 
185                                             {'subst': True, 'map': True}), 
186              'apply_charset_property'     : 'humanbool', 
187              'show_applied_charset'       : ('token', 
188                                             {'allowed': showenc.valid_tokens}), 
189   
190               
191              'viewcvs_base_url'           : ('unicode', 
192                                             {'subst': True, 'map': True}), 
193          }, 
194          'aliases': { 
195              'suppress_if_match'  : 'ignore_if_other_matches', 
196              'fallback'           : 'ignore_if_other_matches', 
197              'reply_to'           : 'reply_to_addr', 
198              'x509_author'        : 'extract_x509_author', 
199              'charset_property'   : 'apply_charset_property', 
200              'truncate_subject'   : 'max_subject_length', 
201              'subject_length'     : 'max_subject_length', 
202              'diff'               : 'diff_command', 
203              'nonmatching_paths'  : 'show_nonmatching_paths', 
204              'nongroup_paths'     : 'show_nonmatching_paths', 
205              'show_nongroup_paths': 'show_nonmatching_paths', 
206          }, 
207      } 
208   
209      return typedstruct.members(**args) 
 210   
211   
213      """ Define the members of the general settings 
214   
215          The following members are defined: 
216              - C{diff_command}: The diff command 
217              - C{sendmail_command}: The sendmail compatible command 
218              - C{smtp_host}: The smtp host (C{host[:port]}) 
219              - C{smtp_user}: The smtp auth. user 
220              - C{smtp_pass}: The smtp auth. password 
221              - C{debug_all_mails_to}: All mails are sent to these addresses 
222                  (for debugging purposes) 
223              - C{cia_rpc_server}: The XML-RPC server running the CIA tracker 
224              - C{tempdir}: The directory to use for temporary files 
225   
226          @param space: The namespace to pollute 
227          @type space: C{dict} 
228   
229          @return: The members definition 
230          @rtype: C{dict} 
231      """ 
232      args = { 
233          'space'      : space, 
234          'typemap'    : struct_accessors.typemap, 
235          'the_members': { 
236              'sendmail_command'  : ('unicommand', {'map': True}), 
237              'smtp_host'         : ('unicode',    {'map': True}), 
238              'smtp_user'         : ('quotedstr',  {'map': True}), 
239              'smtp_pass'         : ('quotedstr',  {'map': True}), 
240              'nntp_host'         : ('unicode',    {'map': True}), 
241              'nntp_user'         : ('quotedstr',  {'map': True}), 
242              'nntp_pass'         : ('quotedstr',  {'map': True}), 
243              'debug_all_mails_to': ('tokenlist',  {'map': True}), 
244              'tempdir'           : ('filename',   {'map': True}), 
245   
246               
247              'cia_rpc_server'    : ('unicode',    {'map': True}), 
248              'diff_command'      : ('unicommand', {'map': True}), 
249          }, 
250          'aliases'    : { 
251              'mail_command' : 'sendmail_command', 
252              'smtp_hostname': 'smtp_host', 
253              'diff'         : 'diff_command', 
254          }, 
255      } 
256   
257      return typedstruct.members(**args) 
 258   
259   
261      """ Define the members of the runtime settings 
262   
263          The following members are defined: 
264              - C{_repos}: The repository object 
265              - C{stdin}: The stdin, read once 
266              - C{path_encoding}: The path-encoding parameter 
267              - C{debug}: debug mode (True/False) 
268              - C{revision}: committed revision number 
269              - C{repository}: path to the repository 
270              - C{config}: supplied config file name 
271              - C{mode}: running mode (see L{modes}) 
272              - C{author}: Author of the commit or revpropchange 
273              - C{propname}: Property changed (in revpropchange) 
274              - C{action}: The revprop action (M, A, D) 
275   
276          @param space: The namespace to pollute 
277          @type space: C{dict} 
278   
279          @return: The members definition 
280          @rtype: C{dict} 
281      """ 
282      args = { 
283          'space'      : space, 
284          'typemap'    : struct_accessors.typemap, 
285          'the_members': { 
286              '_repos'       : None,        
287              'stdin'        : 'stdin', 
288              'path_encoding': 'string', 
289              'debug'        : 'bool', 
290              'revision'     : 'int', 
291              'repository'   : 'filename', 
292              'config'       : 'filename', 
293              'mode'         : 'string', 
294              'author'       : 'unicode', 
295              'propname'     : 'unicode', 
296              'action'       : 'unicode',   
297          }, 
298          'aliases'    : None, 
299      } 
300   
301      return typedstruct.members(**args) 
 302   
303   
305      """ Container for group settings 
306   
307          @see: L{groupMembers} for the actual member list 
308      """ 
309      __slots__ = groupMembers(locals()) 
310   
312          """ compare some of the attributes 
313   
314              @note: It uses a list of attributes that are compared if two 
315                  of these types are tested for equality. Keep in mind that 
316                  this comparision takes place, when the decision is made 
317                  whether a mail for more than one group should be sent more 
318                  than once (if the groups are not equal). All attributes, but 
319                  the ones returned by L{_getIgnorableMembers} are compared. 
320   
321              @see: L{_getIgnorableMembers} 
322   
323              @param other: The object compared to 
324              @type other: C{GroupSettingsContainer} 
325   
326              @return: Are the objects equal? 
327              @rtype: C{bool} 
328          """ 
329          if type(self) != type(other): 
330              return False 
331   
332          attrs = [name for name in self._members_ 
333              if name not in self._getIgnorableMembers() 
334          ] 
335   
336          for name in attrs: 
337              if getattr(self, name) != getattr(other, name): 
338                  return False 
339   
340          return True 
 341   
342   
344          """ Returns the list of member names that be ignored in comparisons 
345   
346              This method called by L{_compare}. Override this method to modify 
347              the list. 
348   
349              @return: The list 
350              @rtype: C{list} 
351          """ 
352          return [ 
353              '_name', '_def_for_repos', '_def_for_paths', 
354              'for_repos', 'for_paths', 'exclude_paths', 
355              'ignore_if_other_matches', 'to_addr', 'from_addr', 
356              'to_newsgroup', 'custom_header', 'cia_rpc_server', 
357              'cia_project_name', 'cia_project_module', 'cia_project_branch', 
358              'cia_project_submodule', 'cia_project_path', 
359          ] 
  360   
361   
363      """ Container for general settings 
364   
365          @see: L{generalMembers} for the actual member list 
366      """ 
367      __slots__ = generalMembers(locals()) 
 368   
369   
371      """ Container for runtime settings 
372   
373          @see: L{runtimeMembers} for the actual member list 
374      """ 
375      __slots__ = runtimeMembers(locals()) 
 376   
377   
379      """ Settings management 
380   
381          @note: The C{init} method must be overridden to do the actual 
382          initialization. 
383   
384          @ivar groups: group settings list 
385          @type groups: C{list} of C{GroupSettingsContainer} 
386   
387          @ivar general: General settings 
388          @type general: C{GeneralSettingsContainer} 
389   
390          @ivar runtime: Runtime settigs 
391          @type runtime: C{RuntimeSettingsContainer} 
392   
393          @ivar debug: Debug state 
394          @type debug: C{bool} 
395   
396          @ivar _charset_: The charset used for settings recoding 
397          @type _charset_: C{str} 
398   
399          @ivar _maps_: The value mappers to use or C{None} 
400          @type _maps_: C{dict} 
401      """ 
402   
404          """ Constructor 
405   
406              Don't override this one. Override C{init()} instead. 
407          """ 
408           
409          self._charset_  = 'us-ascii' 
410          self._fcharset_ = None 
411          self._maps_     = None 
412   
413          self.groups = [] 
414          self.general  = None 
415          self.runtime  = None 
416   
417           
418          self.init(*args, **kwargs) 
419   
420           
421          self._checkInitialization() 
 422   
423   
425          """ Checks if all containers are filled """ 
426          if not(self.general and self.runtime and self.groups): 
427              raise RuntimeError("Settings are not completely initialized") 
 428   
429   
430 -    def init(self, *args, **kwargs): 
 431          """ Abstract initialization method """ 
432          raise NotImplementedError() 
 433   
434   
436          """ Returns the basic arguments for container initialization 
437   
438              @return: The args 
439              @rtype: C{list} 
440          """ 
441          return [ 
442              self._maps_, 
443              {'encoding': self._charset_, 'path_encoding': self._fcharset_} 
444          ] 
 445   
446   
448          """ Returns an initialized group settings container 
449   
450              @return: The container object 
451              @rtype: C{GroupSettingsContainer} 
452          """ 
453          return GroupSettingsContainer(*self._getArgs(), **kwargs) 
 454   
455   
457          """ Returns an initialized default group settings container 
458   
459              @return: The container object 
460              @rtype: C{GroupSettingsContainer} 
461          """ 
462          args = self._getArgs() 
463          args[0] = None  
464          return GroupSettingsContainer(*args, **kwargs) 
 465   
466   
468          """ Returns an initialized general settings container 
469   
470              @return: The container object 
471              @rtype: C{GeneralSettingsContainer} 
472          """ 
473          return GeneralSettingsContainer(*self._getArgs(), **kwargs) 
 474   
475   
477          """ Returns an initialized runtime settings container 
478   
479              Note that the runtime settings (from commandline) 
480              are always assumed to be utf-8 encoded. 
481   
482              @return: The container object 
483              @rtype: C{RuntimeSettingsContainer} 
484          """ 
485          args = self._getArgs() 
486          args[0] = None 
487          args[1]["encoding"] = "utf-8" 
488          return RuntimeSettingsContainer(*args, **kwargs) 
  489