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

root/trunk/cherrypy/__init__.py

Revision 1951 (checked in by fumanchu, 3 weeks ago)

Fix for #805 (Remove import of inspect).

  • 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.0beta3"
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     _console_control_handler = win32.ConsoleCtrlHandler(engine)
178     # If you don't want a ConsoleControlHandler,
179     # unsubscribe this before calling engine.start().
180     _console_control_handler.subscribe()
181     del win32
182 except ImportError:
183     engine = process.bus
184
185
186 # Timeout monitor
187 class _TimeoutMonitor(process.plugins.Monitor):
188    
189     def __init__(self, bus):
190         self.servings = []
191         process.plugins.Monitor.__init__(self, bus, self.run)
192    
193     def acquire(self):
194         self.servings.append((serving.request, serving.response))
195    
196     def release(self):
197         try:
198             self.servings.remove((serving.request, serving.response))
199         except ValueError:
200             pass
201    
202     def run(self):
203         """Check timeout on all responses. (Internal)"""
204         for req, resp in self.servings:
205             resp.check_timeout()
206 timeout_monitor = _TimeoutMonitor(engine)
207 timeout_monitor.subscribe()
208
209 # Add an autoreloader (the 'engine' config namespace may detach/attach it).
210 engine.autoreload = process.plugins.Autoreloader(engine)
211 engine.autoreload.subscribe()
212
213 process.plugins.ThreadManager(engine).subscribe()
214
215 signal_handler = process.plugins.SignalHandler(engine)
216
217 from cherrypy import _cpserver
218 server = _cpserver.Server()
219 server.subscribe()
220
221
222 def quickstart(root=None, script_name="", config=None):
223     """Mount the given root, start the builtin server (and engine), then block.
224     
225     root: an instance of a "controller class" (a collection of page handler
226         methods) which represents the root of the application.
227     script_name: a string containing the "mount point" of the application.
228         This should start with a slash, and be the path portion of the URL
229         at which to mount the given root. For example, if root.index() will
230         handle requests to "http://www.example.com:8080/dept/app1/", then
231         the script_name argument would be "/dept/app1".
232         
233         It MUST NOT end in a slash. If the script_name refers to the root
234         of the URI, it MUST be an empty string (not "/").
235     config: a file or dict containing application config. If this contains
236         a [global] section, those entries will be used in the global
237         (site-wide) config.
238     """
239     if config:
240         _global_conf_alias.update(config)
241    
242     if root is not None:
243         tree.mount(root, script_name, config)
244    
245     signal_handler.subscribe()
246     engine.start()
247     engine.block()
248
249
250 try:
251     from threading import local as _local
252 except ImportError:
253     from cherrypy._cpthreadinglocal import local as _local
254
255 class _Serving(_local):
256     """An interface for registering request and response objects.
257     
258     Rather than have a separate "thread local" object for the request and
259     the response, this class works as a single threadlocal container for
260     both objects (and any others which developers wish to define). In this
261     way, we can easily dump those objects when we stop/start a new HTTP
262     conversation, yet still refer to them as module-level globals in a
263     thread-safe way.
264     """
265    
266     __metaclass__ = _AttributeDocstrings
267    
268     request = _cprequest.Request(_http.Host("127.0.0.1", 80),
269                                  _http.Host("127.0.0.1", 1111))
270     request__doc = """
271     The request object for the current thread. In the main thread,
272     and any threads which are not receiving HTTP requests, this is None."""
273    
274     response = _cprequest.Response()
275     response__doc = """
276     The response object for the current thread. In the main thread,
277     and any threads which are not receiving HTTP requests, this is None."""
278    
279     def load(self, request, response):
280         self.request = request
281         self.response = response
282    
283     def clear(self):
284         """Remove all attributes of self."""
285         self.__dict__.clear()
286
287 serving = _Serving()
288
289
290 class _ThreadLocalProxy(object):
291    
292     __slots__ = ['__attrname__', '__dict__']
293    
294     def __init__(self, attrname):
295         self.__attrname__ = attrname
296    
297     def __getattr__(self, name):
298         child = getattr(serving, self.__attrname__)
299         return getattr(child, name)
300    
301     def __setattr__(self, name, value):
302         if name in ("__attrname__", ):
303             object.__setattr__(self, name, value)
304         else:
305             child = getattr(serving, self.__attrname__)
306             setattr(child, name, value)
307    
308     def __delattr__(self, name):
309         child = getattr(serving, self.__attrname__)
310         delattr(child, name)
311    
312     def _get_dict(self):
313         child = getattr(serving, self.__attrname__)
314         d = child.__class__.__dict__.copy()
315         d.update(child.__dict__)
316         return d
317     __dict__ = property(_get_dict)
318    
319     def __getitem__(self, key):
320         child = getattr(serving, self.__attrname__)
321         return child[key]
322    
323     def __setitem__(self, key, value):
324         child = getattr(serving, self.__attrname__)
325         child[key] = value
326    
327     def __delitem__(self, key):
328         child = getattr(serving, self.__attrname__)
329         del child[key]
330    
331     def __contains__(self, key):
332         child = getattr(serving, self.__attrname__)
333         return key in child
334    
335     def __len__(self):
336         child = getattr(serving, self.__attrname__)
337         return len(child)
338
339
340 # Create request and response object (the same objects will be used
341 #   throughout the entire life of the webserver, but will redirect
342 #   to the "serving" object)
343 request = _ThreadLocalProxy('request')
344 response = _ThreadLocalProxy('response')
345
346 # Create thread_data object as a thread-specific all-purpose storage
347 class _ThreadData(_local):
348     """A container for thread-specific data."""
349 thread_data = _ThreadData()
350
351
352 # Monkeypatch pydoc to allow help() to go through the threadlocal proxy.
353 # Jan 2007: no Googleable examples of anyone else replacing pydoc.resolve.
354 # The only other way would be to change what is returned from type(request)
355 # and that's not possible in pure Python (you'd have to fake ob_type).
356 def _cherrypy_pydoc_resolve(thing, forceload=0):
357     """Given an object or a path to an object, get the object and its name."""
358     if isinstance(thing, _ThreadLocalProxy):
359         thing = getattr(serving, thing.__attrname__)
360     return _pydoc._builtin_resolve(thing, forceload)
361
362 try:
363     import pydoc as _pydoc
364     _pydoc._builtin_resolve = _pydoc.resolve
365     _pydoc.resolve = _cherrypy_pydoc_resolve
366 except ImportError:
367     pass
368
369
370 from cherrypy import _cplogging
371
372 class _GlobalLogManager(_cplogging.LogManager):
373    
374     def __call__(self, *args, **kwargs):
375         try:
376             log = request.app.log
377         except AttributeError:
378             log = self
379         return log.error(*args, **kwargs)
380    
381     def access(self):
382         try:
383             return request.app.log.access()
384         except AttributeError:
385             return _cplogging.LogManager.access(self)
386
387
388 log = _GlobalLogManager()
389 # Set a default screen handler on the global log.
390 log.screen = True
391 log.error_file = ''
392 # Using an access file makes CP about 10% slower. Leave off by default.
393 log.access_file = ''
394
395 def _buslog(msg, level):
396     log.error(msg, 'ENGINE', severity=level)
397 engine.subscribe('log', _buslog)
398
399 #                       Helper functions for CP apps                       #
400
401
402 def expose(func=None, alias=None):
403     """Expose the function, optionally providing an alias or set of aliases."""
404    
405     def expose_(func):
406         func.exposed = True
407         if alias is not None:
408             if isinstance(alias, basestring):
409                 parents[alias.replace(".", "_")] = func
410             else:
411                 for a in alias:
412                     parents[a.replace(".", "_")] = func
413         return func
414    
415     import sys, types
416     if isinstance(func, (types.FunctionType, types.MethodType)):
417         # expose is being called directly, before the method has been bound
418         parents = sys._getframe(1).f_locals
419         return expose_(func)
420     else:
421         if alias is None:
422             # expose is being called as a decorator "@expose"
423             func.exposed = True
424             return func
425         else:
426             # expose is returning a decorator "@expose(alias=...)"
427             parents = sys._getframe(1).f_locals
428             return expose_
429
430 def url(path="", qs="", script_name=None, base=None, relative=None):
431     """Create an absolute URL for the given path.
432     
433     If 'path' starts with a slash ('/'), this will return
434         (base + script_name + path + qs).
435     If it does not start with a slash, this returns
436         (base + script_name [+ request.path_info] + path + qs).
437     
438     If script_name is None, cherrypy.request will be used
439     to find a script_name, if available.
440     
441     If base is None, cherrypy.request.base will be used (if available).
442     Note that you can use cherrypy.tools.proxy to change this.
443     
444     Finally, note that this function can be used to obtain an absolute URL
445     for the current request path (minus the querystring) by passing no args.
446     If you call url(qs=cherrypy.request.query_string), you should get the
447     original browser URL (assuming no internal redirections).
448     
449     If relative is None or not provided, request.app.relative_urls will
450     be used (if available, else False). If False, the output will be an
451     absolute URL (including the scheme, host, vhost, and script_name).
452     If True, the output will instead be a URL that is relative to the
453     current request path, perhaps including '..' atoms. If relative is
454     the string 'server', the output will instead be a URL that is
455     relative to the server root; i.e., it will start with a slash.
456     """
457     if qs:
458         qs = '?' + qs
459    
460     if request.app:
461         if not path.startswith("/"):
462             # Append/remove trailing slash from path_info as needed
463             # (this is to support mistyped URL's without redirecting;
464             # if you want to redirect, use tools.trailing_slash).
465             pi = request.path_info
466             if request.is_index is True:
467                 if not pi.endswith('/'):
468                     pi = pi + '/'
469             elif request.is_index is False:
470                 if pi.endswith('/') and pi != '/':
471                     pi = pi[:-1]
472            
473             if path == "":
474                 path = pi
475             else:
476                 path = _urljoin(pi, path)
477        
478         if script_name is None:
479             script_name = request.script_name
480         if base is None:
481             base = request.base
482        
483         newurl = base + script_name + path + qs
484     else:
485         # No request.app (we're being called outside a request).
486         # We'll have to guess the base from server.* attributes.
487         # This will produce very different results from the above
488         # if you're using vhosts or tools.proxy.
489         if base is None:
490             base = server.base()
491        
492         path = (script_name or "") + path
493         newurl = base + path + qs
494    
495     if './' in newurl:
496         # Normalize the URL by removing ./ and ../
497         atoms = []
498         for atom in newurl.split('/'):
499             if atom == '.':
500                 pass
501             elif atom == '..':
502                 atoms.pop()
503             else:
504                 atoms.append(atom)
505         newurl = '/'.join(atoms)
506    
507     # At this point, we should have a fully-qualified absolute URL.
508    
509     if relative is None:
510         relative = getattr(request.app, "relative_urls", False)
511    
512     # See http://www.ietf.org/rfc/rfc2396.txt
513     if relative == 'server':
514         # "A relative reference beginning with a single slash character is
515         # termed an absolute-path reference, as defined by <abs_path>..."
516         # This is also sometimes called "server-relative".
517         newurl = '/' + '/'.join(newurl.split('/', 3)[3:])
518     elif relative:
519         # "A relative reference that does not begin with a scheme name
520         # or a slash character is termed a relative-path reference."
521         old = url().split('/')[:-1]
522         new = newurl.split('/')
523         while old and new:
524             a, b = old[0], new[0]
525             if a != b:
526                 break
527             old.pop(0)
528             new.pop(0)
529         new = (['..'] * len(old)) + new
530         newurl = '/'.join(new)
531    
532     return newurl
533
534
535 # import _cpconfig last so it can reference other top-level objects
536 from cherrypy import _cpconfig
537 # Use _global_conf_alias so quickstart can use 'config' as an arg
538 # without shadowing cherrypy.config.
539 config = _global_conf_alias = _cpconfig.Config()
540
541 from cherrypy import _cpchecker
542 checker = _cpchecker.Checker()
543 engine.subscribe('start', checker)
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets