11# Roundup Issue Tracker configuration support
22#
3- # $Id: configuration.py,v 1.16 2004-07-28 09:46:58 a1s Exp $
3+ # $Id: configuration.py,v 1.17 2004-10-17 17:35:32 a1s Exp $
44#
55__docformat__ = "restructuredtext"
66
7+ import getopt
78import imp
89import os
910import time
@@ -303,7 +304,7 @@ class FilePathOption(Option):
303304 """
304305
305306 class_description = "The path may be either absolute or relative\n " \
306- " to the directory containig this config file."
307+ "to the directory containig this config file."
307308
308309 def get (self ):
309310 _val = Option .get (self )
@@ -588,15 +589,18 @@ class Config:
588589 section_options = None
589590 # mapping from option names and aliases to Option instances
590591 options = None
592+ # actual name of the config file. set on load.
593+ filepath = os .path .join (HOME , INI_FILE )
591594
592- def __init__ (self , home_dir = None , layout = None ):
595+ def __init__ (self , config_path = None , layout = None ):
593596 """Initialize confing instance
594597
595598 Parameters:
596- home_dir:
597- optional configuration directory.
598- If passed, load the config from that directory
599- after processing config layout (if any).
599+ config_path:
600+ optional directory or file name of the config file.
601+ If passed, load the config after processing layout (if any).
602+ If config_path is a directory name, use default base name
603+ of the config file.
600604 layout:
601605 optional configuration layout, a sequence of
602606 section definitions suitable for .add_section()
@@ -611,8 +615,8 @@ def __init__(self, home_dir=None, layout=None):
611615 if layout :
612616 for section in layout :
613617 self .add_section (* section )
614- if home_dir is not None :
615- self .load (home_dir )
618+ if config_path is not None :
619+ self .load (config_path )
616620
617621 def add_section (self , section , options , description = None ):
618622 """Define new config section
@@ -698,16 +702,30 @@ def reset(self):
698702 for _option in self .items ():
699703 _option .reset ()
700704
701- # This is a placeholder. TBD.
702705 # Meant for commandline tools.
703706 # Allows automatic creation of configuration files like this:
704707 # roundup-server -p 8017 -u roundup --save-config
705- def getopt (self , args , ** options ):
708+ def getopt (self , args , short_options = "" , long_options = (),
709+ config_load_options = ("C" , "config" ), ** options
710+ ):
706711 """Apply options specified in command line arguments.
707712
708713 Parameters:
709714 args:
710715 command line to parse (sys.argv[1:])
716+ short_options:
717+ optional string of letters for command line options
718+ that are not config options
719+ long_options:
720+ optional list of names for long options
721+ that are not config options
722+ config_load_options:
723+ two-element sequence (letter, long_option) defining
724+ the options for config file. If unset, don't load
725+ config file; otherwise config file is read prior
726+ to applying other options. Short option letter
727+ must not have a colon and long_option name must
728+ not have an equal sign or '--' prefix.
711729 options:
712730 mapping from option names to command line option specs.
713731 e.g. server_port="p:", server_user="u:"
@@ -720,6 +738,53 @@ def getopt(self, args, **options):
720738 processed options are removed from returned option list.
721739
722740 """
741+ # take a copy of long_options
742+ long_options = list (long_options )
743+ # build option lists
744+ cfg_names = {}
745+ booleans = []
746+ for (name , letter ) in options .items ():
747+ cfg_name = name .upper ()
748+ short_opt = "-" + letter [0 ]
749+ name = name .lower ().replace ("_" , "-" )
750+ cfg_names .update ({short_opt : cfg_name , "--" + name : cfg_name })
751+
752+ short_options += letter
753+ if letter [- 1 ] == ":" :
754+ long_options .append (name + "=" )
755+ else :
756+ booleans .append (short_opt )
757+ long_options .append (name )
758+
759+ if config_load_options :
760+ short_options += config_load_options [0 ] + ":"
761+ long_options .append (config_load_options [1 ] + "=" )
762+ # compute names that will be searched in getopt return value
763+ config_load_options = (
764+ "-" + config_load_options [0 ],
765+ "--" + config_load_options [1 ],
766+ )
767+ # parse command line arguments
768+ optlist , args = getopt .getopt (args , short_options , long_options )
769+ # load config file if requested
770+ if config_load_options :
771+ for option in optlist :
772+ if option [0 ] in config_load_options :
773+ self .load_ini (option [1 ])
774+ optlist .remove (option )
775+ break
776+ # apply options
777+ extra_options = []
778+ for (opt , arg ) in optlist :
779+ if (opt in booleans ): # and not arg
780+ arg = "yes"
781+ try :
782+ name = cfg_names [opt ]
783+ except KeyError :
784+ extra_options .append ((opt , arg ))
785+ else :
786+ self [name ] = arg
787+ return (extra_options , args )
723788
724789 # option and section locators (used in option access methods)
725790
@@ -746,30 +811,48 @@ def _get_unset_options(self):
746811 need_set .setdefault (option .section , []).append (option .setting )
747812 return need_set
748813
814+ def _adjust_options (self , config ):
815+ """Load ad-hoc option definitions from ConfigParser instance."""
816+ pass
817+
818+ def _get_name (self ):
819+ """Return the service name for config file heading"""
820+ return ""
821+
749822 # file operations
750823
751- def load_ini (self , home_dir , defaults = None ):
824+ def load_ini (self , config_path , defaults = None ):
752825 """Set options from config.ini file in given home_dir
753826
754827 Parameters:
755- home_dir:
756- config home directory
828+ config_path:
829+ directory or file name of the config file.
830+ If config_path is a directory name, use default
831+ base name of the config file
757832 defaults:
758833 optional dictionary of defaults for ConfigParser
759834
760835 Note: if home_dir does not contain config.ini file,
761836 no error is raised. Config will be reset to defaults.
762837
763838 """
839+ if os .path .isdir (config_path ):
840+ home_dir = config_path
841+ config_path = os .path .join (config_path , self .INI_FILE )
842+ else :
843+ home_dir = os .path .dirname (config_path )
764844 # parse the file
765845 config_defaults = {"HOME" : home_dir }
766846 if defaults :
767847 config_defaults .update (defaults )
768848 config = ConfigParser .ConfigParser (config_defaults )
769849 config .read ([os .path .join (home_dir , self .INI_FILE )])
770- # .ini file loaded ok. set the options, starting from HOME
771- self .reset ()
850+ # .ini file loaded ok.
772851 self .HOME = home_dir
852+ self .filepath = config_path
853+ self ._adjust_options (config )
854+ # set the options, starting from HOME
855+ self .reset ()
773856 for option in self .items ():
774857 option .load_ini (config )
775858
@@ -789,12 +872,12 @@ def save(self, ini_file=None):
789872
790873 """
791874 if ini_file is None :
792- ini_file = os . path . join ( self .HOME , self . INI_FILE )
875+ ini_file = self .filepath
793876 _tmp_file = os .path .splitext (ini_file )[0 ]
794877 _bak_file = _tmp_file + ".bak"
795878 _tmp_file = _tmp_file + ".tmp"
796879 _fp = file (_tmp_file , "wt" )
797- _fp .write ("# %s configuration file\n " % self [ "TRACKER_NAME" ] )
880+ _fp .write ("# %s configuration file\n " % self . _get_name () )
798881 _fp .write ("# Autogenerated at %s\n " % time .asctime ())
799882 need_set = self ._get_unset_options ()
800883 if need_set :
@@ -891,40 +974,17 @@ class UserConfig(Config):
891974
892975 """
893976
894- def load_ini (self , home_dir , defaults = None ):
895- """Load options from config.ini file in given home_dir
896-
897- Parameters:
898- home_dir:
899- config home directory
900- defaults:
901- optional dictionary of defaults for ConfigParser
902-
903- Options are automatically created as they are read
904- from the config file.
905-
906- Note: if home_dir does not contain config.ini file,
907- no error is raised. Config will be reset to defaults.
908-
909- """
910- # parse the file
911- config_defaults = {"HOME" : home_dir }
912- if defaults :
913- config_defaults .update (defaults )
914- config = ConfigParser .ConfigParser (defaults )
915- config .read ([os .path .join (home_dir , self .INI_FILE )])
916- # .ini file loaded ok.
917- self .HOME = home_dir
977+ def _adjust_options (self , config ):
978+ # config defaults appear in all sections.
979+ # we'll need to filter them out.
980+ defaults = config .defaults ().keys ()
918981 # see what options are already defined and add missing ones
919982 preset = [(option .section , option .setting ) for option in self .items ()]
920983 for section in config .sections ():
921984 for name in config .options (section ):
922- if (section , name ) not in preset :
985+ if ((section , name ) not in preset ) \
986+ and (name not in defaults ):
923987 self .add_option (Option (self , section , name ))
924- # set the options
925- self .reset ()
926- for option in self .items ():
927- option .load_ini (config )
928988
929989class CoreConfig (Config ):
930990
@@ -968,6 +1028,9 @@ def _get_unset_options(self):
9681028 del need_set ["mail" ]
9691029 return need_set
9701030
1031+ def _get_name (self ):
1032+ return self ["TRACKER_NAME" ]
1033+
9711034 def reset (self ):
9721035 Config .reset (self )
9731036 if self .ext :
0 commit comments