OLD | NEW |
(Empty) | |
| 1 """ |
| 2 Configuration system for CherryPy. |
| 3 |
| 4 Configuration in CherryPy is implemented via dictionaries. Keys are strings |
| 5 which name the mapped value, which may be of any type. |
| 6 |
| 7 |
| 8 Architecture |
| 9 ------------ |
| 10 |
| 11 CherryPy Requests are part of an Application, which runs in a global context, |
| 12 and configuration data may apply to any of those three scopes: |
| 13 |
| 14 Global |
| 15 Configuration entries which apply everywhere are stored in |
| 16 cherrypy.config. |
| 17 |
| 18 Application |
| 19 Entries which apply to each mounted application are stored |
| 20 on the Application object itself, as 'app.config'. This is a two-level |
| 21 dict where each key is a path, or "relative URL" (for example, "/" or |
| 22 "/path/to/my/page"), and each value is a config dict. Usually, this |
| 23 data is provided in the call to tree.mount(root(), config=conf), |
| 24 although you may also use app.merge(conf). |
| 25 |
| 26 Request |
| 27 Each Request object possesses a single 'Request.config' dict. |
| 28 Early in the request process, this dict is populated by merging global |
| 29 config entries, Application entries (whose path equals or is a parent |
| 30 of Request.path_info), and any config acquired while looking up the |
| 31 page handler (see next). |
| 32 |
| 33 |
| 34 Declaration |
| 35 ----------- |
| 36 |
| 37 Configuration data may be supplied as a Python dictionary, as a filename, |
| 38 or as an open file object. When you supply a filename or file, CherryPy |
| 39 uses Python's builtin ConfigParser; you declare Application config by |
| 40 writing each path as a section header:: |
| 41 |
| 42 [/path/to/my/page] |
| 43 request.stream = True |
| 44 |
| 45 To declare global configuration entries, place them in a [global] section. |
| 46 |
| 47 You may also declare config entries directly on the classes and methods |
| 48 (page handlers) that make up your CherryPy application via the ``_cp_config`` |
| 49 attribute. For example:: |
| 50 |
| 51 class Demo: |
| 52 _cp_config = {'tools.gzip.on': True} |
| 53 |
| 54 def index(self): |
| 55 return "Hello world" |
| 56 index.exposed = True |
| 57 index._cp_config = {'request.show_tracebacks': False} |
| 58 |
| 59 .. note:: |
| 60 |
| 61 This behavior is only guaranteed for the default dispatcher. |
| 62 Other dispatchers may have different restrictions on where |
| 63 you can attach _cp_config attributes. |
| 64 |
| 65 |
| 66 Namespaces |
| 67 ---------- |
| 68 |
| 69 Configuration keys are separated into namespaces by the first "." in the key. |
| 70 Current namespaces: |
| 71 |
| 72 engine |
| 73 Controls the 'application engine', including autoreload. |
| 74 These can only be declared in the global config. |
| 75 |
| 76 tree |
| 77 Grafts cherrypy.Application objects onto cherrypy.tree. |
| 78 These can only be declared in the global config. |
| 79 |
| 80 hooks |
| 81 Declares additional request-processing functions. |
| 82 |
| 83 log |
| 84 Configures the logging for each application. |
| 85 These can only be declared in the global or / config. |
| 86 |
| 87 request |
| 88 Adds attributes to each Request. |
| 89 |
| 90 response |
| 91 Adds attributes to each Response. |
| 92 |
| 93 server |
| 94 Controls the default HTTP server via cherrypy.server. |
| 95 These can only be declared in the global config. |
| 96 |
| 97 tools |
| 98 Runs and configures additional request-processing packages. |
| 99 |
| 100 wsgi |
| 101 Adds WSGI middleware to an Application's "pipeline". |
| 102 These can only be declared in the app's root config ("/"). |
| 103 |
| 104 checker |
| 105 Controls the 'checker', which looks for common errors in |
| 106 app state (including config) when the engine starts. |
| 107 Global config only. |
| 108 |
| 109 The only key that does not exist in a namespace is the "environment" entry. |
| 110 This special entry 'imports' other config entries from a template stored in |
| 111 cherrypy._cpconfig.environments[environment]. It only applies to the global |
| 112 config, and only when you use cherrypy.config.update. |
| 113 |
| 114 You can define your own namespaces to be called at the Global, Application, |
| 115 or Request level, by adding a named handler to cherrypy.config.namespaces, |
| 116 app.namespaces, or app.request_class.namespaces. The name can |
| 117 be any string, and the handler must be either a callable or a (Python 2.5 |
| 118 style) context manager. |
| 119 """ |
| 120 |
| 121 import cherrypy |
| 122 from cherrypy._cpcompat import set, basestring |
| 123 from cherrypy.lib import reprconf |
| 124 |
| 125 # Deprecated in CherryPy 3.2--remove in 3.3 |
| 126 NamespaceSet = reprconf.NamespaceSet |
| 127 |
| 128 def merge(base, other): |
| 129 """Merge one app config (from a dict, file, or filename) into another. |
| 130 |
| 131 If the given config is a filename, it will be appended to |
| 132 the list of files to monitor for "autoreload" changes. |
| 133 """ |
| 134 if isinstance(other, basestring): |
| 135 cherrypy.engine.autoreload.files.add(other) |
| 136 |
| 137 # Load other into base |
| 138 for section, value_map in reprconf.as_dict(other).items(): |
| 139 if not isinstance(value_map, dict): |
| 140 raise ValueError( |
| 141 "Application config must include section headers, but the " |
| 142 "config you tried to merge doesn't have any sections. " |
| 143 "Wrap your config in another dict with paths as section " |
| 144 "headers, for example: {'/': config}.") |
| 145 base.setdefault(section, {}).update(value_map) |
| 146 |
| 147 |
| 148 class Config(reprconf.Config): |
| 149 """The 'global' configuration data for the entire CherryPy process.""" |
| 150 |
| 151 def update(self, config): |
| 152 """Update self from a dict, file or filename.""" |
| 153 if isinstance(config, basestring): |
| 154 # Filename |
| 155 cherrypy.engine.autoreload.files.add(config) |
| 156 reprconf.Config.update(self, config) |
| 157 |
| 158 def _apply(self, config): |
| 159 """Update self from a dict.""" |
| 160 if isinstance(config.get("global", None), dict): |
| 161 if len(config) > 1: |
| 162 cherrypy.checker.global_config_contained_paths = True |
| 163 config = config["global"] |
| 164 if 'tools.staticdir.dir' in config: |
| 165 config['tools.staticdir.section'] = "global" |
| 166 reprconf.Config._apply(self, config) |
| 167 |
| 168 def __call__(self, *args, **kwargs): |
| 169 """Decorator for page handlers to set _cp_config.""" |
| 170 if args: |
| 171 raise TypeError( |
| 172 "The cherrypy.config decorator does not accept positional " |
| 173 "arguments; you must use keyword arguments.") |
| 174 def tool_decorator(f): |
| 175 if not hasattr(f, "_cp_config"): |
| 176 f._cp_config = {} |
| 177 for k, v in kwargs.items(): |
| 178 f._cp_config[k] = v |
| 179 return f |
| 180 return tool_decorator |
| 181 |
| 182 |
| 183 Config.environments = environments = { |
| 184 "staging": { |
| 185 'engine.autoreload_on': False, |
| 186 'checker.on': False, |
| 187 'tools.log_headers.on': False, |
| 188 'request.show_tracebacks': False, |
| 189 'request.show_mismatched_params': False, |
| 190 }, |
| 191 "production": { |
| 192 'engine.autoreload_on': False, |
| 193 'checker.on': False, |
| 194 'tools.log_headers.on': False, |
| 195 'request.show_tracebacks': False, |
| 196 'request.show_mismatched_params': False, |
| 197 'log.screen': False, |
| 198 }, |
| 199 "embedded": { |
| 200 # For use with CherryPy embedded in another deployment stack. |
| 201 'engine.autoreload_on': False, |
| 202 'checker.on': False, |
| 203 'tools.log_headers.on': False, |
| 204 'request.show_tracebacks': False, |
| 205 'request.show_mismatched_params': False, |
| 206 'log.screen': False, |
| 207 'engine.SIGHUP': None, |
| 208 'engine.SIGTERM': None, |
| 209 }, |
| 210 "test_suite": { |
| 211 'engine.autoreload_on': False, |
| 212 'checker.on': False, |
| 213 'tools.log_headers.on': False, |
| 214 'request.show_tracebacks': True, |
| 215 'request.show_mismatched_params': True, |
| 216 'log.screen': False, |
| 217 }, |
| 218 } |
| 219 |
| 220 |
| 221 def _server_namespace_handler(k, v): |
| 222 """Config handler for the "server" namespace.""" |
| 223 atoms = k.split(".", 1) |
| 224 if len(atoms) > 1: |
| 225 # Special-case config keys of the form 'server.servername.socket_port' |
| 226 # to configure additional HTTP servers. |
| 227 if not hasattr(cherrypy, "servers"): |
| 228 cherrypy.servers = {} |
| 229 |
| 230 servername, k = atoms |
| 231 if servername not in cherrypy.servers: |
| 232 from cherrypy import _cpserver |
| 233 cherrypy.servers[servername] = _cpserver.Server() |
| 234 # On by default, but 'on = False' can unsubscribe it (see below). |
| 235 cherrypy.servers[servername].subscribe() |
| 236 |
| 237 if k == 'on': |
| 238 if v: |
| 239 cherrypy.servers[servername].subscribe() |
| 240 else: |
| 241 cherrypy.servers[servername].unsubscribe() |
| 242 else: |
| 243 setattr(cherrypy.servers[servername], k, v) |
| 244 else: |
| 245 setattr(cherrypy.server, k, v) |
| 246 Config.namespaces["server"] = _server_namespace_handler |
| 247 |
| 248 def _engine_namespace_handler(k, v): |
| 249 """Backward compatibility handler for the "engine" namespace.""" |
| 250 engine = cherrypy.engine |
| 251 if k == 'autoreload_on': |
| 252 if v: |
| 253 engine.autoreload.subscribe() |
| 254 else: |
| 255 engine.autoreload.unsubscribe() |
| 256 elif k == 'autoreload_frequency': |
| 257 engine.autoreload.frequency = v |
| 258 elif k == 'autoreload_match': |
| 259 engine.autoreload.match = v |
| 260 elif k == 'reload_files': |
| 261 engine.autoreload.files = set(v) |
| 262 elif k == 'deadlock_poll_freq': |
| 263 engine.timeout_monitor.frequency = v |
| 264 elif k == 'SIGHUP': |
| 265 engine.listeners['SIGHUP'] = set([v]) |
| 266 elif k == 'SIGTERM': |
| 267 engine.listeners['SIGTERM'] = set([v]) |
| 268 elif "." in k: |
| 269 plugin, attrname = k.split(".", 1) |
| 270 plugin = getattr(engine, plugin) |
| 271 if attrname == 'on': |
| 272 if v and hasattr(getattr(plugin, 'subscribe', None), '__call__'): |
| 273 plugin.subscribe() |
| 274 return |
| 275 elif (not v) and hasattr(getattr(plugin, 'unsubscribe', None), '__ca
ll__'): |
| 276 plugin.unsubscribe() |
| 277 return |
| 278 setattr(plugin, attrname, v) |
| 279 else: |
| 280 setattr(engine, k, v) |
| 281 Config.namespaces["engine"] = _engine_namespace_handler |
| 282 |
| 283 |
| 284 def _tree_namespace_handler(k, v): |
| 285 """Namespace handler for the 'tree' config namespace.""" |
| 286 if isinstance(v, dict): |
| 287 for script_name, app in v.items(): |
| 288 cherrypy.tree.graft(app, script_name) |
| 289 cherrypy.engine.log("Mounted: %s on %s" % (app, script_name or "/")) |
| 290 else: |
| 291 cherrypy.tree.graft(v, v.script_name) |
| 292 cherrypy.engine.log("Mounted: %s on %s" % (v, v.script_name or "/")) |
| 293 Config.namespaces["tree"] = _tree_namespace_handler |
| 294 |
| 295 |
OLD | NEW |