Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

root/trunk/cherrypy/_cpconfig.py

Revision 1930 (checked in by fumanchu, 2 months ago)

Working cherryd daemon script. New FlupFCGIServer wrapper in servers.py. Also added a config arg to cherrypy.Application.

  • Property svn:eol-style set to native
Line 
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         # For use with CherryPy embedded in another deployment stack.
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
139 def as_dict(config):
140     """Return a dict from 'config' whether it is a dict, file, or filename."""
141     if isinstance(config, basestring):
142         config = _Parser().dict_from_file(config)
143     elif hasattr(config, 'read'):
144         config = _Parser().dict_from_file(config)
145     return config
146
147 def merge(base, other):
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     # Load other into base
157     for section, value_map in as_dict(other).iteritems():
158         base.setdefault(section, {}).update(value_map)
159
160
161 class NamespaceSet(dict):
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    
175     def __call__(self, config):
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         # Separate the given config into namespaces
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         # I chose __enter__ and __exit__ so someday this could be
194         # rewritten using Python 2.5's 'with' statement:
195         # for ns, handler in self.iteritems():
196         #     with handler as callable:
197         #         for k, v in ns_confs.get(ns, {}).iteritems():
198         #             callable(k, v)
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                         # The exceptional case is handled here
210                         no_exc = False
211                         if exit is None:
212                             raise
213                         if not exit(*sys.exc_info()):
214                             raise
215                         # The exception is swallowed if exit() returns true
216                 finally:
217                     # The normal and non-local-goto cases are handled here
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    
224     def __repr__(self):
225         return "%s.%s(%s)" % (self.__module__, self.__class__.__name__,
226                               dict.__repr__(self))
227    
228     def __copy__(self):
229         newobj = self.__class__()
230         newobj.update(self)
231         return newobj
232     copy = __copy__
233
234
235 class Config(dict):
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    
250     def __init__(self):
251         self.reset()
252    
253     def reset(self):
254         """Reset self to default values."""
255         self.clear()
256         dict.update(self, self.defaults)
257    
258     def update(self, config):
259         """Update self from a dict, file or filename."""
260         if isinstance(config, basestring):
261             # Filename
262             cherrypy.engine.autoreload.files.add(config)
263             config = _Parser().dict_from_file(config)
264         elif hasattr(config, 'read'):
265             # Open file object
266             config = _Parser().dict_from_file(config)
267         else:
268             config = config.copy()
269        
270         if isinstance(config.get("global", None), dict):
271             if len(config) > 1:
272                 cherrypy.checker.global_config_contained_paths = True
273             config = config["global"]
274        
275         which_env = config.get('environment')
276         if which_env:
277             env = environments[which_env]
278             for k in env:
279                 if k not in config:
280                     config[k] = env[k]
281        
282         if 'tools.staticdir.dir' in config:
283             config['tools.staticdir.section'] = "global"
284        
285         dict.update(self, config)
286         self.namespaces(config)
287    
288     def __setitem__(self, k, v):
289         dict.__setitem__(self, k, v)
290         self.namespaces({k: v})
291
292
293 def _engine_namespace_handler(k, v):
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 = v
307     elif k == 'deadlock_poll_freq':
308         cherrypy.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 Config.namespaces["engine"] = _engine_namespace_handler
314
315
316 def _tree_namespace_handler(k, v):
317     """Namespace handler for the 'tree' config namespace."""
318     cherrypy.tree.graft(v, v.script_name)
319     cherrypy.engine.log("Mounted: %s on %s" % (v, v.script_name or "/"))
320 Config.namespaces["tree"] = _tree_namespace_handler
321
322
323 class _Parser(ConfigParser.ConfigParser):
324     """Sub-class of ConfigParser that keeps the case of options and that raises
325     an exception if the file cannot be read.
326     """
327    
328     def optionxform(self, optionstr):
329         return optionstr
330    
331     def read(self, filenames):
332         if isinstance(filenames, basestring):
333             filenames = [filenames]
334         for filename in filenames:
335             # try:
336             #     fp = open(filename)
337             # except IOError:
338             #     continue
339             fp = open(filename)
340             try:
341                 self._read(fp, filename)
342             finally:
343                 fp.close()
344    
345     def as_dict(self, raw=False, vars=None):
346         """Convert an INI file to a dictionary"""
347         # Load INI file into a dict
348         from cherrypy.lib import unrepr
349         result = {}
350         for section in self.sections():
351             if section not in result:
352                 result[section] = {}
353             for option in self.options(section):
354                 value = self.get(section, option, raw, vars)
355                 try:
356                     value = unrepr(value)
357                 except Exception, x:
358                     msg = ("Config error in section: %r, option: %r, "
359                            "value: %r. Config values must be valid Python." %
360                            (section, option, value))
361                     raise ValueError(msg, x.__class__.__name__, x.args)
362                 result[section][option] = value
363         return result
364    
365     def dict_from_file(self, file):
366         if hasattr(file, 'read'):
367             self.readfp(file)
368         else:
369             self.read(file)
370         return self.as_dict()
371
372 del ConfigParser
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets