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 """A registered function for use with CherryPy request-processing hooks. 30 31 help(tool.callable) should give you more information about this Tool. 32 """ 33 34 namespace = "tools" 3510237 self._point = point 38 self.callable = callable 39 self._name = name 40 self._priority = priority 41 self.__doc__ = self.callable.__doc__ 42 self._setargs()4345 """Copy func parameter names to obj attributes.""" 46 try: 47 import inspect 48 for arg in inspect.getargspec(self.callable)[0]: 49 setattr(self, arg, None) 50 except (ImportError, AttributeError): 51 pass 52 except TypeError: 53 if hasattr(self.callable, "__call__"): 54 for arg in inspect.getargspec(self.callable.__call__)[0]: 55 setattr(self, arg, None) 56 # IronPython 1.0 raises NotImplementedError because 57 # inspect.getargspec tries to access Python bytecode 58 # in co_code attribute. 59 except NotImplementedError: 60 pass 61 # IronPython 1B1 may raise that error in some cases 62 # but if we trap it here it doesn't prevent CP from 63 # working 64 except IndexError: 65 pass6668 tm = cherrypy.request.toolmaps[self.namespace] 69 if self._name in tm: 70 conf = tm[self._name].copy() 71 else: 72 conf = {} 73 if d: 74 conf.update(d) 75 if "on" in conf: 76 del conf["on"] 77 return conf7880 """Compile-time decorator (turn on the tool in config). 81 82 For example: 83 84 @tools.proxy() 85 def whats_my_base(self): 86 return cherrypy.request.base 87 whats_my_base.exposed = True 88 """ 89 if args: 90 raise TypeError("The %r Tool does not accept positional " 91 "arguments; you must use keyword arguments." 92 % self._name) 93 def tool_decorator(f): 94 if not hasattr(f, "_cp_config"): 95 f._cp_config = {} 96 subspace = self.namespace + "." + self._name + "." 97 f._cp_config[subspace + "on"] = True 98 for k, v in kwargs.iteritems(): 99 f._cp_config[subspace + k] = v 100 return f101 return tool_decorator104 """Hook this tool into cherrypy.request. 105 106 The standard CherryPy request object will automatically call this 107 method when the tool is "turned on" in config. 108 """ 109 conf = self._merged_args() 110 p = conf.pop("priority", None) 111 if p is None: 112 p = getattr(self.callable, "priority", self._priority) 113 cherrypy.request.hooks.attach(self._point, self.callable, 114 priority=p, **conf)115 116118 """Tool which is called 'before main', that may skip normal handlers. 119 120 If the tool successfully handles the request (by setting response.body), 121 if should return True. This will cause CherryPy to skip any 'normal' page 122 handler. If the tool did not handle the request, it should return False 123 to tell CherryPy to continue on and call the normal page handler. If the 124 tool is declared AS a page handler (see the 'handler' method), returning 125 False will raise NotFound. 126 """ 127 130146 150132 """Use this tool as a CherryPy page handler. 133 134 For example: 135 class Root: 136 nav = tools.staticdir.handler(section="/nav", dir="nav", 137 root=absDir) 138 """ 139 def handle_func(*a, **kw): 140 handled = self.callable(*args, **self._merged_args(kwargs)) 141 if not handled: 142 raise cherrypy.NotFound() 143 return cherrypy.response.body144 handle_func.exposed = True 145 return handle_func152 """Hook this tool into cherrypy.request. 153 154 The standard CherryPy request object will automatically call this 155 method when the tool is "turned on" in config. 156 """ 157 conf = self._merged_args() 158 p = conf.pop("priority", None) 159 if p is None: 160 p = getattr(self.callable, "priority", self._priority) 161 cherrypy.request.hooks.attach(self._point, self._wrapper, 162 priority=p, **conf)163 164166 """Tool which is used to replace the default request.error_response.""" 167 170181 182 183 # Builtin tools # 184 185 from cherrypy.lib import cptools, encoding, auth, static, tidy 186 from cherrypy.lib import sessions as _sessions, xmlrpc as _xmlrpc 187 from cherrypy.lib import caching as _caching, wsgiapp as _wsgiapp 188 189172 self.callable(**self._merged_args())173175 """Hook this tool into cherrypy.request. 176 177 The standard CherryPy request object will automatically call this 178 method when the tool is "turned on" in config. 179 """ 180 cherrypy.request.error_response = self._wrapper191 """Session Tool for CherryPy. 192 193 sessions.locking: 194 When 'implicit' (the default), the session will be locked for you, 195 just before running the page handler. 196 When 'early', the session will be locked before reading the request 197 body. This is off by default for safety reasons; for example, 198 a large upload would block the session, denying an AJAX 199 progress meter (see http://www.cherrypy.org/ticket/630). 200 When 'explicit' (or any other value), you need to call 201 cherrypy.session.acquire_lock() yourself before using 202 session data. 203 """ 204241 242206 # _sessions.init must be bound after headers are read 207 Tool.__init__(self, 'before_request_body', _sessions.init)208 211213 """Hook this tool into cherrypy.request. 214 215 The standard CherryPy request object will automatically call this 216 method when the tool is "turned on" in config. 217 """ 218 hooks = cherrypy.request.hooks 219 220 conf = self._merged_args() 221 222 p = conf.pop("priority", None) 223 if p is None: 224 p = getattr(self.callable, "priority", self._priority) 225 226 hooks.attach(self._point, self.callable, priority=p, **conf) 227 228 locking = conf.pop('locking', 'implicit') 229 if locking == 'implicit': 230 hooks.attach('before_handler', self._lock_session) 231 elif locking == 'early': 232 # Lock before the request body (but after _sessions.init runs!) 233 hooks.attach('before_request_body', self._lock_session, 234 priority=60) 235 else: 236 # Don't lock 237 pass 238 239 hooks.attach('before_finalize', _sessions.save) 240 hooks.attach('on_end_request', _sessions.close)244 245 # Note we're hard-coding this into the 'tools' namespace. We could do 246 # a huge amount of work to make it relocatable, but the only reason why 247 # would be if someone actually disabled the default_toolbox. Meh. 248 _cp_config = {'tools.xmlrpc.on': True} 249275 276251 rpcparams, rpcmethod = _xmlrpc.process_body() 252 253 subhandler = self 254 for attr in str(rpcmethod).split('.'): 255 subhandler = getattr(subhandler, attr, None) 256 257 if subhandler and getattr(subhandler, "exposed", False): 258 body = subhandler(*(vpath + rpcparams), **params) 259 260 else: 261 # http://www.cherrypy.org/ticket/533 262 # if a method is not found, an xmlrpclib.Fault should be returned 263 # raising an exception here will do that; see 264 # cherrypy.lib.xmlrpc.on_error 265 raise Exception, 'method "%s" is not supported' % attr 266 267 conf = cherrypy.request.toolmaps['tools'].get("xmlrpc", {}) 268 _xmlrpc.respond(body, 269 conf.get('encoding', 'utf-8'), 270 conf.get('allow_none', 0)) 271 return cherrypy.response.body272 __call__.exposed = True 273 274 index = __call__278 """A tool for running any WSGI middleware/application within CP. 279 280 Here are the parameters: 281 282 wsgi_app - any wsgi application callable 283 env_update - a dictionary with arbitrary keys and values to be 284 merged with the WSGI environ dictionary. 285 286 Example: 287 288 class Whatever: 289 _cp_config = {'tools.wsgiapp.on': True, 290 'tools.wsgiapp.app': some_app, 291 'tools.wsgiapp.env': app_environ, 292 } 293 """ 294299 300296 # Keep request body intact so the wsgi app can have its way with it. 297 cherrypy.request.process_request_body = False 298 HandlerTool._setup(self)302307 308304 for name in dir(cptools.SessionAuth): 305 if not name.startswith("__"): 306 setattr(self, name, None)310 """Caching Tool for CherryPy.""" 311330 331 332313 request = cherrypy.request 314 if _caching.get(**kwargs): 315 request.handler = None 316 else: 317 if request.cacheable: 318 # Note the devious technique here of adding hooks on the fly 319 request.hooks.attach('before_finalize', _caching.tee_output, 320 priority = 90)321 _wrapper.priority = 20 322334 """A collection of Tools. 335 336 This object also functions as a config namespace handler for itself. 337 """ 338 342359 368 369 370 default_toolbox = _d = Toolbox("tools") 371 _d.session_auth = SessionAuthTool(cptools.session_auth) 372 _d.proxy = Tool('before_request_body', cptools.proxy, priority=30) 373 _d.response_headers = Tool('on_start_resource', cptools.response_headers) 374 _d.log_tracebacks = Tool('before_error_response', cptools.log_traceback) 375 _d.log_headers = Tool('before_error_response', cptools.log_request_headers) 376 _d.err_redirect = ErrorTool(cptools.redirect) 377 _d.etags = Tool('before_finalize', cptools.validate_etags) 378 _d.decode = Tool('before_handler', encoding.decode) 379 # the order of encoding, gzip, caching is important 380 _d.encode = Tool('before_finalize', encoding.encode, priority=70) 381 _d.gzip = Tool('before_finalize', encoding.gzip, priority=80) 382 _d.staticdir = HandlerTool(static.staticdir) 383 _d.staticfile = HandlerTool(static.staticfile) 384 _d.sessions = SessionTool() 385 _d.xmlrpc = ErrorTool(_xmlrpc.on_error) 386 _d.wsgiapp = WSGIAppTool(_wsgiapp.run) 387 _d.caching = CachingTool('before_handler', _caching.get, 'caching') 388 _d.expires = Tool('before_finalize', _caching.expires) 389 _d.tidy = Tool('before_finalize', tidy.tidy) 390 _d.nsgmls = Tool('before_finalize', tidy.nsgmls) 391 _d.ignore_headers = Tool('before_request_body', cptools.ignore_headers) 392 _d.referer = Tool('before_request_body', cptools.referer) 393 _d.basic_auth = Tool('on_start_resource', auth.basic_auth) 394 _d.digest_auth = Tool('on_start_resource', auth.digest_auth) 395 _d.trailing_slash = Tool('before_handler', cptools.trailing_slash) 396 _d.flatten = Tool('before_finalize', cptools.flatten) 397 _d.accept = Tool('on_start_resource', cptools.accept) 398 399 del _d, cptools, encoding, auth, static, tidy 400344 # If the Tool._name is None, supply it from the attribute name. 345 if isinstance(value, Tool): 346 if value._name is None: 347 value._name = name 348 value.namespace = self.namespace 349 object.__setattr__(self, name, value)350352 """Populate request.toolmaps from tools specified in config.""" 353 cherrypy.request.toolmaps[self.namespace] = map = {} 354 def populate(k, v): 355 toolname, arg = k.split(".", 1) 356 bucket = map.setdefault(toolname, {}) 357 bucket[arg] = v358 return populate
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0beta1 on Wed Aug 15 01:25:11 2007 | http://epydoc.sourceforge.net |