Changeset 1420
- Timestamp:
- 10/28/06 21:45:36
- Files:
-
- trunk/cherrypy/__init__.py (modified) (3 diffs)
- trunk/cherrypy/_cpconfig.py (modified) (3 diffs)
- trunk/cherrypy/_cprequest.py (modified) (5 diffs)
- trunk/cherrypy/_cptools.py (modified) (9 diffs)
- trunk/cherrypy/_cptree.py (modified) (1 diff)
- trunk/cherrypy/test/test_tools.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/cherrypy/__init__.py
r1409 r1420 9 9 from cherrypy._cperror import HTTPError, HTTPRedirect, InternalRedirect, NotFound, CherryPyException 10 10 from cherrypy._cperror import TimeoutError 11 12 from cherrypy import _cprequest 13 from cherrypy import _cpengine 14 engine = _cpengine.Engine() 11 15 12 16 from cherrypy import _cptools … … 18 22 from cherrypy._cptree import Application 19 23 from cherrypy import _cpwsgi as wsgi 20 from cherrypy import _cpengine21 engine = _cpengine.Engine()22 24 from cherrypy import _cpserver 23 25 server = _cpserver.Server() … … 90 92 # throughout the entire life of the webserver, but will redirect 91 93 # to the "_serving" object) 92 from cherrypy import _cprequest93 94 from cherrypy.lib import http as _http 94 95 request = _ThreadLocalProxy('request', trunk/cherrypy/_cpconfig.py
r1373 r1420 75 75 cherrypy._cpconfig.environments[environment]. It only applies to the global 76 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. 77 83 """ 78 84 … … 126 132 127 133 134 def _call_namespaces(config, namespaces): 135 """Iterate through config and pass it to each namespace. 136 137 'config' should be a flat dict, where keys use dots to separate 138 namespaces, and values are arbitrary. 139 'namespaces' should be a dict whose keys are strings and whose 140 values are namespace handlers. 141 142 The first name in each config key is used to look up the corresponding 143 namespace handler. For example, a config entry of {'tools.gzip.on': v} 144 will call the 'tools' namespace handler with the args: ('gzip.on', v) 145 146 Each handler may be a bare callable, or it may be a context manager 147 with __enter__ and __exit__ methods, in which case the __enter__ 148 method should return the callable. 149 """ 150 # Separate the given config into namespaces 151 ns_confs = {} 152 for k in config: 153 if "." in k: 154 ns, name = k.split(".", 1) 155 bucket = ns_confs.setdefault(ns, {}) 156 bucket[name] = config[k] 157 158 # I chose __enter__ and __exit__ so someday this could be 159 # rewritten using Python 2.5's 'with' statement: 160 # for ns, handler in namespaces.iteritems(): 161 # with handler as callable: 162 # for k, v in ns_confs.get(ns, {}).iteritems(): 163 # callable(k, v) 164 for ns, handler in namespaces.iteritems(): 165 exit = getattr(handler, "__exit__", None) 166 if exit: 167 callable = handler.__enter__() 168 no_exc = True 169 try: 170 try: 171 for k, v in ns_confs.get(ns, {}).iteritems(): 172 callable(k, v) 173 except: 174 # The exceptional case is handled here 175 no_exc = False 176 if exit is None: 177 raise 178 if not exit(*sys.exc_info()): 179 raise 180 # The exception is swallowed if exit() returns true 181 finally: 182 # The normal and non-local-goto cases are handled here 183 if no_exc and exit: 184 exit(None, None, None) 185 else: 186 for k, v in ns_confs.get(ns, {}).iteritems(): 187 handler(k, v) 188 189 128 190 class Config(dict): 129 191 """The 'global' configuration data for the entire CherryPy process.""" … … 173 235 config['tools.staticdir.section'] = "global" 174 236 175 # Must use this idiom in order to hit our custom __setitem__. 176 for k, v in config.iteritems(): 177 self[k] = v 237 dict.update(self, config) 238 _call_namespaces(config, self.namespaces) 178 239 179 240 def __setitem__(self, k, v): 180 241 dict.__setitem__(self, k, v) 181 182 # Override object properties if specified in config. 183 atoms = k.split(".", 1) 184 namespace = atoms[0] 185 if namespace in self.namespaces: 186 self.namespaces[namespace](atoms[1], v) 242 _call_namespaces({k: v}, self.namespaces) 187 243 188 244 trunk/cherrypy/_cprequest.py
r1415 r1420 101 101 102 102 # Config namespace handlers 103 def tools_namespace(k, v):104 """Attach tools specified in config."""105 toolname, arg = k.split(".", 1)106 bucket = cherrypy.request.toolmap.setdefault(toolname, {})107 bucket[arg] = v108 103 109 104 def hooks_namespace(k, v): … … 172 167 app = None 173 168 handler = None 174 toolmap = {}169 toolmaps = {} 175 170 config = None 176 171 recursive_redirect = False … … 184 179 throw_errors = False 185 180 186 namespaces = {"tools": tools_namespace, 187 "hooks": hooks_namespace, 181 namespaces = {"hooks": hooks_namespace, 188 182 "request": request_namespace, 189 183 "response": response_namespace, … … 351 345 self.hooks = self.__class__.hooks.copy() 352 346 self.get_resource(path_info) 353 self.configure()347 cherrypy._cpconfig._call_namespaces(self.config, self.namespaces) 354 348 355 349 self.hooks.run('on_start_resource') … … 455 449 dispatch(path) 456 450 457 def configure(self):458 """Process self.config, populate self.toolmap and set up each tool."""459 self.toolmap = tm = {}460 461 # Process config namespaces (including tools.*)462 reqconf = self.config463 for k in reqconf:464 atoms = k.split(".", 1)465 namespace = atoms[0]466 if namespace in self.namespaces:467 self.namespaces[namespace](atoms[1], reqconf[k])468 469 # Run tool._setup(conf) for each tool in the new toolmap.470 tools = cherrypy.tools471 for toolname in tm:472 if tm[toolname].get("on", False):473 tool = getattr(tools, toolname)474 tool._setup()475 476 451 def process_body(self): 477 452 """Convert request.rfile into request.params (or request.body).""" trunk/cherrypy/_cptools.py
r1415 r1420 33 33 help(tool.callable) should give you more information about this Tool. 34 34 """ 35 36 namespace = "tools" 35 37 36 38 def __init__(self, point, callable, name=None, priority=50): … … 52 54 53 55 def _merged_args(self, d=None): 54 tm = cherrypy.request.toolmap 56 tm = cherrypy.request.toolmaps[self.namespace] 55 57 if self._name in tm: 56 58 conf = tm[self._name].copy() … … 77 79 "arguments; you must use keyword arguments." 78 80 % self._name) 79 def wrapper(f):81 def tool_decorator(f): 80 82 if not hasattr(f, "_cp_config"): 81 83 f._cp_config = {} 82 f._cp_config["tools." + self._name + ".on"] = True 84 subspace = self.namespace + "." + self._name + "." 85 f._cp_config[subspace + "on"] = True 83 86 for k, v in kwargs.iteritems(): 84 f._cp_config[ "tools." + self._name + "."+ k] = v87 f._cp_config[subspace + k] = v 85 88 return f 86 return wrapper89 return tool_decorator 87 90 88 91 def _setup(self): … … 116 119 root=absDir) 117 120 """ 118 def wrapper(*a, **kw):121 def handle_func(*a, **kw): 119 122 handled = self.callable(*args, **self._merged_args(kwargs)) 120 123 if not handled: 121 124 raise cherrypy.NotFound() 122 125 return cherrypy.response.body 123 wrapper.exposed = True124 return wrapper126 handle_func.exposed = True 127 return handle_func 125 128 126 129 def _wrapper(self, **kwargs): … … 181 184 class XMLRPCController(object): 182 185 186 # Note we're hard-coding this into the 'tools' namespace. We could do 187 # a huge amount of work to make it relocatable, but the only reason why 188 # would be if someone actually disabled the default_toolbox. Meh. 183 189 _cp_config = {'tools.xmlrpc.on': True} 184 190 … … 192 198 if subhandler and getattr(subhandler, "exposed", False): 193 199 body = subhandler(*(vpath + rpcparams), **params) 194 200 195 201 else: 196 202 # http://www.cherrypy.org/ticket/533 … … 199 205 # cherrypy.lib.xmlrpc.on_error 200 206 raise Exception, 'method "%s" is not supported' % attr 201 202 conf = cherrypy.request.toolmap .get("xmlrpc", {})207 208 conf = cherrypy.request.toolmaps['tools'].get("xmlrpc", {}) 203 209 _xmlrpc.respond(body, 204 210 conf.get('encoding', 'utf-8'), … … 284 290 285 291 class Toolbox(object): 286 """A collection of Tools.""" 292 """A collection of Tools. 293 294 This object also functions as a config namespace handler for itself. 295 """ 296 297 def __init__(self, namespace): 298 self.namespace = namespace 299 cherrypy.engine.request_class.namespaces[namespace] = self 287 300 288 301 def __setattr__(self, name, value): … … 291 304 if value._name is None: 292 305 value._name = name 306 value.namespace = self.namespace 293 307 object.__setattr__(self, name, value) 294 295 296 default_toolbox = _d = Toolbox() 308 309 def __enter__(self): 310 cherrypy.request.toolmaps[self.namespace] = {} 311 return self 312 313 def __call__(self, k, v): 314 """Populate request.toolmaps from tools specified in config.""" 315 toolname, arg = k.split(".", 1) 316 map = cherrypy.request.toolmaps[self.namespace] 317 bucket = map.setdefault(toolname, {}) 318 bucket[arg] = v 319 320 def __exit__(self, exc_type, exc_val, exc_tb): 321 """Run tool._setup() for each tool in our toolmap.""" 322 map = cherrypy.request.toolmaps.get(self.namespace) 323 if map: 324 for name, settings in map.iteritems(): 325 if settings.get("on", False): 326 tool = getattr(self, name) 327 tool._setup() 328 329 330 default_toolbox = _d = Toolbox("tools") 297 331 default_toolbox.session_auth = SessionAuthTool(cptools.session_auth) 298 332 _d.proxy = Tool('before_request_body', cptools.proxy, priority=30) trunk/cherrypy/_cptree.py
r1385 r1420 48 48 49 49 # Handle namespaces specified in config. 50 rootconf = self.config.get("/", {}) 51 for k, v in rootconf.iteritems(): 52 atoms = k.split(".", 1) 53 namespace = atoms[0] 54 if namespace in self.namespaces: 55 self.namespaces[namespace](atoms[1], v) 50 _cpconfig._call_namespaces(self.config.get("/", {}), self.namespaces) 56 51 57 52 def wsgiapp(self, environ, start_response): trunk/cherrypy/test/test_tools.py
r1414 r1420 15 15 def setup_server(): 16 16 17 # Put check_access in a custom toolbox with its own namespace 18 myauthtools = cherrypy._cptools.Toolbox("myauth") 19 17 20 def check_access(): 18 21 if not getattr(cherrypy.request, "login", None): 19 22 raise cherrypy.HTTPError(401) 20 tools.check_access = cherrypy.Tool('before_request_body', check_access)23 myauthtools.check_access = cherrypy.Tool('before_request_body', check_access) 21 24 22 25 def numerify(): … … 151 154 def restricted(self): 152 155 return "Welcome!" 153 restricted = tools.check_access()(restricted)156 restricted = myauthtools.check_access()(restricted) 154 157 155 158 def err_in_onstart(self):

