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

root/tags/cherrypy-3.0.3/cherrypy/__init__.py

Revision 1843 (checked in by dowski, 10 months ago)

Updated version numbers in preparation for 3.0.3 release.

  • 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.0.3"
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: <attrname> + "__doc".
80         For example:
81         
82             class Thing(object):
83                 """A thing and its properties."""
84                 
85                 __metaclass__ = cherrypy._AttributeDocstrings
86                 
87                 height = 50
88                 height__doc = """The height of the Thing in inches."""
89         
90         In which case, help(Thing) starts like this:
91         
92             >>> help(mod.Thing)
93             Help on class Thing in module pkg.mod:
94             
95             class Thing(__builtin__.object)
96              |  A thing and its properties.
97              | 
98              |  height [= 50]:
99              |      The height of the Thing in inches.
100              |
101         
102         The benefits of this approach over hand-edited class docstrings:
103             1. Places the docstring nearer to the attribute declaration.
104             2. Makes attribute docs more uniform ("name (default): doc").
105             3. Reduces mismatches of attribute _names_ between
106                the declaration and the documentation.
107             4. Reduces mismatches of attribute default _values_ between
108                the declaration and the documentation.
109         
110         The benefits of a metaclass approach over other approaches:
111             1. Simpler ("less magic") than interface-based solutions.
112             2. __metaclass__ can be specified at the module global level
113                for classic classes.
114         
115         The type of the attribute is intentionally not included, because
116         that's not How Python Works. Quack.
117         '''
118        
119         newdoc = [cls.__doc__ or ""]
120        
121         dctnames = dct.keys()
122         dctnames.sort()
123        
124         for name in dctnames:
125             if name.endswith("__doc"):
126                 # Remove the magic doc attribute.
127                 if hasattr(cls, name):
128                     delattr(cls, name)
129                
130                 # Get an inspect-style docstring if possible (usually so).
131                 val = dct[name]
132                 try:
133                     import inspect
134                     val = inspect.getdoc(property(doc=val)).strip()
135                 except:
136                     pass
137                
138                 # Indent the docstring.
139                 val = '\n'.join(['    ' + line.rstrip()
140                                  for line in val.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 from cherrypy import _cprequest
161 from cherrypy.lib import http as _http
162 from cherrypy import _cpengine
163 engine = _cpengine.Engine()
164
165 from cherrypy import _cptools
166 tools = _cptools.default_toolbox
167 Tool = _cptools.Tool
168
169 from cherrypy import _cptree
170 tree = _cptree.Tree()
171 from cherrypy._cptree import Application
172 from cherrypy import _cpwsgi as wsgi
173 from cherrypy import _cpserver
174 server = _cpserver.Server()
175
176 def quickstart(root, script_name="", config=None):
177     """Mount the given root, start the engine and builtin server, then block."""
178     if config:
179         _global_conf_alias.update(config)
180     tree.mount(root, script_name, config)
181     server.quickstart()
182     engine.start()
183
184
185 try:
186     from threading import local as _local
187 except ImportError:
188     from cherrypy._cpthreadinglocal import local as _local
189
190 class _Serving(_local):
191     """An interface for registering request and response objects.
192     
193     Rather than have a separate "thread local" object for the request and
194     the response, this class works as a single threadlocal container for
195     both objects (and any others which developers wish to define). In this
196     way, we can easily dump those objects when we stop/start a new HTTP
197     conversation, yet still refer to them as module-level globals in a
198     thread-safe way.
199     """
200    
201     __metaclass__ = _AttributeDocstrings
202    
203     request = _cprequest.Request(_http.Host("localhost", 80),
204                                  _http.Host("localhost", 1111))
205     request__doc = """
206     The request object for the current thread. In the main thread,
207     and any threads which are not receiving HTTP requests, this is None."""
208    
209     response = _cprequest.Response()
210     response__doc = """
211     The response object for the current thread. In the main thread,
212     and any threads which are not receiving HTTP requests, this is None."""
213    
214     def load(self, request, response):
215         self.request = request
216         self.response = response
217    
218     def clear(self):
219         """Remove all attributes of self."""
220         self.__dict__.clear()
221
222 # The name "_serving" should be removed in 3.1.
223 serving = _serving = _Serving()
224
225
226 class _ThreadLocalProxy(object):
227    
228     __slots__ = ['__attrname__', '__dict__']
229    
230     def __init__(self, attrname):
231         self.__attrname__ = attrname
232    
233     def __getattr__(self, name):
234         child = getattr(serving, self.__attrname__)
235         return getattr(child, name)
236    
237     def __setattr__(self, name, value):
238         if name in ("__attrname__", ):
239             object.__setattr__(self, name, value)
240         else:
241             child = getattr(serving, self.__attrname__)
242             setattr(child, name, value)
243    
244     def __delattr__(self, name):
245         child = getattr(serving, self.__attrname__)
246         delattr(child, name)
247    
248     def _get_dict(self):
249         child = getattr(serving, self.__attrname__)
250         d = child.__class__.__dict__.copy()
251         d.update(child.__dict__)
252         return d
253     __dict__ = property(_get_dict)
254    
255     def __getitem__(self, key):
256         child = getattr(serving, self.__attrname__)
257         return child[key]
258    
259     def __setitem__(self, key, value):
260         child = getattr(serving, self.__attrname__)
261         child[key] = value
262    
263     def __delitem__(self, key):
264         child = getattr(serving, self.__attrname__)
265         del child[key]
266    
267     def __contains__(self, key):
268         child = getattr(serving, self.__attrname__)
269         return key in child
270
271
272 # Create request and response object (the same objects will be used
273 #   throughout the entire life of the webserver, but will redirect
274 #   to the "serving" object)
275 request = _ThreadLocalProxy('request')
276 response = _ThreadLocalProxy('response')
277
278 # Create thread_data object as a thread-specific all-purpose storage
279 class _ThreadData(_local):
280     """A container for thread-specific data."""
281 thread_data = _ThreadData()
282
283
284 # Monkeypatch pydoc to allow help() to go through the threadlocal proxy.
285 # Jan 2007: no Googleable examples of anyone else replacing pydoc.resolve.
286 # The only other way would be to change what is returned from type(request)
287 # and that's not possible in pure Python (you'd have to fake ob_type).
288 def _cherrypy_pydoc_resolve(thing, forceload=0):
289     """Given an object or a path to an object, get the object and its name."""
290     if isinstance(thing, _ThreadLocalProxy):
291         thing = getattr(serving, thing.__attrname__)
292     return pydoc._builtin_resolve(thing, forceload)
293
294 try:
295     import pydoc
296     pydoc._builtin_resolve = pydoc.resolve
297     pydoc.resolve = _cherrypy_pydoc_resolve
298 except ImportError:
299     pass
300
301
302 from cherrypy import _cplogging
303
304 class _GlobalLogManager(_cplogging.LogManager):
305    
306     def __call__(self, *args, **kwargs):
307         try:
308             log = request.app.log
309         except AttributeError:
310             log = self
311         return log.error(*args, **kwargs)
312    
313     def access(self):
314         try:
315             return request.app.log.access()
316         except AttributeError:
317             return _cplogging.LogManager.access(self)
318
319
320 log = _GlobalLogManager()
321 # Set a default screen handler on the global log.
322 log.screen = True
323 log.error_file = ''
324 # Using an access file makes CP about 10% slower. Leave off by default.
325 log.access_file = ''
326
327
328 #                       Helper functions for CP apps                       #
329
330
331 def expose(func=None, alias=None):
332     """Expose the function, optionally providing an alias or set of aliases."""
333    
334     def expose_(func):
335         func.exposed = True
336         if alias is not None:
337             if isinstance(alias, basestring):
338                 parents[alias.replace(".", "_")] = func
339             else:
340                 for a in alias:
341                     parents[a.replace(".", "_")] = func
342         return func
343    
344     import sys, types
345     if isinstance(func, (types.FunctionType, types.MethodType)):
346         # expose is being called directly, before the method has been bound
347         parents = sys._getframe(1).f_locals
348         return expose_(func)
349     else:
350         if alias is None:
351             # expose is being called as a decorator "@expose"
352             func.exposed = True
353             return func
354         else:
355             # expose is returning a decorator "@expose(alias=...)"
356             parents = sys._getframe(1).f_locals
357             return expose_
358
359 def url(path="", qs="", script_name=None, base=None, relative=False):
360     """Create an absolute URL for the given path.
361     
362     If 'path' starts with a slash ('/'), this will return
363         (base + script_name + path + qs).
364     If it does not start with a slash, this returns
365         (base + script_name [+ request.path_info] + path + qs).
366     
367     If script_name is None, cherrypy.request will be used
368     to find a script_name, if available.
369     
370     If base is None, cherrypy.request.base will be used (if available).
371     Note that you can use cherrypy.tools.proxy to change this.
372     
373     Finally, note that this function can be used to obtain an absolute URL
374     for the current request path (minus the querystring) by passing no args.
375     If you call url(qs=cherrypy.request.query_string), you should get the
376     original browser URL (assuming no Internal redirections).
377     
378     If relative is False (the default), the output will be an absolute URL
379     (usually including the scheme, host, vhost, and script_name).
380     If relative is True, the output will instead be a URL that is relative
381     to the current request path, perhaps including '..' atoms.
382     """
383     if qs:
384         qs = '?' + qs
385    
386     if request.app:
387         if not path.startswith("/"):
388             # Append/remove trailing slash from path_info as needed
389             # (this is to support mistyped URL's without redirecting;
390             # if you want to redirect, use tools.trailing_slash).
391             pi = request.path_info
392             if request.is_index is True:
393                 if not pi.endswith('/'):
394                     pi = pi + '/'
395             elif request.is_index is False:
396                 if pi.endswith('/') and pi != '/':
397                     pi = pi[:-1]
398            
399             if path == "":
400                 path = pi
401             else:
402                 path = _urljoin(pi, path)
403        
404         if script_name is None:
405             script_name = request.app.script_name
406         if base is None:
407             base = request.base
408        
409         newurl = base + script_name + path + qs
410     else:
411         # No request.app (we're being called outside a request).
412         # We'll have to guess the base from server.* attributes.
413         # This will produce very different results from the above
414         # if you're using vhosts or tools.proxy.
415         if base is None:
416             base = server.base()
417        
418         path = (script_name or "") + path
419         newurl = base + path + qs
420    
421     if './' in newurl:
422         # Normalize the URL by removing ./ and ../
423         atoms = []
424         for atom in newurl.split('/'):
425             if atom == '.':
426                 pass
427             elif atom == '..':
428                 atoms.pop()
429             else:
430                 atoms.append(atom)
431         newurl = '/'.join(atoms)
432    
433     if relative:
434         old = url().split('/')[:-1]
435         new = newurl.split('/')
436         while old and new:
437             a, b = old[0], new[0]
438             if a != b:
439                 break
440             old.pop(0)
441             new.pop(0)
442         new = (['..'] * len(old)) + new
443         newurl = '/'.join(new)
444    
445     return newurl
446
447
448 # import _cpconfig last so it can reference other top-level objects
449 from cherrypy import _cpconfig
450 # Use _global_conf_alias so quickstart can use 'config' as an arg
451 # without shadowing cherrypy.config.
452 config = _global_conf_alias = _cpconfig.Config()
453
454 from cherrypy import _cpchecker
455 checker = _cpchecker.Checker()
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets