1 """TurboGears configuration"""
2
3 __all__ = ['update_config', 'get', 'update']
4
5 import os, glob, re
6
7 import cherrypy
8 from cherrypy import request, config as cp_config, log as cp_log
9 from configobj import ConfigObj
10 import pkg_resources
11 import logging.handlers
12
13
14 server = cp_config
15
16
17 server_sections = ('DEFAULT', 'global', 'server')
18
19
20
21 app = dict()
22
23
24 logging_sections = ('logging',)
25
26
28 """TurboGears configuration error."""
29
30
44
45
47 """Helper function for getting the handler from the logging config."""
48 for key, handler in handlers.items():
49 try:
50 cls = handler.get('class')
51 args = handler.get('args', tuple())
52 level = handler.get('level', None)
53 try:
54 cls = eval(cls, logging.__dict__)
55 except NameError:
56 try:
57 cls = eval(cls, logging.handlers.__dict__)
58 except NameError, err:
59 raise ConfigError("Specified class in handler %s"
60 " is not a recognizable logger name" % key)
61 try:
62 handler_obj = cls(*eval(args, logging.__dict__))
63 except IOError, err:
64 raise ConfigError("Missing or wrong argument to %s"
65 " in handler %s -> %s " % (cls.__name__,key,err))
66 except TypeError, err:
67 raise ConfigError("Wrong format for arguments to %s"
68 " in handler %s -> %s" % (cls.__name__,key,err))
69 if level:
70 level = eval(level, logging.__dict__)
71 handler_obj.setLevel(level)
72 except KeyError:
73 raise ConfigError("No class specified for logging handler %s"
74 % key)
75 formatter = handler.get('formatter', None)
76 if formatter:
77 try:
78 formatter = formatters[formatter]
79 except KeyError:
80 raise ConfigError("Handler %s references unknown formatter %s"
81 % (key, formatter))
82 handler_obj.setFormatter(formatter)
83 handlers[key] = handler_obj
84
85
87 """Helper function for getting the loggers from the logging config."""
88 for key, logger in loggers.items():
89 qualname = logger.get('qualname', None)
90 if qualname:
91 log = logging.getLogger(qualname)
92 else:
93 log = logging.getLogger()
94
95 level = logger.get('level', None)
96 if level:
97 level = eval(level, logging.__dict__)
98 else:
99 level = logging.NOTSET
100 log.setLevel(level)
101
102 propagate = logger.get('propagate', None)
103 if propagate is not None:
104 log.propagate = propagate
105
106 cfghandlers = logger.get('handlers', None)
107 if cfghandlers:
108 if isinstance(cfghandlers, basestring):
109 cfghandlers = [cfghandlers]
110 for handler in cfghandlers:
111 try:
112 handler = handlers[handler]
113 except KeyError:
114 raise ConfigError("Logger %s references unknown handler %s"
115 % (key, handler))
116 log.addHandler(handler)
117 if qualname == 'cherrypy.error':
118 cp_log.error_log = log
119 elif qualname == 'cherrypy.access':
120 cp_log.access_log = log
121
122
164
165
167 """Return a dict with default global config settings."""
168 return dict(
169 current_dir_uri=os.path.abspath(os.getcwd())
170 )
171
172
174 """Read configuration from given config file and/or module.
175
176 See the docstring of the 'update_config' function for parameter description.
177
178 Returns a config.ConfigObj object.
179
180 """
181 defaults = config_defaults()
182
183 if modulename:
184 firstdot = modulename.find('.')
185 if firstdot < 0:
186 raise ConfigError('Config file package not specified')
187 lastdot = modulename.rfind('.')
188 top_level_package = modulename[:firstdot]
189 packagename = modulename[:lastdot]
190 modname = modulename[lastdot+1:]
191 modfile = pkg_resources.resource_filename(packagename, modname + '.cfg')
192 if not os.path.exists(modfile):
193 modfile = pkg_resources.resource_filename(packagename, modname)
194 if os.path.isdir(modfile):
195 configfiles = glob.glob(os.path.join(modfile, '*.cfg'))
196 else:
197 configfiles = [modfile]
198 configdata = ConfigObj(unrepr=True)
199 top_level_dir = os.path.normpath(pkg_resources.resource_filename(
200 top_level_package, ''))
201 package_dir = os.path.normpath(pkg_resources.resource_filename(
202 packagename, ''))
203 defaults.update(dict(top_level_dir=top_level_dir,
204 package_dir=package_dir))
205 configdata.merge(dict(DEFAULT=defaults))
206 for configfile2 in configfiles:
207 configdata2 = ConfigObj(configfile2, unrepr=True)
208 configdata2.merge(dict(DEFAULT=defaults))
209 configdata.merge(configdata2)
210
211 if configfile:
212 if modulename:
213 configdata2 = ConfigObj(configfile, unrepr=True)
214 configdata2.merge(dict(DEFAULT=defaults))
215 configdata.merge(configdata2)
216 else:
217 configdata = ConfigObj(configfile, unrepr=True)
218 return configdata
219
220
222 """Update the system configuration from given config file and/or module.
223
224 'configfile' is a ConfigObj (INI-style) config file, 'modulename' a module
225 path in dotted notation. The function looks for files with a ".cfg"
226 extension if the given module name refers to a package directory or a file
227 with the base name of the right-most part of the module path and a ".cfg"
228 extension added.
229
230 If both 'configfile' and 'modulname' are specified, the module is read
231 first, followed by the config file. This means that the config file's
232 options override the options in the module file.
233
234 """
235 configdict = config_obj(configfile, modulename).dict()
236 update(configdict)
237
238
239 _obsolete_names = set("""autoreload before_main filters
240 baseurl_filter cache_filter decoding_filter encoding_filter gzip_filter
241 log_debug_info_filter nsgmls_filter response_headers_filter
242 session_authenticate_filter session_auth session_filter
243 static_filter tidy_filter virtual_host_filter wsgiappfilter xmlrpc_filter
244 log_access_file log_file log_to_screen show_tracebacks throw_errors
245 """.split())
246
247 _tools_names = ('toscawidgets', 'visit')
248
250 """Check name and return translated name where applicable.
251
252 In order to keep the config settings simple and compatible among
253 versions, we hide the fact that some features are currently implemented
254 as CherryPy tools and just silently add settings with the tools prefix.
255
256 """
257 basename = name.split('.', 1)[0]
258 if basename in _tools_names:
259 return 'tools.' + name
260 elif basename in _obsolete_names:
261 raise KeyError("The config setting %s is obsolete."
262 " Check the CherryPy 3 docs for the new setting." % name)
263
264
266 """Update the configuration with values from a dictionary.
267
268 The values are sent to the appropriate config system
269 (server, app, or logging) automatically.
270
271 """
272 global server, app
273
274
275
276 for key, value in configvalues.iteritems():
277 if not isinstance(key, basestring):
278 raise ValueError("Key error in config update: %r" % key)
279 if key.startswith('/'):
280 if key in app:
281 app[key].update(value)
282 else:
283 app[key] = value
284 mounted_app = cherrypy.tree.apps.get('')
285 if mounted_app:
286 mounted_app.merge({key:value})
287 elif key in logging_sections:
288 configure_loggers(value)
289 elif key in server_sections and isinstance(value, dict):
290 server.update(value)
291 for key in value:
292 add_key = _check_name(key)
293 if add_key:
294 server[add_key] = value[key]
295 else:
296 server[key] = value
297 add_key = _check_name(key)
298 if add_key:
299 server[add_key] = value
300
301
302 -def get(key, *args):
303 """Get a config setting.
304
305 Uses request.config if available, otherwise defaults back to the
306 server's config settings.
307
308 """
309 config = getattr(request, 'stage', None) and request.config or server
310 value = config.get(key, *args)
311 if value and key == 'sqlobject.dburi' and os.name == 'nt':
312 value = re.sub('///([A-Za-z]):', r'///\1|', value)
313 return value
314
315
317 """Copy server config settings."""
318 return server.copy()
319
320
322 """A dict.items() equivalent for config values.
323
324 Returns request specific information if available, otherwise falls back
325 to server config.
326
327 """
328 if getattr(request, 'stage', None):
329 return request.config.items()
330 else:
331 return server.items()
332