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

root/tags/cherrypy-3.0.0/cherrypy/_cpconfig.py

Revision 1558 (checked in by dowski, 2 years ago)

Fix for #627.

  • 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 cherrypy.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
56 Namespaces
57 ----------
58
59 Configuration keys are separated into namespaces by the first "." in the key.
60 Current namespaces:
61
62     engine:     Controls the 'application engine', including autoreload.
63                 These can only be declared in the global config.
64     hooks:      Declares additional request-processing functions.
65     log:        Configures the logging for each application.
66                 These can only be declared in the global or / config.
67     request:    Adds attributes to each Request.
68     response:   Adds attributes to each Response.
69     server:     Controls the default HTTP server via cherrypy.server.
70                 These can only be declared in the global config.
71     tools:      Runs and configures additional request-processing packages.
72
73 The only key that does not exist in a namespace is the "environment" entry.
74 This special entry 'imports' other config entries from a template stored in
75 cherrypy._cpconfig.environments[environment]. It only applies to the global
76 config, and only when you use cherrypy.config.update.
77
78 You can define your own namespaces to be called at the Global, Application,
79 or Request level, by adding a named handler to cherrypy.config.namespaces,
80 app.namespaces, or cherrypy.engine.request_class.namespaces. The name can
81 be any string, and the handler must be either a callable or a context
82 manager.
83 """
84
85 import ConfigParser
86 import sys
87 from warnings import warn
88
89 import cherrypy
90
91
92 environments = {
93     "staging": {
94         'engine.autoreload_on': False,
95         'engine.checker': None,
96         'tools.log_headers.on': False,
97         'request.show_tracebacks': False,
98         },
99     "production": {
100         'engine.autoreload_on': False,
101         'engine.checker': None,
102         'tools.log_headers.on': False,
103         'request.show_tracebacks': False,
104         'log.screen': False,
105         },
106     "test_suite": {
107         'engine.autoreload_on': False,
108         'engine.checker': None,
109         'tools.log_headers.on': False,
110         'request.show_tracebacks': True,
111         'log.screen': False,
112         },
113     }
114
115 def as_dict(config):
116     """Return a dict from 'config' whether it is a dict, file, or filename."""
117     if isinstance(config, basestring):
118         config = _Parser().dict_from_file(config)
119     elif hasattr(config, 'read'):
120         config = _Parser().dict_from_file(config)
121     return config
122
123 def merge(base, other):
124     """Merge one app config (from a dict, file, or filename) into another.
125     
126     If the given config is a filename, it will be appended to
127     cherrypy.engine.reload_files and monitored for changes.
128     """
129     if isinstance(other, basestring):
130         if other not in cherrypy.engine.reload_files:
131             cherrypy.engine.reload_files.append(other)
132    
133     # Load other into base
134     for section, value_map in as_dict(other).iteritems():
135         base.setdefault(section, {}).update(value_map)
136
137
138 def _call_namespaces(config, namespaces):
139     """Iterate through config and pass it to each namespace.
140     
141     'config' should be a flat dict, where keys use dots to separate
142     namespaces, and values are arbitrary.
143     'namespaces' should be a dict whose keys are strings and whose
144     values are namespace handlers.
145     
146     The first name in each config key is used to look up the corresponding
147     namespace handler. For example, a config entry of {'tools.gzip.on': v}
148     will call the 'tools' namespace handler with the args: ('gzip.on', v)
149     
150     Each handler may be a bare callable, or it may be a context manager
151     with __enter__ and __exit__ methods, in which case the __enter__
152     method should return the callable.
153     """
154     # Separate the given config into namespaces
155     ns_confs = {}
156     for k in config:
157         if "." in k:
158             ns, name = k.split(".", 1)
159             bucket = ns_confs.setdefault(ns, {})
160             bucket[name] = config[k]
161    
162     # I chose __enter__ and __exit__ so someday this could be
163     # rewritten using Python 2.5's 'with' statement:
164     # for ns, handler in namespaces.iteritems():
165     #     with handler as callable:
166     #         for k, v in ns_confs.get(ns, {}).iteritems():
167     #             callable(k, v)
168     for ns, handler in namespaces.iteritems():
169         exit = getattr(handler, "__exit__", None)
170         if exit:
171             callable = handler.__enter__()
172             no_exc = True
173             try:
174                 try:
175                     for k, v in ns_confs.get(ns, {}).iteritems():
176                         callable(k, v)
177                 except:
178                     # The exceptional case is handled here
179                     no_exc = False
180                     if exit is None:
181                         raise
182                     if not exit(*sys.exc_info()):
183                         raise
184                     # The exception is swallowed if exit() returns true
185             finally:
186                 # The normal and non-local-goto cases are handled here
187                 if no_exc and exit:
188                     exit(None, None, None)
189         else:
190             for k, v in ns_confs.get(ns, {}).iteritems():
191                 handler(k, v)
192
193
194 class Config(dict):
195     """The 'global' configuration data for the entire CherryPy process."""
196    
197     defaults = {
198         'tools.log_tracebacks.on': True,
199         'tools.log_headers.on': True,
200         'tools.trailing_slash.on': True,
201         }
202    
203     namespaces = {"server": lambda k, v: setattr(cherrypy.server, k, v),
204                   "engine": lambda k, v: setattr(cherrypy.engine, k, v),
205                   "log": lambda k, v: setattr(cherrypy.log, k, v),
206                   }
207    
208     def __init__(self):
209         self.reset()
210    
211     def reset(self):
212         """Reset self to default values."""
213         self.clear()
214         dict.update(self, self.defaults)
215    
216     def update(self, config):
217         """Update self from a dict, file or filename."""
218         if isinstance(config, basestring):
219             # Filename
220             if config not in cherrypy.engine.reload_files:
221                 cherrypy.engine.reload_files.append(config)
222             config = _Parser().dict_from_file(config)
223         elif hasattr(config, 'read'):
224             # Open file object
225             config = _Parser().dict_from_file(config)
226         else:
227             config = config.copy()
228        
229         if isinstance(config.get("global", None), dict):
230             if len(config) > 1:
231                 cherrypy.engine.checker.global_config_contained_paths = True
232             config = config["global"]
233        
234         if 'environment' in config:
235             env = environments[config['environment']]
236             for k in env:
237                 if k not in config:
238                     config[k] = env[k]
239        
240         if 'tools.staticdir.dir' in config:
241             config['tools.staticdir.section'] = "global"
242        
243         dict.update(self, config)
244         _call_namespaces(config, self.namespaces)
245    
246     def __setitem__(self, k, v):
247         dict.__setitem__(self, k, v)
248         _call_namespaces({k: v}, self.namespaces)
249
250
251
252 class _Parser(ConfigParser.ConfigParser):
253     """Sub-class of ConfigParser that keeps the case of options and that raises
254     an exception if the file cannot be read.
255     """
256    
257     def optionxform(self, optionstr):
258         return optionstr
259    
260     def read(self, filenames):
261         if isinstance(filenames, basestring):
262             filenames = [filenames]
263         for filename in filenames:
264             # try:
265             #     fp = open(filename)
266             # except IOError:
267             #     continue
268             fp = open(filename)
269             try:
270                 self._read(fp, filename)
271             finally:
272                 fp.close()
273    
274     def as_dict(self, raw=False, vars=None):
275         """Convert an INI file to a dictionary"""
276         # Load INI file into a dict
277         from cherrypy.lib import unrepr
278         result = {}
279         for section in self.sections():
280             if section not in result:
281                 result[section] = {}
282             for option in self.options(section):
283                 value = self.get(section, option, raw, vars)
284                 try:
285                     value = unrepr(value)
286                 except Exception, x:
287                     msg = ("Config error in section: %s, option: %s, value: %s" %
288                            (repr(section), repr(option), repr(value)))
289                     raise ValueError(msg, x.__class__.__name__, x.args)
290                 result[section][option] = value
291         return result
292    
293     def dict_from_file(self, file):
294         if hasattr(file, 'read'):
295             self.readfp(file)
296         else:
297             self.read(file)
298         return self.as_dict()
299
300 del ConfigParser
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets