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

Roundup Issue Tracker: http://roundup-tracker.org/