Package cherrypy
[hide private]
[frames] | no frames]

Source Code for Package cherrypy

  1  """CherryPy is a pythonic, object-oriented HTTP framework. 
  2   
  3   
  4  CherryPy consists of not one, but four separate API layers. 
  5   
  6  The APPLICATION LAYER is the simplest. CherryPy applications are written as 
  7  a tree of classes and methods, where each branch in the tree corresponds to 
  8  a branch in the URL path. Each method is a 'page handler', which receives 
  9  GET and POST params as keyword arguments, and returns or yields the (HTML) 
 10  body of the response. The special method name 'index' is used for paths 
 11  that end in a slash, and the special method name 'default' is used to 
 12  handle multiple paths via a single handler. This layer also includes: 
 13   
 14   * the 'exposed' attribute (and cherrypy.expose) 
 15   * cherrypy.quickstart() 
 16   * _cp_config attributes 
 17   * cherrypy.tools (including cherrypy.session) 
 18   * cherrypy.url() 
 19   
 20  The ENVIRONMENT LAYER is used by developers at all levels. It provides 
 21  information about the current request and response, plus the application 
 22  and server environment, via a (default) set of top-level objects: 
 23   
 24   * cherrypy.request 
 25   * cherrypy.response 
 26   * cherrypy.engine 
 27   * cherrypy.server 
 28   * cherrypy.tree 
 29   * cherrypy.config 
 30   * cherrypy.thread_data 
 31   * cherrypy.log 
 32   * cherrypy.HTTPError, NotFound, and HTTPRedirect 
 33   * cherrypy.lib 
 34   
 35  The EXTENSION LAYER allows advanced users to construct and share their own 
 36  plugins. It consists of: 
 37   
 38   * Hook API 
 39   * Tool API 
 40   * Toolbox API 
 41   * Dispatch API 
 42   * Config Namespace API 
 43   
 44  Finally, there is the CORE LAYER, which uses the core API's to construct 
 45  the default components which are available at higher layers. You can think 
 46  of the default components as the 'reference implementation' for CherryPy. 
 47  Megaframeworks (and advanced users) may replace the default components 
 48  with customized or extended components. The core API's are: 
 49   
 50   * Application API 
 51   * Engine API 
 52   * Request API 
 53   * Server API 
 54   * WSGI API 
 55   
 56  These API's are described in the CherryPy specification: 
 57  http://www.cherrypy.org/wiki/CherryPySpec 
 58  """ 
 59   
 60  __version__ = "3.1.2" 
 61   
 62  from urlparse import urljoin as _urljoin 
 63   
 64   
65 -class _AttributeDocstrings(type):
66 """Metaclass for declaring docstrings for class attributes.""" 67 # The full docstring for this type is down in the __init__ method so 68 # that it doesn't show up in help() for every consumer class. 69
70 - def __init__(cls, name, bases, dct):
71 '''Metaclass for declaring docstrings for class attributes. 72 73 Base Python doesn't provide any syntax for setting docstrings on 74 'data attributes' (non-callables). This metaclass allows class 75 definitions to follow the declaration of a data attribute with 76 a docstring for that attribute; the attribute docstring will be 77 popped from the class dict and folded into the class docstring. 78 79 The naming convention for attribute docstrings is: 80 <attrname> + "__doc". 81 For example: 82 83 class Thing(object): 84 """A thing and its properties.""" 85 86 __metaclass__ = cherrypy._AttributeDocstrings 87 88 height = 50 89 height__doc = """The height of the Thing in inches.""" 90 91 In which case, help(Thing) starts like this: 92 93 >>> help(mod.Thing) 94 Help on class Thing in module pkg.mod: 95 96 class Thing(__builtin__.object) 97 | A thing and its properties. 98 | 99 | height [= 50]: 100 | The height of the Thing in inches. 101 | 102 103 The benefits of this approach over hand-edited class docstrings: 104 1. Places the docstring nearer to the attribute declaration. 105 2. Makes attribute docs more uniform ("name (default): doc"). 106 3. Reduces mismatches of attribute _names_ between 107 the declaration and the documentation. 108 4. Reduces mismatches of attribute default _values_ between 109 the declaration and the documentation. 110 111 The benefits of a metaclass approach over other approaches: 112 1. Simpler ("less magic") than interface-based solutions. 113 2. __metaclass__ can be specified at the module global level 114 for classic classes. 115 116 For various formatting reasons, you should write multiline docs 117 with a leading newline and not a trailing one: 118 119 response__doc = """ 120 The response object for the current thread. In the main thread, 121 and any threads which are not HTTP requests, this is None.""" 122 123 The type of the attribute is intentionally not included, because 124 that's not How Python Works. Quack. 125 ''' 126 127 newdoc = [cls.__doc__ or ""] 128 129 dctnames = dct.keys() 130 dctnames.sort() 131 132 for name in dctnames: 133 if name.endswith("__doc"): 134 # Remove the magic doc attribute. 135 if hasattr(cls, name): 136 delattr(cls, name) 137 138 # Make a uniformly-indented docstring from it. 139 val = '\n'.join([' ' + line.strip() 140 for line in dct[name].split('\n')]) 141 142 # Get the default value. 143 attrname = name[:-5] 144 try: 145 attrval = getattr(cls, attrname) 146 except AttributeError: 147 attrval = "missing" 148 149 # Add the complete attribute docstring to our list. 150 newdoc.append("%s [= %r]:\n%s" % (attrname, attrval, val)) 151 152 # Add our list of new docstrings to the class docstring. 153 cls.__doc__ = "\n\n".join(newdoc)
154 155 156 from cherrypy._cperror import HTTPError, HTTPRedirect, InternalRedirect 157 from cherrypy._cperror import NotFound, CherryPyException, TimeoutError 158 159 from cherrypy import _cpdispatch as dispatch 160 161 from cherrypy import _cptools 162 tools = _cptools.default_toolbox 163 Tool = _cptools.Tool 164 165 from cherrypy import _cprequest 166 from cherrypy.lib import http as _http 167 168 from cherrypy import _cptree 169 tree = _cptree.Tree() 170 from cherrypy._cptree import Application 171 from cherrypy import _cpwsgi as wsgi 172 173 from cherrypy import process 174 try: 175 from cherrypy.process import win32 176 engine = win32.Win32Bus() 177 engine.console_control_handler = win32.ConsoleCtrlHandler(engine) 178 del win32 179 except ImportError: 180 engine = process.bus 181 182 183 # Timeout monitor
184 -class _TimeoutMonitor(process.plugins.Monitor):
185
186 - def __init__(self, bus):
187 self.servings = [] 188 process.plugins.Monitor.__init__(self, bus, self.run)
189
190 - def acquire(self):
191 self.servings.append((serving.request, serving.response))
192
193 - def release(self):
194 try: 195 self.servings.remove((serving.request, serving.response)) 196 except ValueError: 197 pass
198
199 - def run(self):
200 """Check timeout on all responses. (Internal)""" 201 for req, resp in self.servings: 202 resp.check_timeout()
203 engine.timeout_monitor = _TimeoutMonitor(engine) 204 engine.timeout_monitor.subscribe() 205 206 engine.autoreload = process.plugins.Autoreloader(engine) 207 engine.autoreload.subscribe() 208 209 engine.thread_manager = process.plugins.ThreadManager(engine) 210 engine.thread_manager.subscribe() 211 212 engine.signal_handler = process.plugins.SignalHandler(engine) 213 214 215 from cherrypy import _cpserver 216 server = _cpserver.Server() 217 server.subscribe() 218 219
220 -def quickstart(root=None, script_name="", config=None):
221 """Mount the given root, start the builtin server (and engine), then block. 222 223 root: an instance of a "controller class" (a collection of page handler 224 methods) which represents the root of the application. 225 script_name: a string containing the "mount point" of the application. 226 This should start with a slash, and be the path portion of the URL 227 at which to mount the given root. For example, if root.index() will 228 handle requests to "http://www.example.com:8080/dept/app1/", then 229 the script_name argument would be "/dept/app1". 230 231 It MUST NOT end in a slash. If the script_name refers to the root 232 of the URI, it MUST be an empty string (not "/"). 233 config: a file or dict containing application config. If this contains 234 a [global] section, those entries will be used in the global 235 (site-wide) config. 236 """ 237 if config: 238 _global_conf_alias.update(config) 239 240 if root is not None: 241 tree.mount(root, script_name, config) 242 243 if hasattr(engine, "signal_handler"): 244 engine.signal_handler.subscribe() 245 if hasattr(engine, "console_control_handler"): 246 engine.console_control_handler.subscribe() 247 248 engine.start() 249 engine.block()
250 251 252 try: 253 from threading import local as _local 254 except ImportError: 255 from cherrypy._cpthreadinglocal import local as _local 256
257 -class _Serving(_local):
258 """An interface for registering request and response objects. 259 260 Rather than have a separate "thread local" object for the request and 261 the response, this class works as a single threadlocal container for 262 both objects (and any others which developers wish to define). In this 263 way, we can easily dump those objects when we stop/start a new HTTP 264 conversation, yet still refer to them as module-level globals in a 265 thread-safe way. 266 """ 267 268 __metaclass__ = _AttributeDocstrings 269 270 request = _cprequest.Request(_http.Host("127.0.0.1", 80), 271 _http.Host("127.0.0.1", 1111)) 272 request__doc = """ 273 The request object for the current thread. In the main thread, 274 and any threads which are not receiving HTTP requests, this is None.""" 275 276 response = _cprequest.Response() 277 response__doc = """ 278 The response object for the current thread. In the main thread, 279 and any threads which are not receiving HTTP requests, this is None.""" 280
281 - def load(self, request, response):
282 self.request = request 283 self.response = response
284
285 - def clear(self):
286 """Remove all attributes of self.""" 287 self.__dict__.clear()
288 289 serving = _Serving() 290 291
292 -class _ThreadLocalProxy(object):
293 294 __slots__ = ['__attrname__', '__dict__'] 295
296 - def __init__(self, attrname):
297 self.__attrname__ = attrname
298
299 - def __getattr__(self, name):
300 child = getattr(serving, self.__attrname__) 301 return getattr(child, name)
302
303 - def __setattr__(self, name, value):
304 if name in ("__attrname__", ): 305 object.__setattr__(self, name, value) 306 else: 307 child = getattr(serving, self.__attrname__) 308 setattr(child, name, value)
309
310 - def __delattr__(self, name):
311 child = getattr(serving, self.__attrname__) 312 delattr(child, name)
313
314 - def _get_dict(self):
315 child = getattr(serving, self.__attrname__) 316 d = child.__class__.__dict__.copy() 317 d.update(child.__dict__) 318 return d
319 __dict__ = property(_get_dict) 320
321 - def __getitem__(self, key):
322 child = getattr(serving, self.__attrname__) 323 return child[key]
324
325 - def __setitem__(self, key, value):
326 child = getattr(serving, self.__attrname__) 327 child[key] = value
328
329 - def __delitem__(self, key):
330 child = getattr(serving, self.__attrname__) 331 del child[key]
332
333 - def __contains__(self, key):
334 child = getattr(serving, self.__attrname__) 335 return key in child
336
337 - def __len__(self):
338 child = getattr(serving, self.__attrname__) 339 return len(child)
340
341 - def __nonzero__(self):
342 child = getattr(serving, self.__attrname__) 343 return bool(child)
344 345 346 # Create request and response object (the same objects will be used 347 # throughout the entire life of the webserver, but will redirect 348 # to the "serving" object) 349 request = _ThreadLocalProxy('request') 350 response = _ThreadLocalProxy('response') 351 352 # Create thread_data object as a thread-specific all-purpose storage
353 -class _ThreadData(_local):
354 """A container for thread-specific data."""
355 thread_data = _ThreadData() 356 357 358 # Monkeypatch pydoc to allow help() to go through the threadlocal proxy. 359 # Jan 2007: no Googleable examples of anyone else replacing pydoc.resolve. 360 # The only other way would be to change what is returned from type(request) 361 # and that's not possible in pure Python (you'd have to fake ob_type).
362 -def _cherrypy_pydoc_resolve(thing, forceload=0):
363 """Given an object or a path to an object, get the object and its name.""" 364 if isinstance(thing, _ThreadLocalProxy): 365 thing = getattr(serving, thing.__attrname__) 366 return _pydoc._builtin_resolve(thing, forceload)
367 368 try: 369 import pydoc as _pydoc 370 _pydoc._builtin_resolve = _pydoc.resolve 371 _pydoc.resolve = _cherrypy_pydoc_resolve 372 except ImportError: 373 pass 374 375 376 from cherrypy import _cplogging 377
378 -class _GlobalLogManager(_cplogging.LogManager):
379
380 - def __call__(self, *args, **kwargs):
381 try: 382 log = request.app.log 383 except AttributeError: 384 log = self 385 return log.error(*args, **kwargs)
386
387 - def access(self):
388 try: 389 return request.app.log.access() 390 except AttributeError: 391 return _cplogging.LogManager.access(self)
392 393 394 log = _GlobalLogManager() 395 # Set a default screen handler on the global log. 396 log.screen = True 397 log.error_file = '' 398 # Using an access file makes CP about 10% slower. Leave off by default. 399 log.access_file = '' 400
401 -def _buslog(msg, level):
402 log.error(msg, 'ENGINE', severity=level)
403 engine.subscribe('log', _buslog) 404 405 # Helper functions for CP apps # 406 407
408 -def expose(func=None, alias=None):
409 """Expose the function, optionally providing an alias or set of aliases.""" 410 def expose_(func): 411 func.exposed = True 412 if alias is not None: 413 if isinstance(alias, basestring): 414 parents[alias.replace(".", "_")] = func 415 else: 416 for a in alias: 417 parents[a.replace(".", "_")] = func 418 return func
419 420 import sys, types 421 if isinstance(func, (types.FunctionType, types.MethodType)): 422 if alias is None: 423 # @expose 424 func.exposed = True 425 return func 426 else: 427 # func = expose(func, alias) 428 parents = sys._getframe(1).f_locals 429 return expose_(func) 430 elif func is None: 431 if alias is None: 432 # @expose() 433 parents = sys._getframe(1).f_locals 434 return expose_ 435 else: 436 # @expose(alias="alias") or 437 # @expose(alias=["alias1", "alias2"]) 438 parents = sys._getframe(1).f_locals 439 return expose_ 440 else: 441 # @expose("alias") or 442 # @expose(["alias1", "alias2"]) 443 parents = sys._getframe(1).f_locals 444 alias = func 445 return expose_ 446 447
448 -def url(path="", qs="", script_name=None, base=None, relative=None):
449 """Create an absolute URL for the given path. 450 451 If 'path' starts with a slash ('/'), this will return 452 (base + script_name + path + qs). 453 If it does not start with a slash, this returns 454 (base + script_name [+ request.path_info] + path + qs). 455 456 If script_name is None, cherrypy.request will be used 457 to find a script_name, if available. 458 459 If base is None, cherrypy.request.base will be used (if available). 460 Note that you can use cherrypy.tools.proxy to change this. 461 462 Finally, note that this function can be used to obtain an absolute URL 463 for the current request path (minus the querystring) by passing no args. 464 If you call url(qs=cherrypy.request.query_string), you should get the 465 original browser URL (assuming no internal redirections). 466 467 If relative is None or not provided, request.app.relative_urls will 468 be used (if available, else False). If False, the output will be an 469 absolute URL (including the scheme, host, vhost, and script_name). 470 If True, the output will instead be a URL that is relative to the 471 current request path, perhaps including '..' atoms. If relative is 472 the string 'server', the output will instead be a URL that is 473 relative to the server root; i.e., it will start with a slash. 474 """ 475 if qs: 476 qs = '?' + qs 477 478 if request.app: 479 if not path.startswith("/"): 480 # Append/remove trailing slash from path_info as needed 481 # (this is to support mistyped URL's without redirecting; 482 # if you want to redirect, use tools.trailing_slash). 483 pi = request.path_info 484 if request.is_index is True: 485 if not pi.endswith('/'): 486 pi = pi + '/' 487 elif request.is_index is False: 488 if pi.endswith('/') and pi != '/': 489 pi = pi[:-1] 490 491 if path == "": 492 path = pi 493 else: 494 path = _urljoin(pi, path) 495 496 if script_name is None: 497 script_name = request.script_name 498 if base is None: 499 base = request.base 500 501 newurl = base + script_name + path + qs 502 else: 503 # No request.app (we're being called outside a request). 504 # We'll have to guess the base from server.* attributes. 505 # This will produce very different results from the above 506 # if you're using vhosts or tools.proxy. 507 if base is None: 508 base = server.base() 509 510 path = (script_name or "") + path 511 newurl = base + path + qs 512 513 if './' in newurl: 514 # Normalize the URL by removing ./ and ../ 515 atoms = [] 516 for atom in newurl.split('/'): 517 if atom == '.': 518 pass 519 elif atom == '..': 520 atoms.pop() 521 else: 522 atoms.append(atom) 523 newurl = '/'.join(atoms) 524 525 # At this point, we should have a fully-qualified absolute URL. 526 527 if relative is None: 528 relative = getattr(request.app, "relative_urls", False) 529 530 # See http://www.ietf.org/rfc/rfc2396.txt 531 if relative == 'server': 532 # "A relative reference beginning with a single slash character is 533 # termed an absolute-path reference, as defined by <abs_path>..." 534 # This is also sometimes called "server-relative". 535 newurl = '/' + '/'.join(newurl.split('/', 3)[3:]) 536 elif relative: 537 # "A relative reference that does not begin with a scheme name 538 # or a slash character is termed a relative-path reference." 539 old = url().split('/')[:-1] 540 new = newurl.split('/') 541 while old and new: 542 a, b = old[0], new[0] 543 if a != b: 544 break 545 old.pop(0) 546 new.pop(0) 547 new = (['..'] * len(old)) + new 548 newurl = '/'.join(new) 549 550 return newurl
551 552 553 # import _cpconfig last so it can reference other top-level objects 554 from cherrypy import _cpconfig 555 # Use _global_conf_alias so quickstart can use 'config' as an arg 556 # without shadowing cherrypy.config. 557 config = _global_conf_alias = _cpconfig.Config() 558 559 from cherrypy import _cpchecker 560 checker = _cpchecker.Checker() 561 engine.subscribe('start', checker) 562