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.2.2" 
 61   
 62  from cherrypy._cpcompat import urljoin as _urljoin, urlencode as _urlencode 
 63  from cherrypy._cpcompat import basestring, unicodestr, set 
 64   
 65  from cherrypy._cperror import HTTPError, HTTPRedirect, InternalRedirect 
 66  from cherrypy._cperror import NotFound, CherryPyException, TimeoutError 
 67   
 68  from cherrypy import _cpdispatch as dispatch 
 69   
 70  from cherrypy import _cptools 
 71  tools = _cptools.default_toolbox 
 72  Tool = _cptools.Tool 
 73   
 74  from cherrypy import _cprequest 
 75  from cherrypy.lib import httputil as _httputil 
 76   
 77  from cherrypy import _cptree 
 78  tree = _cptree.Tree() 
 79  from cherrypy._cptree import Application 
 80  from cherrypy import _cpwsgi as wsgi 
 81   
 82  from cherrypy import process 
 83  try: 
 84      from cherrypy.process import win32 
 85      engine = win32.Win32Bus() 
 86      engine.console_control_handler = win32.ConsoleCtrlHandler(engine) 
 87      del win32 
 88  except ImportError: 
 89      engine = process.bus 
 90   
 91   
 92  # Timeout monitor. We add two channels to the engine 
 93  # to which cherrypy.Application will publish. 
 94  engine.listeners['before_request'] = set() 
 95  engine.listeners['after_request'] = set() 
 96   
97 -class _TimeoutMonitor(process.plugins.Monitor):
98
99 - def __init__(self, bus):
100 self.servings = [] 101 process.plugins.Monitor.__init__(self, bus, self.run)
102
103 - def before_request(self):
104 self.servings.append((serving.request, serving.response))
105
106 - def after_request(self):
107 try: 108 self.servings.remove((serving.request, serving.response)) 109 except ValueError: 110 pass
111
112 - def run(self):
113 """Check timeout on all responses. (Internal)""" 114 for req, resp in self.servings: 115 resp.check_timeout()
116 engine.timeout_monitor = _TimeoutMonitor(engine) 117 engine.timeout_monitor.subscribe() 118 119 engine.autoreload = process.plugins.Autoreloader(engine) 120 engine.autoreload.subscribe() 121 122 engine.thread_manager = process.plugins.ThreadManager(engine) 123 engine.thread_manager.subscribe() 124 125 engine.signal_handler = process.plugins.SignalHandler(engine) 126 127 128 from cherrypy import _cpserver 129 server = _cpserver.Server() 130 server.subscribe() 131 132
133 -def quickstart(root=None, script_name="", config=None):
134 """Mount the given root, start the builtin server (and engine), then block. 135 136 root: an instance of a "controller class" (a collection of page handler 137 methods) which represents the root of the application. 138 script_name: a string containing the "mount point" of the application. 139 This should start with a slash, and be the path portion of the URL 140 at which to mount the given root. For example, if root.index() will 141 handle requests to "http://www.example.com:8080/dept/app1/", then 142 the script_name argument would be "/dept/app1". 143 144 It MUST NOT end in a slash. If the script_name refers to the root 145 of the URI, it MUST be an empty string (not "/"). 146 config: a file or dict containing application config. If this contains 147 a [global] section, those entries will be used in the global 148 (site-wide) config. 149 """ 150 if config: 151 _global_conf_alias.update(config) 152 153 tree.mount(root, script_name, config) 154 155 if hasattr(engine, "signal_handler"): 156 engine.signal_handler.subscribe() 157 if hasattr(engine, "console_control_handler"): 158 engine.console_control_handler.subscribe() 159 160 engine.start() 161 engine.block()
162 163 164 from cherrypy._cpcompat import threadlocal as _local 165
166 -class _Serving(_local):
167 """An interface for registering request and response objects. 168 169 Rather than have a separate "thread local" object for the request and 170 the response, this class works as a single threadlocal container for 171 both objects (and any others which developers wish to define). In this 172 way, we can easily dump those objects when we stop/start a new HTTP 173 conversation, yet still refer to them as module-level globals in a 174 thread-safe way. 175 """ 176 177 request = _cprequest.Request(_httputil.Host("127.0.0.1", 80), 178 _httputil.Host("127.0.0.1", 1111)) 179 """ 180 The request object for the current thread. In the main thread, 181 and any threads which are not receiving HTTP requests, this is None.""" 182 183 response = _cprequest.Response() 184 """ 185 The response object for the current thread. In the main thread, 186 and any threads which are not receiving HTTP requests, this is None.""" 187
188 - def load(self, request, response):
189 self.request = request 190 self.response = response
191
192 - def clear(self):
193 """Remove all attributes of self.""" 194 self.__dict__.clear()
195 196 serving = _Serving() 197 198
199 -class _ThreadLocalProxy(object):
200 201 __slots__ = ['__attrname__', '__dict__'] 202
203 - def __init__(self, attrname):
204 self.__attrname__ = attrname
205
206 - def __getattr__(self, name):
207 child = getattr(serving, self.__attrname__) 208 return getattr(child, name)
209
210 - def __setattr__(self, name, value):
211 if name in ("__attrname__", ): 212 object.__setattr__(self, name, value) 213 else: 214 child = getattr(serving, self.__attrname__) 215 setattr(child, name, value)
216
217 - def __delattr__(self, name):
218 child = getattr(serving, self.__attrname__) 219 delattr(child, name)
220
221 - def _get_dict(self):
222 child = getattr(serving, self.__attrname__) 223 d = child.__class__.__dict__.copy() 224 d.update(child.__dict__) 225 return d
226 __dict__ = property(_get_dict) 227
228 - def __getitem__(self, key):
229 child = getattr(serving, self.__attrname__) 230 return child[key]
231
232 - def __setitem__(self, key, value):
233 child = getattr(serving, self.__attrname__) 234 child[key] = value
235
236 - def __delitem__(self, key):
237 child = getattr(serving, self.__attrname__) 238 del child[key]
239
240 - def __contains__(self, key):
241 child = getattr(serving, self.__attrname__) 242 return key in child
243
244 - def __len__(self):
245 child = getattr(serving, self.__attrname__) 246 return len(child)
247
248 - def __nonzero__(self):
249 child = getattr(serving, self.__attrname__) 250 return bool(child)
251 # Python 3 252 __bool__ = __nonzero__
253 254 # Create request and response object (the same objects will be used 255 # throughout the entire life of the webserver, but will redirect 256 # to the "serving" object) 257 request = _ThreadLocalProxy('request') 258 response = _ThreadLocalProxy('response') 259 260 # Create thread_data object as a thread-specific all-purpose storage
261 -class _ThreadData(_local):
262 """A container for thread-specific data."""
263 thread_data = _ThreadData() 264 265 266 # Monkeypatch pydoc to allow help() to go through the threadlocal proxy. 267 # Jan 2007: no Googleable examples of anyone else replacing pydoc.resolve. 268 # The only other way would be to change what is returned from type(request) 269 # and that's not possible in pure Python (you'd have to fake ob_type).
270 -def _cherrypy_pydoc_resolve(thing, forceload=0):
271 """Given an object or a path to an object, get the object and its name.""" 272 if isinstance(thing, _ThreadLocalProxy): 273 thing = getattr(serving, thing.__attrname__) 274 return _pydoc._builtin_resolve(thing, forceload)
275 276 try: 277 import pydoc as _pydoc 278 _pydoc._builtin_resolve = _pydoc.resolve 279 _pydoc.resolve = _cherrypy_pydoc_resolve 280 except ImportError: 281 pass 282 283 284 from cherrypy import _cplogging 285
286 -class _GlobalLogManager(_cplogging.LogManager):
287 """A site-wide LogManager; routes to app.log or global log as appropriate. 288 289 This :class:`LogManager<cherrypy._cplogging.LogManager>` implements 290 cherrypy.log() and cherrypy.log.access(). If either 291 function is called during a request, the message will be sent to the 292 logger for the current Application. If they are called outside of a 293 request, the message will be sent to the site-wide logger. 294 """ 295
296 - def __call__(self, *args, **kwargs):
297 """Log the given message to the app.log or global log as appropriate.""" 298 # Do NOT use try/except here. See http://www.cherrypy.org/ticket/945 299 if hasattr(request, 'app') and hasattr(request.app, 'log'): 300 log = request.app.log 301 else: 302 log = self 303 return log.error(*args, **kwargs)
304
305 - def access(self):
306 """Log an access message to the app.log or global log as appropriate.""" 307 try: 308 return request.app.log.access() 309 except AttributeError: 310 return _cplogging.LogManager.access(self)
311 312 313 log = _GlobalLogManager() 314 # Set a default screen handler on the global log. 315 log.screen = True 316 log.error_file = '' 317 # Using an access file makes CP about 10% slower. Leave off by default. 318 log.access_file = '' 319
320 -def _buslog(msg, level):
321 log.error(msg, 'ENGINE', severity=level)
322 engine.subscribe('log', _buslog) 323 324 # Helper functions for CP apps # 325 326
327 -def expose(func=None, alias=None):
328 """Expose the function, optionally providing an alias or set of aliases.""" 329 def expose_(func): 330 func.exposed = True 331 if alias is not None: 332 if isinstance(alias, basestring): 333 parents[alias.replace(".", "_")] = func 334 else: 335 for a in alias: 336 parents[a.replace(".", "_")] = func 337 return func
338 339 import sys, types 340 if isinstance(func, (types.FunctionType, types.MethodType)): 341 if alias is None: 342 # @expose 343 func.exposed = True 344 return func 345 else: 346 # func = expose(func, alias) 347 parents = sys._getframe(1).f_locals 348 return expose_(func) 349 elif func is None: 350 if alias is None: 351 # @expose() 352 parents = sys._getframe(1).f_locals 353 return expose_ 354 else: 355 # @expose(alias="alias") or 356 # @expose(alias=["alias1", "alias2"]) 357 parents = sys._getframe(1).f_locals 358 return expose_ 359 else: 360 # @expose("alias") or 361 # @expose(["alias1", "alias2"]) 362 parents = sys._getframe(1).f_locals 363 alias = func 364 return expose_ 365
366 -def popargs(*args, **kwargs):
367 """A decorator for _cp_dispatch 368 (cherrypy.dispatch.Dispatcher.dispatch_method_name). 369 370 Optional keyword argument: handler=(Object or Function) 371 372 Provides a _cp_dispatch function that pops off path segments into 373 cherrypy.request.params under the names specified. The dispatch 374 is then forwarded on to the next vpath element. 375 376 Note that any existing (and exposed) member function of the class that 377 popargs is applied to will override that value of the argument. For 378 instance, if you have a method named "list" on the class decorated with 379 popargs, then accessing "/list" will call that function instead of popping 380 it off as the requested parameter. This restriction applies to all 381 _cp_dispatch functions. The only way around this restriction is to create 382 a "blank class" whose only function is to provide _cp_dispatch. 383 384 If there are path elements after the arguments, or more arguments 385 are requested than are available in the vpath, then the 'handler' 386 keyword argument specifies the next object to handle the parameterized 387 request. If handler is not specified or is None, then self is used. 388 If handler is a function rather than an instance, then that function 389 will be called with the args specified and the return value from that 390 function used as the next object INSTEAD of adding the parameters to 391 cherrypy.request.args. 392 393 This decorator may be used in one of two ways: 394 395 As a class decorator: 396 @cherrypy.popargs('year', 'month', 'day') 397 class Blog: 398 def index(self, year=None, month=None, day=None): 399 #Process the parameters here; any url like 400 #/, /2009, /2009/12, or /2009/12/31 401 #will fill in the appropriate parameters. 402 403 def create(self): 404 #This link will still be available at /create. Defined functions 405 #take precedence over arguments. 406 407 Or as a member of a class: 408 class Blog: 409 _cp_dispatch = cherrypy.popargs('year', 'month', 'day') 410 #... 411 412 The handler argument may be used to mix arguments with built in functions. 413 For instance, the following setup allows different activities at the 414 day, month, and year level: 415 416 class DayHandler: 417 def index(self, year, month, day): 418 #Do something with this day; probably list entries 419 420 def delete(self, year, month, day): 421 #Delete all entries for this day 422 423 @cherrypy.popargs('day', handler=DayHandler()) 424 class MonthHandler: 425 def index(self, year, month): 426 #Do something with this month; probably list entries 427 428 def delete(self, year, month): 429 #Delete all entries for this month 430 431 @cherrypy.popargs('month', handler=MonthHandler()) 432 class YearHandler: 433 def index(self, year): 434 #Do something with this year 435 436 #... 437 438 @cherrypy.popargs('year', handler=YearHandler()) 439 class Root: 440 def index(self): 441 #... 442 443 """ 444 445 #Since keyword arg comes after *args, we have to process it ourselves 446 #for lower versions of python. 447 448 handler = None 449 handler_call = False 450 for k,v in kwargs.items(): 451 if k == 'handler': 452 handler = v 453 else: 454 raise TypeError( 455 "cherrypy.popargs() got an unexpected keyword argument '{0}'" \ 456 .format(k) 457 ) 458 459 import inspect 460 461 if handler is not None \ 462 and (hasattr(handler, '__call__') or inspect.isclass(handler)): 463 handler_call = True 464 465 def decorated(cls_or_self=None, vpath=None): 466 if inspect.isclass(cls_or_self): 467 #cherrypy.popargs is a class decorator 468 cls = cls_or_self 469 setattr(cls, dispatch.Dispatcher.dispatch_method_name, decorated) 470 return cls 471 472 #We're in the actual function 473 self = cls_or_self 474 parms = {} 475 for arg in args: 476 if not vpath: 477 break 478 parms[arg] = vpath.pop(0) 479 480 if handler is not None: 481 if handler_call: 482 return handler(**parms) 483 else: 484 request.params.update(parms) 485 return handler 486 487 request.params.update(parms) 488 489 #If we are the ultimate handler, then to prevent our _cp_dispatch 490 #from being called again, we will resolve remaining elements through 491 #getattr() directly. 492 if vpath: 493 return getattr(self, vpath.pop(0), None) 494 else: 495 return self
496 497 return decorated 498
499 -def url(path="", qs="", script_name=None, base=None, relative=None):
500 """Create an absolute URL for the given path. 501 502 If 'path' starts with a slash ('/'), this will return 503 (base + script_name + path + qs). 504 If it does not start with a slash, this returns 505 (base + script_name [+ request.path_info] + path + qs). 506 507 If script_name is None, cherrypy.request will be used 508 to find a script_name, if available. 509 510 If base is None, cherrypy.request.base will be used (if available). 511 Note that you can use cherrypy.tools.proxy to change this. 512 513 Finally, note that this function can be used to obtain an absolute URL 514 for the current request path (minus the querystring) by passing no args. 515 If you call url(qs=cherrypy.request.query_string), you should get the 516 original browser URL (assuming no internal redirections). 517 518 If relative is None or not provided, request.app.relative_urls will 519 be used (if available, else False). If False, the output will be an 520 absolute URL (including the scheme, host, vhost, and script_name). 521 If True, the output will instead be a URL that is relative to the 522 current request path, perhaps including '..' atoms. If relative is 523 the string 'server', the output will instead be a URL that is 524 relative to the server root; i.e., it will start with a slash. 525 """ 526 if isinstance(qs, (tuple, list, dict)): 527 qs = _urlencode(qs) 528 if qs: 529 qs = '?' + qs 530 531 if request.app: 532 if not path.startswith("/"): 533 # Append/remove trailing slash from path_info as needed 534 # (this is to support mistyped URL's without redirecting; 535 # if you want to redirect, use tools.trailing_slash). 536 pi = request.path_info 537 if request.is_index is True: 538 if not pi.endswith('/'): 539 pi = pi + '/' 540 elif request.is_index is False: 541 if pi.endswith('/') and pi != '/': 542 pi = pi[:-1] 543 544 if path == "": 545 path = pi 546 else: 547 path = _urljoin(pi, path) 548 549 if script_name is None: 550 script_name = request.script_name 551 if base is None: 552 base = request.base 553 554 newurl = base + script_name + path + qs 555 else: 556 # No request.app (we're being called outside a request). 557 # We'll have to guess the base from server.* attributes. 558 # This will produce very different results from the above 559 # if you're using vhosts or tools.proxy. 560 if base is None: 561 base = server.base() 562 563 path = (script_name or "") + path 564 newurl = base + path + qs 565 566 if './' in newurl: 567 # Normalize the URL by removing ./ and ../ 568 atoms = [] 569 for atom in newurl.split('/'): 570 if atom == '.': 571 pass 572 elif atom == '..': 573 atoms.pop() 574 else: 575 atoms.append(atom) 576 newurl = '/'.join(atoms) 577 578 # At this point, we should have a fully-qualified absolute URL. 579 580 if relative is None: 581 relative = getattr(request.app, "relative_urls", False) 582 583 # See http://www.ietf.org/rfc/rfc2396.txt 584 if relative == 'server': 585 # "A relative reference beginning with a single slash character is 586 # termed an absolute-path reference, as defined by <abs_path>..." 587 # This is also sometimes called "server-relative". 588 newurl = '/' + '/'.join(newurl.split('/', 3)[3:]) 589 elif relative: 590 # "A relative reference that does not begin with a scheme name 591 # or a slash character is termed a relative-path reference." 592 old = url(relative=False).split('/')[:-1] 593 new = newurl.split('/') 594 while old and new: 595 a, b = old[0], new[0] 596 if a != b: 597 break 598 old.pop(0) 599 new.pop(0) 600 new = (['..'] * len(old)) + new 601 newurl = '/'.join(new) 602 603 return newurl
604 605 606 # import _cpconfig last so it can reference other top-level objects 607 from cherrypy import _cpconfig 608 # Use _global_conf_alias so quickstart can use 'config' as an arg 609 # without shadowing cherrypy.config. 610 config = _global_conf_alias = _cpconfig.Config() 611 config.defaults = { 612 'tools.log_tracebacks.on': True, 613 'tools.log_headers.on': True, 614 'tools.trailing_slash.on': True, 615 'tools.encode.on': True 616 } 617 config.namespaces["log"] = lambda k, v: setattr(log, k, v) 618 config.namespaces["checker"] = lambda k, v: setattr(checker, k, v) 619 # Must reset to get our defaults applied. 620 config.reset() 621 622 from cherrypy import _cpchecker 623 checker = _cpchecker.Checker() 624 engine.subscribe('start', checker) 625