Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

root/trunk/cherrypy/__init__.py

Revision 2007 (checked in by fumanchu, 2 months ago)

Marking CP 3.1.0 final.

  • Property svn:eol-style set to native
Line 
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.0"
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
342 # Create request and response object (the same objects will be used
343 #   throughout the entire life of the webserver, but will redirect
344 #   to the "serving" object)
345 request = _ThreadLocalProxy('request')
346 response = _ThreadLocalProxy('response')
347
348 # Create thread_data object as a thread-specific all-purpose storage
349 class _ThreadData(_local):
350     """A container for thread-specific data."""
351 thread_data = _ThreadData()
352
353
354 # Monkeypatch pydoc to allow help() to go through the threadlocal proxy.
355 # Jan 2007: no Googleable examples of anyone else replacing pydoc.resolve.
356 # The only other way would be to change what is returned from type(request)
357 # and that's not possible in pure Python (you'd have to fake ob_type).
358 def _cherrypy_pydoc_resolve(thing, forceload=0):
359     """Given an object or a path to an object, get the object and its name."""
360     if isinstance(thing, _ThreadLocalProxy):
361         thing = getattr(serving, thing.__attrname__)
362     return _pydoc._builtin_resolve(thing, forceload)
363
364 try:
365     import pydoc as _pydoc
366     _pydoc._builtin_resolve = _pydoc.resolve
367     _pydoc.resolve = _cherrypy_pydoc_resolve
368 except ImportError:
369     pass
370
371
372 from cherrypy import _cplogging
373
374 class _GlobalLogManager(_cplogging.LogManager):
375    
376     def __call__(self, *args, **kwargs):
377         try:
378             log = request.app.log
379         except AttributeError:
380             log = self
381         return log.error(*args, **kwargs)
382    
383     def access(self):
384         try:
385             return request.app.log.access()
386         except AttributeError:
387             return _cplogging.LogManager.access(self)
388
389
390 log = _GlobalLogManager()
391 # Set a default screen handler on the global log.
392 log.screen = True
393 log.error_file = ''
394 # Using an access file makes CP about 10% slower. Leave off by default.
395 log.access_file = ''
396
397 def _buslog(msg, level):
398     log.error(msg, 'ENGINE', severity=level)
399 engine.subscribe('log', _buslog)
400
401 #                       Helper functions for CP apps                       #
402
403
404 def expose(func=None, alias=None):
405     """Expose the function, optionally providing an alias or set of aliases."""
406     def expose_(func):
407         func.exposed = True
408         if alias is not None:
409             if isinstance(alias, basestring):
410                 parents[alias.replace(".", "_")] = func
411             else:
412                 for a in alias:
413                     parents[a.replace(".", "_")] = func
414         return func
415    
416     import sys, types
417     if isinstance(func, (types.FunctionType, types.MethodType)):
418         if alias is None:
419             # @expose
420             func.exposed = True
421             return func
422         else:
423             # func = expose(func, alias)
424             parents = sys._getframe(1).f_locals
425             return expose_(func)
426     elif func is None:
427         if alias is None:
428             # @expose()
429             parents = sys._getframe(1).f_locals
430             return expose_
431         else:
432             # @expose(alias="alias") or
433             # @expose(alias=["alias1", "alias2"])
434             parents = sys._getframe(1).f_locals
435             return expose_
436     else:
437         # @expose("alias") or
438         # @expose(["alias1", "alias2"])
439         parents = sys._getframe(1).f_locals
440         alias = func
441         return expose_
442
443
444 def url(path="", qs="", script_name=None, base=None, relative=None):
445     """Create an absolute URL for the given path.
446     
447     If 'path' starts with a slash ('/'), this will return
448         (base + script_name + path + qs).
449     If it does not start with a slash, this returns
450         (base + script_name [+ request.path_info] + path + qs).
451     
452     If script_name is None, cherrypy.request will be used
453     to find a script_name, if available.
454     
455     If base is None, cherrypy.request.base will be used (if available).
456     Note that you can use cherrypy.tools.proxy to change this.
457     
458     Finally, note that this function can be used to obtain an absolute URL
459     for the current request path (minus the querystring) by passing no args.
460     If you call url(qs=cherrypy.request.query_string), you should get the
461     original browser URL (assuming no internal redirections).
462     
463     If relative is None or not provided, request.app.relative_urls will
464     be used (if available, else False). If False, the output will be an
465     absolute URL (including the scheme, host, vhost, and script_name).
466     If True, the output will instead be a URL that is relative to the
467     current request path, perhaps including '..' atoms. If relative is
468     the string 'server', the output will instead be a URL that is
469     relative to the server root; i.e., it will start with a slash.
470     """
471     if qs:
472         qs = '?' + qs
473    
474     if request.app:
475         if not path.startswith("/"):
476             # Append/remove trailing slash from path_info as needed
477             # (this is to support mistyped URL's without redirecting;
478             # if you want to redirect, use tools.trailing_slash).
479             pi = request.path_info
480             if request.is_index is True:
481                 if not pi.endswith('/'):
482                     pi = pi + '/'
483             elif request.is_index is False:
484                 if pi.endswith('/') and pi != '/':
485                     pi = pi[:-1]
486            
487             if path == "":
488                 path = pi
489             else:
490                 path = _urljoin(pi, path)
491        
492         if script_name is None:
493             script_name = request.script_name
494         if base is None:
495             base = request.base
496        
497         newurl = base + script_name + path + qs
498     else:
499         # No request.app (we're being called outside a request).
500         # We'll have to guess the base from server.* attributes.
501         # This will produce very different results from the above
502         # if you're using vhosts or tools.proxy.
503         if base is None:
504             base = server.base()
505        
506         path = (script_name or "") + path
507         newurl = base + path + qs
508    
509     if './' in newurl:
510         # Normalize the URL by removing ./ and ../
511         atoms = []
512         for atom in newurl.split('/'):
513             if atom == '.':
514                 pass
515             elif atom == '..':
516                 atoms.pop()
517             else:
518                 atoms.append(atom)
519         newurl = '/'.join(atoms)
520    
521     # At this point, we should have a fully-qualified absolute URL.
522    
523     if relative is None:
524         relative = getattr(request.app, "relative_urls", False)
525    
526     # See http://www.ietf.org/rfc/rfc2396.txt
527     if relative == 'server':
528         # "A relative reference beginning with a single slash character is
529         # termed an absolute-path reference, as defined by <abs_path>..."
530         # This is also sometimes called "server-relative".
531         newurl = '/' + '/'.join(newurl.split('/', 3)[3:])
532     elif relative:
533         # "A relative reference that does not begin with a scheme name
534         # or a slash character is termed a relative-path reference."
535         old = url().split('/')[:-1]
536         new = newurl.split('/')
537         while old and new:
538             a, b = old[0], new[0]
539             if a != b:
540                 break
541             old.pop(0)
542             new.pop(0)
543         new = (['..'] * len(old)) + new
544         newurl = '/'.join(new)
545    
546     return newurl
547
548
549 # import _cpconfig last so it can reference other top-level objects
550 from cherrypy import _cpconfig
551 # Use _global_conf_alias so quickstart can use 'config' as an arg
552 # without shadowing cherrypy.config.
553 config = _global_conf_alias = _cpconfig.Config()
554
555 from cherrypy import _cpchecker
556 checker = _cpchecker.Checker()
557 engine.subscribe('start', checker)
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets