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
66 """Metaclass for declaring docstrings for class attributes."""
67
68
69
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
135 if hasattr(cls, name):
136 delattr(cls, name)
137
138
139 val = '\n'.join([' ' + line.strip()
140 for line in dct[name].split('\n')])
141
142
143 attrname = name[:-5]
144 try:
145 attrval = getattr(cls, attrname)
146 except AttributeError:
147 attrval = "missing"
148
149
150 newdoc.append("%s [= %r]:\n%s" % (attrname, attrval, val))
151
152
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
185
189
192
198
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
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):
284
286 """Remove all attributes of self."""
287 self.__dict__.clear()
288
289 serving = _Serving()
290
291
293
294 __slots__ = ['__attrname__', '__dict__']
295
298
302
309
313
319 __dict__ = property(_get_dict)
320
324
328
332
336
340
344
345
346
347
348
349 request = _ThreadLocalProxy('request')
350 response = _ThreadLocalProxy('response')
351
352
354 """A container for thread-specific data."""
355 thread_data = _ThreadData()
356
357
358
359
360
361
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
392
393
394 log = _GlobalLogManager()
395
396 log.screen = True
397 log.error_file = ''
398
399 log.access_file = ''
400
402 log.error(msg, 'ENGINE', severity=level)
403 engine.subscribe('log', _buslog)
404
405
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
424 func.exposed = True
425 return func
426 else:
427
428 parents = sys._getframe(1).f_locals
429 return expose_(func)
430 elif func is None:
431 if alias is None:
432
433 parents = sys._getframe(1).f_locals
434 return expose_
435 else:
436
437
438 parents = sys._getframe(1).f_locals
439 return expose_
440 else:
441
442
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
481
482
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
504
505
506
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
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
526
527 if relative is None:
528 relative = getattr(request.app, "relative_urls", False)
529
530
531 if relative == 'server':
532
533
534
535 newurl = '/' + '/'.join(newurl.split('/', 3)[3:])
536 elif relative:
537
538
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
554 from cherrypy import _cpconfig
555
556
557 config = _global_conf_alias = _cpconfig.Config()
558
559 from cherrypy import _cpchecker
560 checker = _cpchecker.Checker()
561 engine.subscribe('start', checker)
562