Package cherrypy :: Module _cpconfig
[hide private]
[frames] | no frames]

Source Code for Module cherrypy._cpconfig

  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 = 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
329 -def _tree_namespace_handler(k, v):
330 """Namespace handler for the 'tree' config namespace.""" 331 cherrypy.tree.graft(v, v.script_name) 332 cherrypy.engine.log("Mounted: %s on %s" % (v, v.script_name or "/"))
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
341 - def optionxform(self, optionstr):
342 return optionstr
343
344 - def read(self, filenames):
345 if isinstance(filenames, basestring): 346 filenames = [filenames] 347 for filename in filenames: 348 # try: 349 # fp = open(filename) 350 # except IOError: 351 # continue 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 # Load INI file into a dict 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
378 - def dict_from_file(self, file):
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