1 """Configuration system for CherryPy.
2
3 Configuration in CherryPy is implemented via dictionaries. Keys are strings
4 which name the mapped value, which may be of any type.
5
6
7 Architecture
8 ------------
9
10 CherryPy Requests are part of an Application, which runs in a global context,
11 and configuration data may apply to any of those three scopes:
12
13 Global: configuration entries which apply everywhere are stored in
14 cherrypy.config.
15
16 Application: entries which apply to each mounted application are stored
17 on the Application object itself, as 'app.config'. This is a two-level
18 dict where each key is a path, or "relative URL" (for example, "/" or
19 "/path/to/my/page"), and each value is a config dict. Usually, this
20 data is provided in the call to tree.mount(root(), config=conf),
21 although you may also use app.merge(conf).
22
23 Request: each Request object possesses a single 'Request.config' dict.
24 Early in the request process, this dict is populated by merging global
25 config entries, Application entries (whose path equals or is a parent
26 of Request.path_info), and any config acquired while looking up the
27 page handler (see next).
28
29
30 Declaration
31 -----------
32
33 Configuration data may be supplied as a Python dictionary, as a filename,
34 or as an open file object. When you supply a filename or file, CherryPy
35 uses Python's builtin ConfigParser; you declare Application config by
36 writing each path as a section header:
37
38 [/path/to/my/page]
39 request.stream = True
40
41 To declare global configuration entries, place them in a [global] section.
42
43 You may also declare config entries directly on the classes and methods
44 (page handlers) that make up your CherryPy application via the '_cp_config'
45 attribute. For example:
46
47 class Demo:
48 _cp_config = {'tools.gzip.on': True}
49
50 def index(self):
51 return "Hello world"
52 index.exposed = True
53 index._cp_config = {'request.show_tracebacks': False}
54
55 Note, however, that this behavior is only guaranteed for the default
56 dispatcher. Other dispatchers may have different restrictions on where
57 you can attach _cp_config attributes.
58
59
60 Namespaces
61 ----------
62
63 Configuration keys are separated into namespaces by the first "." in the key.
64 Current namespaces:
65
66 engine: Controls the 'application engine', including autoreload.
67 These can only be declared in the global config.
68 tree: Grafts cherrypy.Application objects onto cherrypy.tree.
69 These can only be declared in the global config.
70 hooks: Declares additional request-processing functions.
71 log: Configures the logging for each application.
72 These can only be declared in the global or / config.
73 request: Adds attributes to each Request.
74 response: Adds attributes to each Response.
75 server: Controls the default HTTP server via cherrypy.server.
76 These can only be declared in the global config.
77 tools: Runs and configures additional request-processing packages.
78 wsgi: Adds WSGI middleware to an Application's "pipeline".
79 These can only be declared in the app's root config ("/").
80 checker: Controls the 'checker', which looks for common errors in
81 app state (including config) when the engine starts.
82 Global config only.
83
84 The only key that does not exist in a namespace is the "environment" entry.
85 This special entry 'imports' other config entries from a template stored in
86 cherrypy._cpconfig.environments[environment]. It only applies to the global
87 config, and only when you use cherrypy.config.update.
88
89 You can define your own namespaces to be called at the Global, Application,
90 or Request level, by adding a named handler to cherrypy.config.namespaces,
91 app.namespaces, or app.request_class.namespaces. The name can
92 be any string, and the handler must be either a callable or a (Python 2.5
93 style) context manager.
94 """
95
96 import ConfigParser
97 try:
98 set
99 except NameError:
100 from sets import Set as set
101 import sys
102
103 import cherrypy
104
105
106 environments = {
107 "staging": {
108 'engine.autoreload_on': False,
109 'checker.on': False,
110 'tools.log_headers.on': False,
111 'request.show_tracebacks': False,
112 },
113 "production": {
114 'engine.autoreload_on': False,
115 'checker.on': False,
116 'tools.log_headers.on': False,
117 'request.show_tracebacks': False,
118 'log.screen': False,
119 },
120 "embedded": {
121
122 'engine.autoreload_on': False,
123 'checker.on': False,
124 'tools.log_headers.on': False,
125 'request.show_tracebacks': False,
126 'log.screen': False,
127 'engine.SIGHUP': None,
128 'engine.SIGTERM': None,
129 },
130 "test_suite": {
131 'engine.autoreload_on': False,
132 'checker.on': False,
133 'tools.log_headers.on': False,
134 'request.show_tracebacks': True,
135 'log.screen': False,
136 },
137 }
138
146
148 """Merge one app config (from a dict, file, or filename) into another.
149
150 If the given config is a filename, it will be appended to
151 the list of files to monitor for "autoreload" changes.
152 """
153 if isinstance(other, basestring):
154 cherrypy.engine.autoreload.files.add(other)
155
156
157 for section, value_map in as_dict(other).iteritems():
158 base.setdefault(section, {}).update(value_map)
159
160
162 """A dict of config namespace names and handlers.
163
164 Each config entry should begin with a namespace name; the corresponding
165 namespace handler will be called once for each config entry in that
166 namespace, and will be passed two arguments: the config key (with the
167 namespace removed) and the config value.
168
169 Namespace handlers may be any Python callable; they may also be
170 Python 2.5-style 'context managers', in which case their __enter__
171 method should return a callable to be used as the handler.
172 See cherrypy.tools (the Toolbox class) for an example.
173 """
174
176 """Iterate through config and pass it to each namespace handler.
177
178 'config' should be a flat dict, where keys use dots to separate
179 namespaces, and values are arbitrary.
180
181 The first name in each config key is used to look up the corresponding
182 namespace handler. For example, a config entry of {'tools.gzip.on': v}
183 will call the 'tools' namespace handler with the args: ('gzip.on', v)
184 """
185
186 ns_confs = {}
187 for k in config:
188 if "." in k:
189 ns, name = k.split(".", 1)
190 bucket = ns_confs.setdefault(ns, {})
191 bucket[name] = config[k]
192
193
194
195
196
197
198
199 for ns, handler in self.iteritems():
200 exit = getattr(handler, "__exit__", None)
201 if exit:
202 callable = handler.__enter__()
203 no_exc = True
204 try:
205 try:
206 for k, v in ns_confs.get(ns, {}).iteritems():
207 callable(k, v)
208 except:
209
210 no_exc = False
211 if exit is None:
212 raise
213 if not exit(*sys.exc_info()):
214 raise
215
216 finally:
217
218 if no_exc and exit:
219 exit(None, None, None)
220 else:
221 for k, v in ns_confs.get(ns, {}).iteritems():
222 handler(k, v)
223
225 return "%s.%s(%s)" % (self.__module__, self.__class__.__name__,
226 dict.__repr__(self))
227
229 newobj = self.__class__()
230 newobj.update(self)
231 return newobj
232 copy = __copy__
233
234
236 """The 'global' configuration data for the entire CherryPy process."""
237
238 defaults = {
239 'tools.log_tracebacks.on': True,
240 'tools.log_headers.on': True,
241 'tools.trailing_slash.on': True,
242 }
243
244 namespaces = NamespaceSet(
245 **{"server": lambda k, v: setattr(cherrypy.server, k, v),
246 "log": lambda k, v: setattr(cherrypy.log, k, v),
247 "checker": lambda k, v: setattr(cherrypy.checker, k, v),
248 })
249
252
257
287
291
292
294 """Backward compatibility handler for the "engine" namespace."""
295 engine = cherrypy.engine
296 if k == 'autoreload_on':
297 if v:
298 engine.autoreload.subscribe()
299 else:
300 engine.autoreload.unsubscribe()
301 elif k == 'autoreload_frequency':
302 engine.autoreload.frequency = v
303 elif k == 'autoreload_match':
304 engine.autoreload.match = v
305 elif k == 'reload_files':
306 engine.autoreload.files = set(v)
307 elif k == 'deadlock_poll_freq':
308 engine.timeout_monitor.frequency = v
309 elif k == 'SIGHUP':
310 engine.listeners['SIGHUP'] = set([v])
311 elif k == 'SIGTERM':
312 engine.listeners['SIGTERM'] = set([v])
313 elif "." in k:
314 plugin, attrname = k.split(".", 1)
315 plugin = getattr(engine, plugin)
316 if attrname == 'on':
317 if v and callable(getattr(plugin, 'subscribe', None)):
318 plugin.subscribe()
319 return
320 elif (not v) and callable(getattr(plugin, 'unsubscribe', None)):
321 plugin.unsubscribe()
322 return
323 setattr(plugin, attrname, v)
324 else:
325 setattr(engine, k, v)
326 Config.namespaces["engine"] = _engine_namespace_handler
327
328
333 Config.namespaces["tree"] = _tree_namespace_handler
334
335
336 -class _Parser(ConfigParser.ConfigParser):
337 """Sub-class of ConfigParser that keeps the case of options and that raises
338 an exception if the file cannot be read.
339 """
340
343
344 - def read(self, filenames):
345 if isinstance(filenames, basestring):
346 filenames = [filenames]
347 for filename in filenames:
348
349
350
351
352 fp = open(filename)
353 try:
354 self._read(fp, filename)
355 finally:
356 fp.close()
357
358 - def as_dict(self, raw=False, vars=None):
359 """Convert an INI file to a dictionary"""
360
361 from cherrypy.lib import unrepr
362 result = {}
363 for section in self.sections():
364 if section not in result:
365 result[section] = {}
366 for option in self.options(section):
367 value = self.get(section, option, raw, vars)
368 try:
369 value = unrepr(value)
370 except Exception, x:
371 msg = ("Config error in section: %r, option: %r, "
372 "value: %r. Config values must be valid Python." %
373 (section, option, value))
374 raise ValueError(msg, x.__class__.__name__, x.args)
375 result[section][option] = value
376 return result
377
379 if hasattr(file, 'read'):
380 self.readfp(file)
381 else:
382 self.read(file)
383 return self.as_dict()
384
385 del ConfigParser
386