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 = ( 47 "CherryPy Tools cannot be turned on directly. Instead, turn them " 48 "on via config, or use them as decorators on your page handlers." 49 ) 50 5153 54 """A registered function for use with CherryPy request-processing hooks. 55 56 help(tool.callable) should give you more information about this Tool. 57 """ 58 59 namespace = "tools" 6013562 self._point = point 63 self.callable = callable 64 self._name = name 65 self._priority = priority 66 self.__doc__ = self.callable.__doc__ 67 self._setargs()6870 raise AttributeError(_attr_error)7173 raise AttributeError(_attr_error)74 on = property(_get_on, _set_on) 7577 """Copy func parameter names to obj attributes.""" 78 try: 79 for arg in _getargs(self.callable): 80 setattr(self, arg, None) 81 except (TypeError, AttributeError): 82 if hasattr(self.callable, "__call__"): 83 for arg in _getargs(self.callable.__call__): 84 setattr(self, arg, None) 85 # IronPython 1.0 raises NotImplementedError because 86 # inspect.getargspec tries to access Python bytecode 87 # in co_code attribute. 88 except NotImplementedError: 89 pass 90 # IronPython 1B1 may raise IndexError in some cases, 91 # but if we trap it here it doesn't prevent CP from working. 92 except IndexError: 93 pass9496 """Return a dict of configuration entries for this Tool.""" 97 if d: 98 conf = d.copy() 99 else: 100 conf = {} 101 102 tm = cherrypy.serving.request.toolmaps[self.namespace] 103 if self._name in tm: 104 conf.update(tm[self._name]) 105 106 if "on" in conf: 107 del conf["on"] 108 109 return conf110112 """Compile-time decorator (turn on the tool in config). 113 114 For example:: 115 116 @tools.proxy() 117 def whats_my_base(self): 118 return cherrypy.request.base 119 whats_my_base.exposed = True 120 """ 121 if args: 122 raise TypeError("The %r Tool does not accept positional " 123 "arguments; you must use keyword arguments." 124 % self._name) 125 126 def tool_decorator(f): 127 if not hasattr(f, "_cp_config"): 128 f._cp_config = {} 129 subspace = self.namespace + "." + self._name + "." 130 f._cp_config[subspace + "on"] = True 131 for k, v in kwargs.items(): 132 f._cp_config[subspace + k] = v 133 return f134 return tool_decorator137 """Hook this tool into cherrypy.request. 138 139 The standard CherryPy request object will automatically call this 140 method when the tool is "turned on" in config. 141 """ 142 conf = self._merged_args() 143 p = conf.pop("priority", None) 144 if p is None: 145 p = getattr(self.callable, "priority", self._priority) 146 cherrypy.serving.request.hooks.attach(self._point, self.callable, 147 priority=p, **conf)148 149151 152 """Tool which is called 'before main', that may skip normal handlers. 153 154 If the tool successfully handles the request (by setting response.body), 155 if should return True. This will cause CherryPy to skip any 'normal' page 156 handler. If the tool did not handle the request, it should return False 157 to tell CherryPy to continue on and call the normal page handler. If the 158 tool is declared AS a page handler (see the 'handler' method), returning 159 False will raise NotFound. 160 """ 161 164181 185166 """Use this tool as a CherryPy page handler. 167 168 For example:: 169 170 class Root: 171 nav = tools.staticdir.handler(section="/nav", dir="nav", 172 root=absDir) 173 """ 174 def handle_func(*a, **kw): 175 handled = self.callable(*args, **self._merged_args(kwargs)) 176 if not handled: 177 raise cherrypy.NotFound() 178 return cherrypy.serving.response.body179 handle_func.exposed = True 180 return handle_func187 """Hook this tool into cherrypy.request. 188 189 The standard CherryPy request object will automatically call this 190 method when the tool is "turned on" in config. 191 """ 192 conf = self._merged_args() 193 p = conf.pop("priority", None) 194 if p is None: 195 p = getattr(self.callable, "priority", self._priority) 196 cherrypy.serving.request.hooks.attach(self._point, self._wrapper, 197 priority=p, **conf)198 199201 202 """Tool which wraps request.handler in a provided wrapper function. 203 204 The 'newhandler' arg must be a handler wrapper function that takes a 205 'next_handler' argument, plus ``*args`` and ``**kwargs``. Like all 206 page handler 207 functions, it must return an iterable for use as cherrypy.response.body. 208 209 For example, to allow your 'inner' page handlers to return dicts 210 which then get interpolated into a template:: 211 212 def interpolator(next_handler, *args, **kwargs): 213 filename = cherrypy.request.config.get('template') 214 cherrypy.response.template = env.get_template(filename) 215 response_dict = next_handler(*args, **kwargs) 216 return cherrypy.response.template.render(**response_dict) 217 cherrypy.tools.jinja = HandlerWrapperTool(interpolator) 218 """ 219233 234222 self.newhandler = newhandler 223 self._point = point 224 self._name = name 225 self._priority = priority226228 innerfunc = cherrypy.serving.request.handler 229 230 def wrap(*args, **kwargs): 231 return self.newhandler(innerfunc, *args, **kwargs)232 cherrypy.serving.request.handler = wrap236 237 """Tool which is used to replace the default request.error_response.""" 238 241 244252 253 254 # Builtin tools # 255 256 from cherrypy.lib import cptools, encoding, auth, static, jsontools 257 from cherrypy.lib import sessions as _sessions, xmlrpcutil as _xmlrpc 258 from cherrypy.lib import caching as _caching 259 from cherrypy.lib import auth_basic, auth_digest 260 261246 """Hook this tool into cherrypy.request. 247 248 The standard CherryPy request object will automatically call this 249 method when the tool is "turned on" in config. 250 """ 251 cherrypy.serving.request.error_response = self._wrapper263 264 """Session Tool for CherryPy. 265 266 sessions.locking 267 When 'implicit' (the default), the session will be locked for you, 268 just before running the page handler. 269 270 When 'early', the session will be locked before reading the request 271 body. This is off by default for safety reasons; for example, 272 a large upload would block the session, denying an AJAX 273 progress meter 274 (`issue <https://bitbucket.org/cherrypy/cherrypy/issue/630>`_). 275 276 When 'explicit' (or any other value), you need to call 277 cherrypy.session.acquire_lock() yourself before using 278 session data. 279 """ 280328 329282 # _sessions.init must be bound after headers are read 283 Tool.__init__(self, 'before_request_body', _sessions.init)284 287289 """Hook this tool into cherrypy.request. 290 291 The standard CherryPy request object will automatically call this 292 method when the tool is "turned on" in config. 293 """ 294 hooks = cherrypy.serving.request.hooks 295 296 conf = self._merged_args() 297 298 p = conf.pop("priority", None) 299 if p is None: 300 p = getattr(self.callable, "priority", self._priority) 301 302 hooks.attach(self._point, self.callable, priority=p, **conf) 303 304 locking = conf.pop('locking', 'implicit') 305 if locking == 'implicit': 306 hooks.attach('before_handler', self._lock_session) 307 elif locking == 'early': 308 # Lock before the request body (but after _sessions.init runs!) 309 hooks.attach('before_request_body', self._lock_session, 310 priority=60) 311 else: 312 # Don't lock 313 pass 314 315 hooks.attach('before_finalize', _sessions.save) 316 hooks.attach('on_end_request', _sessions.close)317319 """Drop the current session and make a new one (with a new id).""" 320 sess = cherrypy.serving.session 321 sess.regenerate() 322 323 # Grab cookie-relevant tool args 324 conf = dict([(k, v) for k, v in self._merged_args().items() 325 if k in ('path', 'path_header', 'name', 'timeout', 326 'domain', 'secure')]) 327 _sessions.set_response_cookie(**conf)331 332 """A Controller (page handler collection) for XML-RPC. 333 334 To use it, have your controllers subclass this base class (it will 335 turn on the tool for you). 336 337 You can also supply the following optional config entries:: 338 339 tools.xmlrpc.encoding: 'utf-8' 340 tools.xmlrpc.allow_none: 0 341 342 XML-RPC is a rather discontinuous layer over HTTP; dispatching to the 343 appropriate handler must first be performed according to the URL, and 344 then a second dispatch step must take place according to the RPC method 345 specified in the request body. It also allows a superfluous "/RPC2" 346 prefix in the URL, supplies its own handler args in the body, and 347 requires a 200 OK "Fault" response instead of 404 when the desired 348 method is not found. 349 350 Therefore, XML-RPC cannot be implemented for CherryPy via a Tool alone. 351 This Controller acts as the dispatch target for the first half (based 352 on the URL); it then reads the RPC method from the request body and 353 does its own second dispatch step based on that method. It also reads 354 body params, and returns a Fault on error. 355 356 The XMLRPCDispatcher strips any /RPC2 prefix; if you aren't using /RPC2 357 in your URL's, you can safely skip turning on the XMLRPCDispatcher. 358 Otherwise, you need to use declare it in config:: 359 360 request.dispatch: cherrypy.dispatch.XMLRPCDispatcher() 361 """ 362 363 # Note we're hard-coding this into the 'tools' namespace. We could do 364 # a huge amount of work to make it relocatable, but the only reason why 365 # would be if someone actually disabled the default_toolbox. Meh. 366 _cp_config = {'tools.xmlrpc.on': True} 367391 392369 rpcparams, rpcmethod = _xmlrpc.process_body() 370 371 subhandler = self 372 for attr in str(rpcmethod).split('.'): 373 subhandler = getattr(subhandler, attr, None) 374 375 if subhandler and getattr(subhandler, "exposed", False): 376 body = subhandler(*(vpath + rpcparams), **params) 377 378 else: 379 # https://bitbucket.org/cherrypy/cherrypy/issue/533 380 # if a method is not found, an xmlrpclib.Fault should be returned 381 # raising an exception here will do that; see 382 # cherrypy.lib.xmlrpcutil.on_error 383 raise Exception('method "%s" is not supported' % attr) 384 385 conf = cherrypy.serving.request.toolmaps['tools'].get("xmlrpc", {}) 386 _xmlrpc.respond(body, 387 conf.get('encoding', 'utf-8'), 388 conf.get('allow_none', 0)) 389 return cherrypy.serving.response.body390 default.exposed = True394399 400396 for name in dir(cptools.SessionAuth): 397 if not name.startswith("__"): 398 setattr(self, name, None)402 403 """Caching Tool for CherryPy.""" 404423 424406 request = cherrypy.serving.request 407 if _caching.get(**kwargs): 408 request.handler = None 409 else: 410 if request.cacheable: 411 # Note the devious technique here of adding hooks on the fly 412 request.hooks.attach('before_finalize', _caching.tee_output, 413 priority=90)414 _wrapper.priority = 20 415426 427 """A collection of Tools. 428 429 This object also functions as a config namespace handler for itself. 430 Custom toolboxes should be added to each Application's toolboxes dict. 431 """ 432 435453437 # If the Tool._name is None, supply it from the attribute name. 438 if isinstance(value, Tool): 439 if value._name is None: 440 value._name = name 441 value.namespace = self.namespace 442 object.__setattr__(self, name, value)443445 """Populate request.toolmaps from tools specified in config.""" 446 cherrypy.serving.request.toolmaps[self.namespace] = map = {} 447 448 def populate(k, v): 449 toolname, arg = k.split(".", 1) 450 bucket = map.setdefault(toolname, {}) 451 bucket[arg] = v452 return populate455 """Run tool._setup() for each tool in our toolmap.""" 456 map = cherrypy.serving.request.toolmaps.get(self.namespace) 457 if map: 458 for name, settings in map.items(): 459 if settings.get("on", False): 460 tool = getattr(self, name) 461 tool._setup()462 463 480482 warnings.warn(self.warnmsg)483 484 485 default_toolbox = _d = Toolbox("tools") 486 _d.session_auth = SessionAuthTool(cptools.session_auth) 487 _d.allow = Tool('on_start_resource', cptools.allow) 488 _d.proxy = Tool('before_request_body', cptools.proxy, priority=30) 489 _d.response_headers = Tool('on_start_resource', cptools.response_headers) 490 _d.log_tracebacks = Tool('before_error_response', cptools.log_traceback) 491 _d.log_headers = Tool('before_error_response', cptools.log_request_headers) 492 _d.log_hooks = Tool('on_end_request', cptools.log_hooks, priority=100) 493 _d.err_redirect = ErrorTool(cptools.redirect) 494 _d.etags = Tool('before_finalize', cptools.validate_etags, priority=75) 495 _d.decode = Tool('before_request_body', encoding.decode) 496 # the order of encoding, gzip, caching is important 497 _d.encode = Tool('before_handler', encoding.ResponseEncoder, priority=70) 498 _d.gzip = Tool('before_finalize', encoding.gzip, priority=80) 499 _d.staticdir = HandlerTool(static.staticdir) 500 _d.staticfile = HandlerTool(static.staticfile) 501 _d.sessions = SessionTool() 502 _d.xmlrpc = ErrorTool(_xmlrpc.on_error) 503 _d.caching = CachingTool('before_handler', _caching.get, 'caching') 504 _d.expires = Tool('before_finalize', _caching.expires) 505 _d.tidy = DeprecatedTool( 506 'before_finalize', 507 "The tidy tool has been removed from the standard distribution of " 508 "CherryPy. The most recent version can be found at " 509 "http://tools.cherrypy.org/browser.") 510 _d.nsgmls = DeprecatedTool( 511 'before_finalize', 512 "The nsgmls tool has been removed from the standard distribution of " 513 "CherryPy. The most recent version can be found at " 514 "http://tools.cherrypy.org/browser.") 515 _d.ignore_headers = Tool('before_request_body', cptools.ignore_headers) 516 _d.referer = Tool('before_request_body', cptools.referer) 517 _d.basic_auth = Tool('on_start_resource', auth.basic_auth) 518 _d.digest_auth = Tool('on_start_resource', auth.digest_auth) 519 _d.trailing_slash = Tool('before_handler', cptools.trailing_slash, priority=60) 520 _d.flatten = Tool('before_finalize', cptools.flatten) 521 _d.accept = Tool('on_start_resource', cptools.accept) 522 _d.redirect = Tool('on_start_resource', cptools.redirect) 523 _d.autovary = Tool('on_start_resource', cptools.autovary, priority=0) 524 _d.json_in = Tool('before_request_body', jsontools.json_in, priority=30) 525 _d.json_out = Tool('before_handler', jsontools.json_out, priority=30) 526 _d.auth_basic = Tool('before_handler', auth_basic.basic_auth, priority=1) 527 _d.auth_digest = Tool('before_handler', auth_digest.digest_auth, priority=1) 528 529 del _d, cptools, encoding, auth, static 530
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Wed Jan 13 06:16:22 2016 | http://epydoc.sourceforge.net |