1
"""CherryPy is a pythonic, object-oriented HTTP framework.
4
CherryPy consists of not one, but four separate API layers.
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:
14
* the 'exposed' attribute (and cherrypy.expose)
15
* cherrypy.quickstart()
16
* _cp_config attributes
17
* cherrypy.tools (including cherrypy.session)
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:
30
* cherrypy.thread_data
32
* cherrypy.HTTPError, NotFound, and HTTPRedirect
35
The EXTENSION LAYER allows advanced users to construct and share their own
36
plugins. It consists of:
42
* Config Namespace API
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:
56
These API's are described in the CherryPy specification:
57
http://www.cherrypy.org/wiki/CherryPySpec
60
__version__ = "3.2.0rc1"
62
from urlparse import urljoin as _urljoin
63
from urllib import urlencode as _urlencode
66
class _AttributeDocstrings(type):
67
"""Metaclass for declaring docstrings for class attributes."""
68
# The full docstring for this type is down in the __init__ method so
69
# that it doesn't show up in help() for every consumer class.
71
def __init__(cls, name, bases, dct):
72
'''Metaclass for declaring docstrings for class attributes.
74
Base Python doesn't provide any syntax for setting docstrings on
75
'data attributes' (non-callables). This metaclass allows class
76
definitions to follow the declaration of a data attribute with
77
a docstring for that attribute; the attribute docstring will be
78
popped from the class dict and folded into the class docstring.
80
The naming convention for attribute docstrings is:
85
"""A thing and its properties."""
87
__metaclass__ = cherrypy._AttributeDocstrings
90
height__doc = """The height of the Thing in inches."""
92
In which case, help(Thing) starts like this:
95
Help on class Thing in module pkg.mod:
97
class Thing(__builtin__.object)
98
| A thing and its properties.
101
| The height of the Thing in inches.
104
The benefits of this approach over hand-edited class docstrings:
105
1. Places the docstring nearer to the attribute declaration.
106
2. Makes attribute docs more uniform ("name (default): doc").
107
3. Reduces mismatches of attribute _names_ between
108
the declaration and the documentation.
109
4. Reduces mismatches of attribute default _values_ between
110
the declaration and the documentation.
112
The benefits of a metaclass approach over other approaches:
113
1. Simpler ("less magic") than interface-based solutions.
114
2. __metaclass__ can be specified at the module global level
117
For various formatting reasons, you should write multiline docs
118
with a leading newline and not a trailing one:
121
The response object for the current thread. In the main thread,
122
and any threads which are not HTTP requests, this is None."""
124
The type of the attribute is intentionally not included, because
125
that's not How Python Works. Quack.
128
newdoc = [cls.__doc__ or ""]
133
if name.endswith("__doc"):
134
# Remove the magic doc attribute.
135
if hasattr(cls, name):
138
# Make a uniformly-indented docstring from it.
139
val = '\n'.join([' ' + line.strip()
140
for line in dct[name].split('\n')])
142
# Get the default value.
145
attrval = getattr(cls, attrname)
146
except AttributeError:
149
# Add the complete attribute docstring to our list.
150
newdoc.append("%s [= %r]:\n%s" % (attrname, attrval, val))
152
# Add our list of new docstrings to the class docstring.
153
cls.__doc__ = "\n\n".join(newdoc)
156
from cherrypy._cperror import HTTPError, HTTPRedirect, InternalRedirect
157
from cherrypy._cperror import NotFound, CherryPyException, TimeoutError
159
from cherrypy import _cpdispatch as dispatch
161
from cherrypy import _cptools
162
tools = _cptools.default_toolbox
165
from cherrypy import _cprequest
166
from cherrypy.lib import httputil as _httputil
168
from cherrypy import _cptree
169
tree = _cptree.Tree()
170
from cherrypy._cptree import Application
171
from cherrypy import _cpwsgi as wsgi
173
from cherrypy import process
175
from cherrypy.process import win32
176
engine = win32.Win32Bus()
177
engine.console_control_handler = win32.ConsoleCtrlHandler(engine)
184
class _TimeoutMonitor(process.plugins.Monitor):
186
def __init__(self, bus):
188
process.plugins.Monitor.__init__(self, bus, self.run)
191
self.servings.append((serving.request, serving.response))
195
self.servings.remove((serving.request, serving.response))
200
"""Check timeout on all responses. (Internal)"""
201
for req, resp in self.servings:
203
engine.timeout_monitor = _TimeoutMonitor(engine)
204
engine.timeout_monitor.subscribe()
206
engine.autoreload = process.plugins.Autoreloader(engine)
207
engine.autoreload.subscribe()
209
engine.thread_manager = process.plugins.ThreadManager(engine)
210
engine.thread_manager.subscribe()
212
engine.signal_handler = process.plugins.SignalHandler(engine)
215
from cherrypy import _cpserver
216
server = _cpserver.Server()
220
def quickstart(root=None, script_name="", config=None):
221
"""Mount the given root, start the builtin server (and engine), then block.
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".
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
238
_global_conf_alias.update(config)
240
tree.mount(root, script_name, config)
242
if hasattr(engine, "signal_handler"):
243
engine.signal_handler.subscribe()
244
if hasattr(engine, "console_control_handler"):
245
engine.console_control_handler.subscribe()
252
from threading import local as _local
254
from cherrypy._cpthreadinglocal import local as _local
256
class _Serving(_local):
257
"""An interface for registering request and response objects.
259
Rather than have a separate "thread local" object for the request and
260
the response, this class works as a single threadlocal container for
261
both objects (and any others which developers wish to define). In this
262
way, we can easily dump those objects when we stop/start a new HTTP
263
conversation, yet still refer to them as module-level globals in a
267
__metaclass__ = _AttributeDocstrings
269
request = _cprequest.Request(_httputil.Host("127.0.0.1", 80),
270
_httputil.Host("127.0.0.1", 1111))
272
The request object for the current thread. In the main thread,
273
and any threads which are not receiving HTTP requests, this is None."""
275
response = _cprequest.Response()
277
The response object for the current thread. In the main thread,
278
and any threads which are not receiving HTTP requests, this is None."""
280
def load(self, request, response):
281
self.request = request
282
self.response = response
285
"""Remove all attributes of self."""
286
self.__dict__.clear()
291
class _ThreadLocalProxy(object):
293
__slots__ = ['__attrname__', '__dict__']
295
def __init__(self, attrname):
296
self.__attrname__ = attrname
298
def __getattr__(self, name):
299
child = getattr(serving, self.__attrname__)
300
return getattr(child, name)
302
def __setattr__(self, name, value):
303
if name in ("__attrname__", ):
304
object.__setattr__(self, name, value)
306
child = getattr(serving, self.__attrname__)
307
setattr(child, name, value)
309
def __delattr__(self, name):
310
child = getattr(serving, self.__attrname__)
314
child = getattr(serving, self.__attrname__)
315
d = child.__class__.__dict__.copy()
316
d.update(child.__dict__)
318
__dict__ = property(_get_dict)
320
def __getitem__(self, key):
321
child = getattr(serving, self.__attrname__)
324
def __setitem__(self, key, value):
325
child = getattr(serving, self.__attrname__)
328
def __delitem__(self, key):
329
child = getattr(serving, self.__attrname__)
332
def __contains__(self, key):
333
child = getattr(serving, self.__attrname__)
337
child = getattr(serving, self.__attrname__)
340
def __nonzero__(self):
341
child = getattr(serving, self.__attrname__)
345
# Create request and response object (the same objects will be used
346
# throughout the entire life of the webserver, but will redirect
347
# to the "serving" object)
348
request = _ThreadLocalProxy('request')
349
response = _ThreadLocalProxy('response')
351
# Create thread_data object as a thread-specific all-purpose storage
352
class _ThreadData(_local):
353
"""A container for thread-specific data."""
354
thread_data = _ThreadData()
357
# Monkeypatch pydoc to allow help() to go through the threadlocal proxy.
358
# Jan 2007: no Googleable examples of anyone else replacing pydoc.resolve.
359
# The only other way would be to change what is returned from type(request)
360
# and that's not possible in pure Python (you'd have to fake ob_type).
361
def _cherrypy_pydoc_resolve(thing, forceload=0):
362
"""Given an object or a path to an object, get the object and its name."""
363
if isinstance(thing, _ThreadLocalProxy):
364
thing = getattr(serving, thing.__attrname__)
365
return _pydoc._builtin_resolve(thing, forceload)
368
import pydoc as _pydoc
369
_pydoc._builtin_resolve = _pydoc.resolve
370
_pydoc.resolve = _cherrypy_pydoc_resolve
375
from cherrypy import _cplogging
377
class _GlobalLogManager(_cplogging.LogManager):
379
def __call__(self, *args, **kwargs):
380
# Do NOT use try/except here. See http://www.cherrypy.org/ticket/945
381
if hasattr(request, 'app') and hasattr(request.app, 'log'):
382
log = request.app.log
385
return log.error(*args, **kwargs)
389
return request.app.log.access()
390
except AttributeError:
391
return _cplogging.LogManager.access(self)
394
log = _GlobalLogManager()
395
# Set a default screen handler on the global log.
398
# Using an access file makes CP about 10% slower. Leave off by default.
401
def _buslog(msg, level):
402
log.error(msg, 'ENGINE', severity=level)
403
engine.subscribe('log', _buslog)
405
# Helper functions for CP apps #
408
def expose(func=None, alias=None):
409
"""Expose the function, optionally providing an alias or set of aliases."""
412
if alias is not None:
413
if isinstance(alias, basestring):
414
parents[alias.replace(".", "_")] = func
417
parents[a.replace(".", "_")] = func
421
if isinstance(func, (types.FunctionType, types.MethodType)):
427
# func = expose(func, alias)
428
parents = sys._getframe(1).f_locals
433
parents = sys._getframe(1).f_locals
436
# @expose(alias="alias") or
437
# @expose(alias=["alias1", "alias2"])
438
parents = sys._getframe(1).f_locals
441
# @expose("alias") or
442
# @expose(["alias1", "alias2"])
443
parents = sys._getframe(1).f_locals
448
def url(path="", qs="", script_name=None, base=None, relative=None):
449
"""Create an absolute URL for the given path.
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).
456
If script_name is None, cherrypy.request will be used
457
to find a script_name, if available.
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.
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).
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.
475
if isinstance(qs, (tuple, list, dict)):
481
if not path.startswith("/"):
482
# Append/remove trailing slash from path_info as needed
483
# (this is to support mistyped URL's without redirecting;
484
# if you want to redirect, use tools.trailing_slash).
485
pi = request.path_info
486
if request.is_index is True:
487
if not pi.endswith('/'):
489
elif request.is_index is False:
490
if pi.endswith('/') and pi != '/':
496
path = _urljoin(pi, path)
498
if script_name is None:
499
script_name = request.script_name
503
newurl = base + script_name + path + qs
505
# No request.app (we're being called outside a request).
506
# We'll have to guess the base from server.* attributes.
507
# This will produce very different results from the above
508
# if you're using vhosts or tools.proxy.
512
path = (script_name or "") + path
513
newurl = base + path + qs
516
# Normalize the URL by removing ./ and ../
518
for atom in newurl.split('/'):
525
newurl = '/'.join(atoms)
527
# At this point, we should have a fully-qualified absolute URL.
530
relative = getattr(request.app, "relative_urls", False)
532
# See http://www.ietf.org/rfc/rfc2396.txt
533
if relative == 'server':
534
# "A relative reference beginning with a single slash character is
535
# termed an absolute-path reference, as defined by <abs_path>..."
536
# This is also sometimes called "server-relative".
537
newurl = '/' + '/'.join(newurl.split('/', 3)[3:])
539
# "A relative reference that does not begin with a scheme name
540
# or a slash character is termed a relative-path reference."
541
old = url().split('/')[:-1]
542
new = newurl.split('/')
544
a, b = old[0], new[0]
549
new = (['..'] * len(old)) + new
550
newurl = '/'.join(new)
555
# import _cpconfig last so it can reference other top-level objects
556
from cherrypy import _cpconfig
557
# Use _global_conf_alias so quickstart can use 'config' as an arg
558
# without shadowing cherrypy.config.
559
config = _global_conf_alias = _cpconfig.Config()
561
'tools.log_tracebacks.on': True,
562
'tools.log_headers.on': True,
563
'tools.trailing_slash.on': True,
564
'tools.encode.on': True
566
config.namespaces["log"] = lambda k, v: setattr(log, k, v)
567
config.namespaces["checker"] = lambda k, v: setattr(checker, k, v)
568
# Must reset to get our defaults applied.
571
from cherrypy import _cpchecker
572
checker = _cpchecker.Checker()
573
engine.subscribe('start', checker)