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

root/branches/598-sendall/cherrypy/__init__.py

Revision 1868 (checked in by fumanchu, 11 months ago)

Half of the fix for #717 (sessions should have a len function). Not implemented yet for PG, Memcached.

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

Hosted by WebFaction

Log in as guest/cpguest to create tickets