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