Mercurial > p > roundup > code
comparison roundup/config.py @ 604:13719594278b config-0-4-0-branch
I've re-worked the config structure a little so it's simpler
(one less file) and added a unit test so we can be sure it's working.
| author | Richard Jones <richard@users.sourceforge.net> |
|---|---|
| date | Wed, 06 Feb 2002 07:11:13 +0000 |
| parents | fdee2ff82b40 |
| children |
comparison
equal
deleted
inserted
replaced
| 603:986354c4b1fb | 604:13719594278b |
|---|---|
| 1 import sys | 1 '''Organise the configuration files for roundup installations. |
| 2 import os | 2 |
| 3 import ConfigParser | 3 There's two configuration files of interest to any given roundup instance: |
| 4 import string | 4 |
| 5 roundup.rc: | |
| 6 This is the global configuration file. It specifies: | |
| 7 . default configuration variable values | |
| 8 . instance names and locations | |
| 9 | |
| 10 <instance home>/config.rc: | |
| 11 This defines the configuration overrides for the instance | |
| 12 | |
| 13 Config values are determined in order: | |
| 14 1. instance config application-specific section: | |
| 15 'MAIL GATEWAY' | |
| 16 'HTTP SERVER' | |
| 17 'CGI' | |
| 18 'ADMIN' | |
| 19 2. instance config 'DEFAULT' with some added vars: | |
| 20 'instance_home': the home dir | |
| 21 3. all the entries from the roundup.rc global '[DEFAULT]' | |
| 22 4. pre-set application defaults (in this file) | |
| 23 | |
| 24 Some variables will raise errors if an attempt is made to look them up | |
| 25 using the application defaults: | |
| 26 . mailhost | |
| 27 . mail_domain | |
| 28 . issue_tracker_email | |
| 29 . issue_tracker_web | |
| 30 . admin_email | |
| 31 | |
| 32 ''' | |
| 33 import sys, os, ConfigParser | |
| 5 | 34 |
| 6 class Error(Exception): | 35 class Error(Exception): |
| 7 pass | 36 pass |
| 8 | 37 |
| 9 class UnknownInstanceLocation(Error): | 38 class UnknownInstanceLocation(Error): |
| 11 | 40 |
| 12 class NoInstanceConfigFile(Error): | 41 class NoInstanceConfigFile(Error): |
| 13 pass | 42 pass |
| 14 | 43 |
| 15 def debug_mode(): | 44 def debug_mode(): |
| 16 """ | 45 """Returns the basic debug mode/level. |
| 17 Returns the basic debug mode/level. | |
| 18 """ | 46 """ |
| 19 return os.environ.get('ROUNDUP_DEBUG', 0) | 47 return os.environ.get('ROUNDUP_DEBUG', 0) |
| 20 | 48 |
| 21 def loadBaseConfig(): | 49 def loadBaseConfig(): |
| 22 """ | 50 """Loads the base configuration for Roundup. |
| 23 Loads the base configuration for Roundup. | |
| 24 """ | 51 """ |
| 25 | 52 |
| 26 c = ConfigParser.ConfigParser() | |
| 27 | 53 |
| 28 ## | 54 # CTB: this is where to search for all overrides, including |
| 29 ## CTB: this is where to search for all overrides, including | 55 # system-specific files, registry settings, etc. |
| 30 ## system-specific files, registry settings, etc. | 56 # |
| 31 ## | |
| 32 | |
| 33 # For the moment, search for the config file in | 57 # For the moment, search for the config file in |
| 34 # | 58 # |
| 59 # ${ROUNDUP_CONF}/roundup.rc | |
| 35 # %(sys.prefix)s/share/roundup/roundup.rc, | 60 # %(sys.prefix)s/share/roundup/roundup.rc, |
| 36 # | 61 # |
| 37 # with ROUNDUP_CONF overriding it. | |
| 38 | |
| 39 filenames_to_check = [] # list of files to check: | 62 filenames_to_check = [] # list of files to check: |
| 40 if os.environ.has_key('ROUNDUP_CONF'): | 63 if os.environ.has_key('ROUNDUP_CONF'): |
| 41 filenames_to_check.append(os.environ['ROUNDUP_CONF']) | 64 filenames_to_check.append(os.environ['ROUNDUP_CONF']) |
| 42 | 65 filenames_to_check.append('%s/share/roundup/roundup.rc'%sys.prefix) |
| 43 filenames_to_check.append('%s/share/roundup/roundup.rc'%(sys.prefix,)) | |
| 44 | 66 |
| 67 # right, now try to get a config | |
| 45 for filename in filenames_to_check: | 68 for filename in filenames_to_check: |
| 46 if os.path.exists(filename): | 69 if os.path.exists(filename): |
| 47 c.read(filename) | |
| 48 break | 70 break |
| 49 else: | 71 else: |
| 50 raise Error("could not find configuration file") | 72 raise Error("could not find configuration file") |
| 51 | 73 |
| 52 if debug_mode(): | 74 if debug_mode(): |
| 53 print 'Loaded configuration from "%s".'%(filename,) | 75 print 'Loaded configuration from "%s".'%(filename,) |
| 54 | 76 |
| 55 # we also want to give a base path for other config file names; | 77 return BaseConfig(filename) |
| 56 # for the moment, make it the base path of the filename we chose. | |
| 57 base_path = os.path.dirname(filename) | |
| 58 | |
| 59 return BaseConfig(c, base_path) | |
| 60 | 78 |
| 61 class BaseConfig: | 79 class BaseConfig: |
| 80 """A container for the installation-wide roundup configuration. | |
| 62 """ | 81 """ |
| 63 A container for the installation-wide roundup configuration. | 82 def __init__(self, filename): |
| 64 """ | 83 self.filename = filename |
| 65 def __init__(self, c, base_path): | 84 self.conf = ConfigParser.ConfigParser() |
| 66 assert isinstance(c, ConfigParser.ConfigParser) | 85 self.conf.read(filename) |
| 67 self.conf = c | |
| 68 self.base_path = base_path | |
| 69 | 86 |
| 70 def get(self, group, attr): | 87 def get(self, group, attr): |
| 71 return self.conf.get(group, attr) | 88 return self.conf.get(group, attr) |
| 72 | 89 |
| 73 def loadInstances(self): | 90 def listInstances(self): |
| 74 filename = string.strip(self.conf.get('base', 'instances')) | 91 return filter(lambda x:x!='BASE', self.conf.sections()) |
| 75 | 92 |
| 76 # if it looks like an absolute path, leave it alone; otherwise, | 93 def loadInstanceConfig(self, home): |
| 77 # add on the base path. | 94 # set up the defaults for the instance config |
| 78 if filename[0] == '/' or filename[0] == '\\': | 95 defaults = { |
| 79 pass | 96 'instance_home': home, |
| 80 else: | 97 'http_port': '80', |
| 81 filename = os.path.normpath(self.base_path + '/' + filename) | 98 'database': '%(instance_home)s/db', |
| 82 | 99 'templates': '%(instance_home)s/html', |
| 83 defaults_dictionary = { 'roundup_conf_dir' : self.base_path } | 100 'log': '%(instance_home)s/log', |
| 84 | 101 'filter_position': 'bottom', |
| 85 c = ConfigParser.ConfigParser(defaults_dictionary) | 102 'anonymous_access': 'deny', |
| 86 c.read(filename) | 103 'anonymous_register': 'deny', |
| 87 | 104 'messages_to_author': 'no', |
| 88 return InstancesConfig(c, filename) | 105 'email_signature_position': 'bottom', |
| 89 | 106 } |
| 90 class InstancesConfig: | 107 for option in self.conf.options('BASE'): |
| 91 """ | 108 defaults[option] = self.conf.get('BASE', option, 1) |
| 92 A container for the installation-wide list of instances. | |
| 93 """ | |
| 94 def __init__(self, c, filename=""): | |
| 95 assert isinstance(c, ConfigParser.ConfigParser) | |
| 96 self.conf = c | |
| 97 self.filename = filename | |
| 98 | |
| 99 instance_names = {} | |
| 100 instance_dirs = {} | |
| 101 | 109 |
| 102 for name in c.sections(): | 110 # make the instance config |
| 103 dir = c.get(name, 'homedir') | 111 inst = InstanceConfig(defaults) |
| 104 | 112 inst.read(os.path.join(home, 'config.rc')) |
| 105 if instance_names.has_key(dir) or instance_dirs.has_key(name): | 113 inst.validate() |
| 106 error_text = 'ERROR: dir/name correspondence is not unique (%s)'%(self.filename,) | 114 return inst |
| 107 raise ValueError(error_text) | |
| 108 | |
| 109 instance_dirs[name] = dir | |
| 110 instance_names[dir] = name | |
| 111 | |
| 112 self.instance_dirs = instance_dirs | |
| 113 self.instance_names = instance_names | |
| 114 | |
| 115 def getNames(self): | |
| 116 return self.instance_dirs.keys() | |
| 117 | |
| 118 def getNameFromDir(self, dir): | |
| 119 if self.instance_names.has_key(dir): | |
| 120 return self.instance_names[dir] | |
| 121 else: | |
| 122 raise UnknownInstanceLocation(dir) | |
| 123 | |
| 124 def getDirFromName(self, name): | |
| 125 return self.instance_dirs[name] | |
| 126 | |
| 127 def loadConfig(self, name): | |
| 128 instance_dir = self.getDirFromName(name) | |
| 129 | |
| 130 defaults_file = self.conf.get(name, 'defaults') | |
| 131 if not os.path.exists(defaults_file): | |
| 132 raise NoInstanceConfigFile("defaults file %s does not exist"%(defaults_file,)) | |
| 133 | |
| 134 config_file = self.conf.get(name, 'config') | |
| 135 if not os.path.exists(config_file): | |
| 136 raise NoInstanceConfigFile("%s does not exist"%(config_file,)) | |
| 137 | |
| 138 defaults_dictionary = { 'homedir' : instance_dir, | |
| 139 'instance_name' : name, | |
| 140 } | |
| 141 | 115 |
| 142 | 116 |
| 143 c = ConfigParser.ConfigParser(defaults_dictionary) | 117 class InstanceConfig(ConfigParser.ConfigParser): |
| 144 c.read(defaults_file) | 118 """A container for each per-instance configuration. |
| 145 c.read(config_file) | 119 """ |
| 120 def validate(self): | |
| 121 '''Make sure the config is complete | |
| 122 ''' | |
| 123 assert self.has_option('BASE', 'instance_name') | |
| 124 assert self.has_option('BASE', 'mailhost') | |
| 125 assert self.has_option('BASE', 'mail_domain') | |
| 126 assert self.has_option('BASE', 'issue_tracker_email') | |
| 127 assert self.has_option('BASE', 'issue_tracker_web') | |
| 128 assert self.has_option('BASE', 'admin_email') | |
| 146 | 129 |
| 147 return InstanceConfig(c, name, instance_dir) | 130 def getBase(self, name): |
| 131 '''Convenience wrapper | |
| 132 ''' | |
| 133 return self.get('BASE', name) | |
| 148 | 134 |
| 149 class InstanceConfig: | 135 def getMailGW(self, name): |
| 150 """ | 136 '''Look up a var for the mail gateway |
| 151 A container for each per-instance configuration. | 137 ''' |
| 152 """ | 138 return self.get('MAIL GATEWAY', name) |
| 153 | |
| 154 def __init__(self, c, instanceName, instanceDirectory): | |
| 155 assert isinstance(c, ConfigParser.ConfigParser) | |
| 156 self.conf = c | |
| 157 self.name = instanceName | |
| 158 self.directory = instanceDirectory | |
| 159 | 139 |
| 160 def get_name(self): | 140 def getHTTPServer(self, name): |
| 161 return self.name | 141 '''Look up a var for the standalone HTTP server |
| 142 ''' | |
| 143 return self.get('HTTP SERVER', name) | |
| 162 | 144 |
| 163 def get_directory(self): | 145 def getCGI(self, name): |
| 164 return self.directory | 146 '''Look up a var for the cgi script |
| 147 ''' | |
| 148 return self.get('CGI', name) | |
| 165 | 149 |
| 166 def get(self, group, attr): | 150 def getAdmin(self, name): |
| 167 return self.conf.get(group, attr) | 151 '''Look up a var for the admin script |
| 168 | 152 ''' |
| 169 if __name__ == '__main__': | 153 return self.get('ADMIN', name) |
| 170 base_config = loadBaseConfig() | 154 |
| 171 instances = base_config.loadInstances() | 155 def get_default_database_dir(self): |
| 172 | 156 '''Historical method to allow migration to using this new config |
| 173 for k in instances.getNames(): | 157 system... |
| 174 print "%s:%s"%(k, instances.getDirFromName(k),) | 158 ''' |
| 159 return self.get('BASE', 'DATABASE') | |
| 160 |
