Home | Trees | Indices | Help |
|
---|
|
1 """CherryPy tools. A "tool" is any helper, adapted to CP. 2 3 Tools are usually designed to be used in a variety of ways (although some 4 may only offer one if they choose): 5 6 Library calls: 7 All tools are callables that can be used wherever needed. 8 The arguments are straightforward and should be detailed within the 9 docstring. 10 11 Function decorators: 12 All tools, when called, may be used as decorators which configure 13 individual CherryPy page handlers (methods on the CherryPy tree). 14 That is, "@tools.anytool()" should "turn on" the tool via the 15 decorated function's _cp_config attribute. 16 17 CherryPy config: 18 If a tool exposes a "_setup" callable, it will be called 19 once per Request (if the feature is "turned on" via config). 20 21 Tools may be implemented as any object with a namespace. The builtins 22 are generally either modules or instances of the tools.Tool class. 23 """ 24 25 import cherrypy 26 2729 """Return the names of all static arguments to the given function.""" 30 # Use this instead of importing inspect for less mem overhead. 31 import types 32 if isinstance(func, types.MethodType): 33 func = func.im_func 34 co = func.func_code 35 return co.co_varnames[:co.co_argcount]36 3739 """A registered function for use with CherryPy request-processing hooks. 40 41 help(tool.callable) should give you more information about this Tool. 42 """ 43 44 namespace = "tools" 4511247 self._point = point 48 self.callable = callable 49 self._name = name 50 self._priority = priority 51 self.__doc__ = self.callable.__doc__ 52 self._setargs()5355 """Copy func parameter names to obj attributes.""" 56 try: 57 for arg in _getargs(self.callable): 58 setattr(self, arg, None) 59 except (TypeError, AttributeError): 60 if hasattr(self.callable, "__call__"): 61 for arg in _getargs(self.callable.__call__): 62 setattr(self, arg, None) 63 # IronPython 1.0 raises NotImplementedError because 64 # inspect.getargspec tries to access Python bytecode 65 # in co_code attribute. 66 except NotImplementedError: 67 pass 68 # IronPython 1B1 may raise IndexError in some cases, 69 # but if we trap it here it doesn't prevent CP from working. 70 except IndexError: 71 pass7274 """Return a dict of configuration entries for this Tool.""" 75 if d: 76 conf = d.copy() 77 else: 78 conf = {} 79 80 tm = cherrypy.request.toolmaps[self.namespace] 81 if self._name in tm: 82 conf.update(tm[self._name]) 83 84 if "on" in conf: 85 del conf["on"] 86 87 return conf8890 """Compile-time decorator (turn on the tool in config). 91 92 For example: 93 94 @tools.proxy() 95 def whats_my_base(self): 96 return cherrypy.request.base 97 whats_my_base.exposed = True 98 """ 99 if args: 100 raise TypeError("The %r Tool does not accept positional " 101 "arguments; you must use keyword arguments." 102 % self._name) 103 def tool_decorator(f): 104 if not hasattr(f, "_cp_config"): 105 f._cp_config = {} 106 subspace = self.namespace + "." + self._name + "." 107 f._cp_config[subspace + "on"] = True 108 for k, v in kwargs.iteritems(): 109 f._cp_config[subspace + k] = v 110 return f111 return tool_decorator114 """Hook this tool into cherrypy.request. 115 116 The standard CherryPy request object will automatically call this 117 method when the tool is "turned on" in config. 118 """ 119 conf = self._merged_args() 120 p = conf.pop("priority", None) 121 if p is None: 122 p = getattr(self.callable, "priority", self._priority) 123 cherrypy.request.hooks.attach(self._point, self.callable, 124 priority=p, **conf)125 126128 """Tool which is called 'before main', that may skip normal handlers. 129 130 If the tool successfully handles the request (by setting response.body), 131 if should return True. This will cause CherryPy to skip any 'normal' page 132 handler. If the tool did not handle the request, it should return False 133 to tell CherryPy to continue on and call the normal page handler. If the 134 tool is declared AS a page handler (see the 'handler' method), returning 135 False will raise NotFound. 136 """ 137 140156 160142 """Use this tool as a CherryPy page handler. 143 144 For example: 145 class Root: 146 nav = tools.staticdir.handler(section="/nav", dir="nav", 147 root=absDir) 148 """ 149 def handle_func(*a, **kw): 150 handled = self.callable(*args, **self._merged_args(kwargs)) 151 if not handled: 152 raise cherrypy.NotFound() 153 return cherrypy.response.body154 handle_func.exposed = True 155 return handle_func162 """Hook this tool into cherrypy.request. 163 164 The standard CherryPy request object will automatically call this 165 method when the tool is "turned on" in config. 166 """ 167 conf = self._merged_args() 168 p = conf.pop("priority", None) 169 if p is None: 170 p = getattr(self.callable, "priority", self._priority) 171 cherrypy.request.hooks.attach(self._point, self._wrapper, 172 priority=p, **conf)173 174176 """Tool which wraps request.handler in a provided wrapper function. 177 178 The 'newhandler' arg must be a handler wrapper function that takes a 179 'next_handler' argument, plus *args and **kwargs. Like all page handler 180 functions, it must return an iterable for use as cherrypy.response.body. 181 182 For example, to allow your 'inner' page handlers to return dicts 183 which then get interpolated into a template: 184 185 def interpolator(next_handler, *args, **kwargs): 186 filename = cherrypy.request.config.get('template') 187 cherrypy.response.template = env.get_template(filename) 188 response_dict = next_handler(*args, **kwargs) 189 return cherrypy.response.template.render(**response_dict) 190 cherrypy.tools.jinja = HandlerWrapperTool(interpolator) 191 """ 192204 205194 self.newhandler = newhandler 195 self._point = point 196 self._name = name 197 self._priority = priority198200 innerfunc = cherrypy.request.handler 201 def wrap(*args, **kwargs): 202 return self.newhandler(innerfunc, *args, **kwargs)203 cherrypy.request.handler = wrap207 """Tool which is used to replace the default request.error_response.""" 208 211 214222 223 224 # Builtin tools # 225 226 from cherrypy.lib import cptools, encoding, auth, static, tidy 227 from cherrypy.lib import sessions as _sessions, xmlrpc as _xmlrpc 228 from cherrypy.lib import caching as _caching, wsgiapp as _wsgiapp 229 230216 """Hook this tool into cherrypy.request. 217 218 The standard CherryPy request object will automatically call this 219 method when the tool is "turned on" in config. 220 """ 221 cherrypy.request.error_response = self._wrapper232 """Session Tool for CherryPy. 233 234 sessions.locking: 235 When 'implicit' (the default), the session will be locked for you, 236 just before running the page handler. 237 When 'early', the session will be locked before reading the request 238 body. This is off by default for safety reasons; for example, 239 a large upload would block the session, denying an AJAX 240 progress meter (see http://www.cherrypy.org/ticket/630). 241 When 'explicit' (or any other value), you need to call 242 cherrypy.session.acquire_lock() yourself before using 243 session data. 244 """ 245293 294 295 296247 # _sessions.init must be bound after headers are read 248 Tool.__init__(self, 'before_request_body', _sessions.init)249 252254 """Hook this tool into cherrypy.request. 255 256 The standard CherryPy request object will automatically call this 257 method when the tool is "turned on" in config. 258 """ 259 hooks = cherrypy.request.hooks 260 261 conf = self._merged_args() 262 263 p = conf.pop("priority", None) 264 if p is None: 265 p = getattr(self.callable, "priority", self._priority) 266 267 hooks.attach(self._point, self.callable, priority=p, **conf) 268 269 locking = conf.pop('locking', 'implicit') 270 if locking == 'implicit': 271 hooks.attach('before_handler', self._lock_session) 272 elif locking == 'early': 273 # Lock before the request body (but after _sessions.init runs!) 274 hooks.attach('before_request_body', self._lock_session, 275 priority=60) 276 else: 277 # Don't lock 278 pass 279 280 hooks.attach('before_finalize', _sessions.save) 281 hooks.attach('on_end_request', _sessions.close)282284 """Drop the current session and make a new one (with a new id).""" 285 sess = cherrypy.serving.session 286 sess.regenerate() 287 288 # Grab cookie-relevant tool args 289 conf = dict([(k, v) for k, v in self._merged_args().iteritems() 290 if k in ('path', 'path_header', 'name', 'timeout', 291 'domain', 'secure')]) 292 _sessions.set_response_cookie(**conf)298 """A Controller (page handler collection) for XML-RPC. 299 300 To use it, have your controllers subclass this base class (it will 301 turn on the tool for you). 302 303 You can also supply the following optional config entries: 304 305 tools.xmlrpc.encoding: 'utf-8' 306 tools.xmlrpc.allow_none: 0 307 308 XML-RPC is a rather discontinuous layer over HTTP; dispatching to the 309 appropriate handler must first be performed according to the URL, and 310 then a second dispatch step must take place according to the RPC method 311 specified in the request body. It also allows a superfluous "/RPC2" 312 prefix in the URL, supplies its own handler args in the body, and 313 requires a 200 OK "Fault" response instead of 404 when the desired 314 method is not found. 315 316 Therefore, XML-RPC cannot be implemented for CherryPy via a Tool alone. 317 This Controller acts as the dispatch target for the first half (based 318 on the URL); it then reads the RPC method from the request body and 319 does its own second dispatch step based on that method. It also reads 320 body params, and returns a Fault on error. 321 322 The XMLRPCDispatcher strips any /RPC2 prefix; if you aren't using /RPC2 323 in your URL's, you can safely skip turning on the XMLRPCDispatcher. 324 Otherwise, you need to use declare it in config: 325 326 request.dispatch: cherrypy.dispatch.XMLRPCDispatcher() 327 """ 328 329 # Note we're hard-coding this into the 'tools' namespace. We could do 330 # a huge amount of work to make it relocatable, but the only reason why 331 # would be if someone actually disabled the default_toolbox. Meh. 332 _cp_config = {'tools.xmlrpc.on': True} 333357 358335 rpcparams, rpcmethod = _xmlrpc.process_body() 336 337 subhandler = self 338 for attr in str(rpcmethod).split('.'): 339 subhandler = getattr(subhandler, attr, None) 340 341 if subhandler and getattr(subhandler, "exposed", False): 342 body = subhandler(*(vpath + rpcparams), **params) 343 344 else: 345 # http://www.cherrypy.org/ticket/533 346 # if a method is not found, an xmlrpclib.Fault should be returned 347 # raising an exception here will do that; see 348 # cherrypy.lib.xmlrpc.on_error 349 raise Exception, 'method "%s" is not supported' % attr 350 351 conf = cherrypy.request.toolmaps['tools'].get("xmlrpc", {}) 352 _xmlrpc.respond(body, 353 conf.get('encoding', 'utf-8'), 354 conf.get('allow_none', 0)) 355 return cherrypy.response.body356 default.exposed = True360 """A tool for running any WSGI middleware/application within CP. 361 362 Here are the parameters: 363 364 wsgi_app - any wsgi application callable 365 env_update - a dictionary with arbitrary keys and values to be 366 merged with the WSGI environ dictionary. 367 368 Example: 369 370 class Whatever: 371 _cp_config = {'tools.wsgiapp.on': True, 372 'tools.wsgiapp.app': some_app, 373 'tools.wsgiapp.env': app_environ, 374 } 375 """ 376381 382378 # Keep request body intact so the wsgi app can have its way with it. 379 cherrypy.request.process_request_body = False 380 HandlerTool._setup(self)384389 390386 for name in dir(cptools.SessionAuth): 387 if not name.startswith("__"): 388 setattr(self, name, None)392 """Caching Tool for CherryPy.""" 393421 422 423395 request = cherrypy.request 396 397 if not hasattr(cherrypy, "_cache"): 398 # Make a process-wide Cache object. 399 cherrypy._cache = kwargs.pop("cache_class", _caching.MemoryCache)() 400 401 # Take all remaining kwargs and set them on the Cache object. 402 for k, v in kwargs.iteritems(): 403 setattr(cherrypy._cache, k, v) 404 405 if _caching.get(invalid_methods=invalid_methods): 406 request.handler = None 407 else: 408 if request.cacheable: 409 # Note the devious technique here of adding hooks on the fly 410 request.hooks.attach('before_finalize', _caching.tee_output, 411 priority = 90)412 _wrapper.priority = 20 413425 """A collection of Tools. 426 427 This object also functions as a config namespace handler for itself. 428 Custom toolboxes should be added to each Application's toolboxes dict. 429 """ 430 433450 459 460 461 default_toolbox = _d = Toolbox("tools") 462 _d.session_auth = SessionAuthTool(cptools.session_auth) 463 _d.proxy = Tool('before_request_body', cptools.proxy, priority=30) 464 _d.response_headers = Tool('on_start_resource', cptools.response_headers) 465 _d.log_tracebacks = Tool('before_error_response', cptools.log_traceback) 466 _d.log_headers = Tool('before_error_response', cptools.log_request_headers) 467 _d.log_hooks = Tool('on_end_request', cptools.log_hooks, priority=100) 468 _d.err_redirect = ErrorTool(cptools.redirect) 469 _d.etags = Tool('before_finalize', cptools.validate_etags, priority=75) 470 _d.decode = Tool('before_handler', encoding.decode) 471 # the order of encoding, gzip, caching is important 472 _d.encode = Tool('before_finalize', encoding.encode, priority=70) 473 _d.gzip = Tool('before_finalize', encoding.gzip, priority=80) 474 _d.staticdir = HandlerTool(static.staticdir) 475 _d.staticfile = HandlerTool(static.staticfile) 476 _d.sessions = SessionTool() 477 _d.xmlrpc = ErrorTool(_xmlrpc.on_error) 478 _d.wsgiapp = WSGIAppTool(_wsgiapp.run) 479 _d.caching = CachingTool('before_handler', _caching.get, 'caching') 480 _d.expires = Tool('before_finalize', _caching.expires) 481 _d.tidy = Tool('before_finalize', tidy.tidy) 482 _d.nsgmls = Tool('before_finalize', tidy.nsgmls) 483 _d.ignore_headers = Tool('before_request_body', cptools.ignore_headers) 484 _d.referer = Tool('before_request_body', cptools.referer) 485 _d.basic_auth = Tool('on_start_resource', auth.basic_auth) 486 _d.digest_auth = Tool('on_start_resource', auth.digest_auth) 487 _d.trailing_slash = Tool('before_handler', cptools.trailing_slash, priority=60) 488 _d.flatten = Tool('before_finalize', cptools.flatten) 489 _d.accept = Tool('on_start_resource', cptools.accept) 490 _d.redirect = Tool('on_start_resource', cptools.redirect) 491 492 del _d, cptools, encoding, auth, static, tidy 493435 # If the Tool._name is None, supply it from the attribute name. 436 if isinstance(value, Tool): 437 if value._name is None: 438 value._name = name 439 value.namespace = self.namespace 440 object.__setattr__(self, name, value)441443 """Populate request.toolmaps from tools specified in config.""" 444 cherrypy.request.toolmaps[self.namespace] = map = {} 445 def populate(k, v): 446 toolname, arg = k.split(".", 1) 447 bucket = map.setdefault(toolname, {}) 448 bucket[arg] = v449 return populate
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Sat Aug 15 15:33:07 2009 | http://epydoc.sourceforge.net |