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 sys 26 import warnings 27 28 import cherrypy 29 3032 """Return the names of all static arguments to the given function.""" 33 # Use this instead of importing inspect for less mem overhead. 34 import types 35 if sys.version_info >= (3, 0): 36 if isinstance(func, types.MethodType): 37 func = func.__func__ 38 co = func.__code__ 39 else: 40 if isinstance(func, types.MethodType): 41 func = func.im_func 42 co = func.func_code 43 return co.co_varnames[:co.co_argcount]44 45 46 _attr_error = ("CherryPy Tools cannot be turned on directly. Instead, turn them " 47 "on via config, or use them as decorators on your page handlers.") 4850 """A registered function for use with CherryPy request-processing hooks. 51 52 help(tool.callable) should give you more information about this Tool. 53 """ 54 55 namespace = "tools" 5612958 self._point = point 59 self.callable = callable 60 self._name = name 61 self._priority = priority 62 self.__doc__ = self.callable.__doc__ 63 self._setargs()6466 raise AttributeError(_attr_error)68 raise AttributeError(_attr_error)69 on = property(_get_on, _set_on) 7072 """Copy func parameter names to obj attributes.""" 73 try: 74 for arg in _getargs(self.callable): 75 setattr(self, arg, None) 76 except (TypeError, AttributeError): 77 if hasattr(self.callable, "__call__"): 78 for arg in _getargs(self.callable.__call__): 79 setattr(self, arg, None) 80 # IronPython 1.0 raises NotImplementedError because 81 # inspect.getargspec tries to access Python bytecode 82 # in co_code attribute. 83 except NotImplementedError: 84 pass 85 # IronPython 1B1 may raise IndexError in some cases, 86 # but if we trap it here it doesn't prevent CP from working. 87 except IndexError: 88 pass8991 """Return a dict of configuration entries for this Tool.""" 92 if d: 93 conf = d.copy() 94 else: 95 conf = {} 96 97 tm = cherrypy.serving.request.toolmaps[self.namespace] 98 if self._name in tm: 99 conf.update(tm[self._name]) 100 101 if "on" in conf: 102 del conf["on"] 103 104 return conf105107 """Compile-time decorator (turn on the tool in config). 108 109 For example:: 110 111 @tools.proxy() 112 def whats_my_base(self): 113 return cherrypy.request.base 114 whats_my_base.exposed = True 115 """ 116 if args: 117 raise TypeError("The %r Tool does not accept positional " 118 "arguments; you must use keyword arguments." 119 % self._name) 120 def tool_decorator(f): 121 if not hasattr(f, "_cp_config"): 122 f._cp_config = {} 123 subspace = self.namespace + "." + self._name + "." 124 f._cp_config[subspace + "on"] = True 125 for k, v in kwargs.items(): 126 f._cp_config[subspace + k] = v 127 return f128 return tool_decorator131 """Hook this tool into cherrypy.request. 132 133 The standard CherryPy request object will automatically call this 134 method when the tool is "turned on" in config. 135 """ 136 conf = self._merged_args() 137 p = conf.pop("priority", None) 138 if p is None: 139 p = getattr(self.callable, "priority", self._priority) 140 cherrypy.serving.request.hooks.attach(self._point, self.callable, 141 priority=p, **conf)142 143145 """Tool which is called 'before main', that may skip normal handlers. 146 147 If the tool successfully handles the request (by setting response.body), 148 if should return True. This will cause CherryPy to skip any 'normal' page 149 handler. If the tool did not handle the request, it should return False 150 to tell CherryPy to continue on and call the normal page handler. If the 151 tool is declared AS a page handler (see the 'handler' method), returning 152 False will raise NotFound. 153 """ 154 157174 178159 """Use this tool as a CherryPy page handler. 160 161 For example:: 162 163 class Root: 164 nav = tools.staticdir.handler(section="/nav", dir="nav", 165 root=absDir) 166 """ 167 def handle_func(*a, **kw): 168 handled = self.callable(*args, **self._merged_args(kwargs)) 169 if not handled: 170 raise cherrypy.NotFound() 171 return cherrypy.serving.response.body172 handle_func.exposed = True 173 return handle_func180 """Hook this tool into cherrypy.request. 181 182 The standard CherryPy request object will automatically call this 183 method when the tool is "turned on" in config. 184 """ 185 conf = self._merged_args() 186 p = conf.pop("priority", None) 187 if p is None: 188 p = getattr(self.callable, "priority", self._priority) 189 cherrypy.serving.request.hooks.attach(self._point, self._wrapper, 190 priority=p, **conf)191 192194 """Tool which wraps request.handler in a provided wrapper function. 195 196 The 'newhandler' arg must be a handler wrapper function that takes a 197 'next_handler' argument, plus ``*args`` and ``**kwargs``. Like all 198 page handler 199 functions, it must return an iterable for use as cherrypy.response.body. 200 201 For example, to allow your 'inner' page handlers to return dicts 202 which then get interpolated into a template:: 203 204 def interpolator(next_handler, *args, **kwargs): 205 filename = cherrypy.request.config.get('template') 206 cherrypy.response.template = env.get_template(filename) 207 response_dict = next_handler(*args, **kwargs) 208 return cherrypy.response.template.render(**response_dict) 209 cherrypy.tools.jinja = HandlerWrapperTool(interpolator) 210 """ 211223 224213 self.newhandler = newhandler 214 self._point = point 215 self._name = name 216 self._priority = priority217219 innerfunc = cherrypy.serving.request.handler 220 def wrap(*args, **kwargs): 221 return self.newhandler(innerfunc, *args, **kwargs)222 cherrypy.serving.request.handler = wrap226 """Tool which is used to replace the default request.error_response.""" 227 230 233241 242 243 # Builtin tools # 244 245 from cherrypy.lib import cptools, encoding, auth, static, jsontools 246 from cherrypy.lib import sessions as _sessions, xmlrpcutil as _xmlrpc 247 from cherrypy.lib import caching as _caching 248 from cherrypy.lib import auth_basic, auth_digest 249 250235 """Hook this tool into cherrypy.request. 236 237 The standard CherryPy request object will automatically call this 238 method when the tool is "turned on" in config. 239 """ 240 cherrypy.serving.request.error_response = self._wrapper252 """Session Tool for CherryPy. 253 254 sessions.locking 255 When 'implicit' (the default), the session will be locked for you, 256 just before running the page handler. 257 258 When 'early', the session will be locked before reading the request 259 body. This is off by default for safety reasons; for example, 260 a large upload would block the session, denying an AJAX 261 progress meter (see http://www.cherrypy.org/ticket/630). 262 263 When 'explicit' (or any other value), you need to call 264 cherrypy.session.acquire_lock() yourself before using 265 session data. 266 """ 267315 316 317 318269 # _sessions.init must be bound after headers are read 270 Tool.__init__(self, 'before_request_body', _sessions.init)271 274276 """Hook this tool into cherrypy.request. 277 278 The standard CherryPy request object will automatically call this 279 method when the tool is "turned on" in config. 280 """ 281 hooks = cherrypy.serving.request.hooks 282 283 conf = self._merged_args() 284 285 p = conf.pop("priority", None) 286 if p is None: 287 p = getattr(self.callable, "priority", self._priority) 288 289 hooks.attach(self._point, self.callable, priority=p, **conf) 290 291 locking = conf.pop('locking', 'implicit') 292 if locking == 'implicit': 293 hooks.attach('before_handler', self._lock_session) 294 elif locking == 'early': 295 # Lock before the request body (but after _sessions.init runs!) 296 hooks.attach('before_request_body', self._lock_session, 297 priority=60) 298 else: 299 # Don't lock 300 pass 301 302 hooks.attach('before_finalize', _sessions.save) 303 hooks.attach('on_end_request', _sessions.close)304306 """Drop the current session and make a new one (with a new id).""" 307 sess = cherrypy.serving.session 308 sess.regenerate() 309 310 # Grab cookie-relevant tool args 311 conf = dict([(k, v) for k, v in self._merged_args().items() 312 if k in ('path', 'path_header', 'name', 'timeout', 313 'domain', 'secure')]) 314 _sessions.set_response_cookie(**conf)320 """A Controller (page handler collection) for XML-RPC. 321 322 To use it, have your controllers subclass this base class (it will 323 turn on the tool for you). 324 325 You can also supply the following optional config entries:: 326 327 tools.xmlrpc.encoding: 'utf-8' 328 tools.xmlrpc.allow_none: 0 329 330 XML-RPC is a rather discontinuous layer over HTTP; dispatching to the 331 appropriate handler must first be performed according to the URL, and 332 then a second dispatch step must take place according to the RPC method 333 specified in the request body. It also allows a superfluous "/RPC2" 334 prefix in the URL, supplies its own handler args in the body, and 335 requires a 200 OK "Fault" response instead of 404 when the desired 336 method is not found. 337 338 Therefore, XML-RPC cannot be implemented for CherryPy via a Tool alone. 339 This Controller acts as the dispatch target for the first half (based 340 on the URL); it then reads the RPC method from the request body and 341 does its own second dispatch step based on that method. It also reads 342 body params, and returns a Fault on error. 343 344 The XMLRPCDispatcher strips any /RPC2 prefix; if you aren't using /RPC2 345 in your URL's, you can safely skip turning on the XMLRPCDispatcher. 346 Otherwise, you need to use declare it in config:: 347 348 request.dispatch: cherrypy.dispatch.XMLRPCDispatcher() 349 """ 350 351 # Note we're hard-coding this into the 'tools' namespace. We could do 352 # a huge amount of work to make it relocatable, but the only reason why 353 # would be if someone actually disabled the default_toolbox. Meh. 354 _cp_config = {'tools.xmlrpc.on': True} 355379 380357 rpcparams, rpcmethod = _xmlrpc.process_body() 358 359 subhandler = self 360 for attr in str(rpcmethod).split('.'): 361 subhandler = getattr(subhandler, attr, None) 362 363 if subhandler and getattr(subhandler, "exposed", False): 364 body = subhandler(*(vpath + rpcparams), **params) 365 366 else: 367 # http://www.cherrypy.org/ticket/533 368 # if a method is not found, an xmlrpclib.Fault should be returned 369 # raising an exception here will do that; see 370 # cherrypy.lib.xmlrpcutil.on_error 371 raise Exception('method "%s" is not supported' % attr) 372 373 conf = cherrypy.serving.request.toolmaps['tools'].get("xmlrpc", {}) 374 _xmlrpc.respond(body, 375 conf.get('encoding', 'utf-8'), 376 conf.get('allow_none', 0)) 377 return cherrypy.serving.response.body378 default.exposed = True382387 388384 for name in dir(cptools.SessionAuth): 385 if not name.startswith("__"): 386 setattr(self, name, None)390 """Caching Tool for CherryPy.""" 391410 411 412393 request = cherrypy.serving.request 394 if _caching.get(**kwargs): 395 request.handler = None 396 else: 397 if request.cacheable: 398 # Note the devious technique here of adding hooks on the fly 399 request.hooks.attach('before_finalize', _caching.tee_output, 400 priority = 90)401 _wrapper.priority = 20 402414 """A collection of Tools. 415 416 This object also functions as a config namespace handler for itself. 417 Custom toolboxes should be added to each Application's toolboxes dict. 418 """ 419 422439424 # If the Tool._name is None, supply it from the attribute name. 425 if isinstance(value, Tool): 426 if value._name is None: 427 value._name = name 428 value.namespace = self.namespace 429 object.__setattr__(self, name, value)430432 """Populate request.toolmaps from tools specified in config.""" 433 cherrypy.serving.request.toolmaps[self.namespace] = map = {} 434 def populate(k, v): 435 toolname, arg = k.split(".", 1) 436 bucket = map.setdefault(toolname, {}) 437 bucket[arg] = v438 return populate441 """Run tool._setup() for each tool in our toolmap.""" 442 map = cherrypy.serving.request.toolmaps.get(self.namespace) 443 if map: 444 for name, settings in map.items(): 445 if settings.get("on", False): 446 tool = getattr(self, name) 447 tool._setup()448 449 465467 warnings.warn(self.warnmsg)468 469 470 default_toolbox = _d = Toolbox("tools") 471 _d.session_auth = SessionAuthTool(cptools.session_auth) 472 _d.allow = Tool('on_start_resource', cptools.allow) 473 _d.proxy = Tool('before_request_body', cptools.proxy, priority=30) 474 _d.response_headers = Tool('on_start_resource', cptools.response_headers) 475 _d.log_tracebacks = Tool('before_error_response', cptools.log_traceback) 476 _d.log_headers = Tool('before_error_response', cptools.log_request_headers) 477 _d.log_hooks = Tool('on_end_request', cptools.log_hooks, priority=100) 478 _d.err_redirect = ErrorTool(cptools.redirect) 479 _d.etags = Tool('before_finalize', cptools.validate_etags, priority=75) 480 _d.decode = Tool('before_request_body', encoding.decode) 481 # the order of encoding, gzip, caching is important 482 _d.encode = Tool('before_handler', encoding.ResponseEncoder, priority=70) 483 _d.gzip = Tool('before_finalize', encoding.gzip, priority=80) 484 _d.staticdir = HandlerTool(static.staticdir) 485 _d.staticfile = HandlerTool(static.staticfile) 486 _d.sessions = SessionTool() 487 _d.xmlrpc = ErrorTool(_xmlrpc.on_error) 488 _d.caching = CachingTool('before_handler', _caching.get, 'caching') 489 _d.expires = Tool('before_finalize', _caching.expires) 490 _d.tidy = DeprecatedTool('before_finalize', 491 "The tidy tool has been removed from the standard distribution of CherryPy. " 492 "The most recent version can be found at http://tools.cherrypy.org/browser.") 493 _d.nsgmls = DeprecatedTool('before_finalize', 494 "The nsgmls tool has been removed from the standard distribution of CherryPy. " 495 "The most recent version can be found at http://tools.cherrypy.org/browser.") 496 _d.ignore_headers = Tool('before_request_body', cptools.ignore_headers) 497 _d.referer = Tool('before_request_body', cptools.referer) 498 _d.basic_auth = Tool('on_start_resource', auth.basic_auth) 499 _d.digest_auth = Tool('on_start_resource', auth.digest_auth) 500 _d.trailing_slash = Tool('before_handler', cptools.trailing_slash, priority=60) 501 _d.flatten = Tool('before_finalize', cptools.flatten) 502 _d.accept = Tool('on_start_resource', cptools.accept) 503 _d.redirect = Tool('on_start_resource', cptools.redirect) 504 _d.autovary = Tool('on_start_resource', cptools.autovary, priority=0) 505 _d.json_in = Tool('before_request_body', jsontools.json_in, priority=30) 506 _d.json_out = Tool('before_handler', jsontools.json_out, priority=30) 507 _d.auth_basic = Tool('before_handler', auth_basic.basic_auth, priority=1) 508 _d.auth_digest = Tool('before_handler', auth_digest.digest_auth, priority=1) 509 510 del _d, cptools, encoding, auth, static 511
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Sun Mar 16 17:19:56 2014 | http://epydoc.sourceforge.net |