Commits

Robert Brewer committed 6a03f70

New engine.release method, which decouples request and engine. Also new server.base method, which simplifies cherrypy.url. Finally, cherrypy._serving is promoted to cherrypy.serving, and has a new "load" method.

Comments (0)

Files changed (6)

cherrypy/__init__.py

-"""Global module that all modules developing with CherryPy should import."""
+"""CherryPy is a pythonic, object-oriented HTTP framework.
+
+
+CherryPy consists of not one, but four separate API layers.
+
+The APPLICATION LAYER is the simplest. CherryPy applications are written as
+a tree of classes and methods, where each branch in the tree corresponds to
+a branch in the URL path. Each method is a 'page handler', which receives
+GET and POST params as keyword arguments, and returns or yields the (HTML)
+body of the response. The special method name 'index' is used for paths
+that end in a slash, and the special method name 'default' is used to
+handle multiple paths via a single handler. This layer also includes:
+
+ * the 'exposed' attribute (and cherrypy.expose)
+ * cherrypy.quickstart()
+ * _cp_config attributes
+ * cherrypy.tools (including cherrypy.session)
+ * cherrypy.url()
+
+The ENVIRONMENT LAYER is used by developers at all levels. It provides
+information about the current request and response, plus the application
+and server environment, via a (default) set of top-level objects:
+
+ * cherrypy.request
+ * cherrypy.response
+ * cherrypy.engine
+ * cherrypy.server
+ * cherrypy.tree
+ * cherrypy.config
+ * cherrypy.thread_data
+ * cherrypy.log
+ * cherrypy.HTTPError, NotFound, and HTTPRedirect
+ * cherrypy.lib
+
+The EXTENSION LAYER allows advanced users to construct and share their own
+plugins. It consists of:
+
+ * Hook API
+ * Tool API
+ * Toolbox API
+ * Dispatch API
+ * Config Namespace API
+
+Finally, there is the CORE LAYER, which uses the core API's to construct
+the default components which are available at higher layers. You can think
+of the default components as the 'reference implementation' for CherryPy.
+Megaframeworks (and advanced users) may replace the default components
+with customized or extended components. The core API's are:
+
+ * Application API
+ * Engine API
+ * Request API
+ * Server API
+ * WSGI API
+
+These API's are described in the CherryPy specification:
+http://www.cherrypy.org/wiki/CherryPySpec
+"""
 
 __version__ = "3.0.1alpha"
 
 # objects. In this way, we can easily dump those objects when we stop/start
 # a new HTTP conversation, yet still refer to them as module-level globals
 # in a thread-safe way.
-_serving = _local()
+class _Serving(_local):
+    """An interface for registering request and response objects."""
+    
+    def load(self, request, response):
+        self.request = request
+        self.response = response
+    
+    def clear(self):
+        """Remove all attributes of self."""
+        self.__dict__.clear()
+
+serving = _serving = _Serving()
 
 
 class _ThreadLocalProxy(object):
         # This will produce very different results from the above
         # if you're using vhosts or tools.proxy.
         if base is None:
-            f = server.socket_file
-            if f:
-                base = f
-            else:
-                host = server.socket_host
-                if not host:
-                    # The empty string signifies INADDR_ANY.
-                    # Look up the host name, which should be
-                    # the safest thing to spit out in a URL.
-                    import socket
-                    host = socket.gethostname()
-                port = server.socket_port
-                if server.ssl_certificate:
-                    scheme = "https"
-                    if port != 443:
-                        host += ":%s" % port
-                else:
-                    scheme = "http"
-                    if port != 80:
-                        host += ":%s" % port
-                base = "%s://%s" % (scheme, host)
+            base = server.base()
+        
         path = (script_name or "") + path
         newurl = base + path + qs
     

cherrypy/_cpengine.py

     
     def wait(self):
         """Block the caller until ready to receive requests (or error)."""
-        while not self.ready:
+        while not (self.state == STARTED):
             time.sleep(.1)
     
-    def _is_ready(self):
-        return bool(self.state == STARTED)
-    ready = property(_is_ready, doc="Return True if the engine is ready to"
-                                    " receive requests, False otherwise.")
-    
     def request(self, local_host, remote_host, scheme="http",
                 server_protocol="HTTP/1.1"):
-        """Obtain an HTTP Request object.
+        """Obtain and return an HTTP Request object. (Core)
         
         local_host should be an http.Host object with the server info.
         remote_host should be an http.Host object with the client info.
                     func(i)
             req = self.request_class(local_host, remote_host, scheme,
                                      server_protocol)
-        cherrypy._serving.request = req
-        cherrypy._serving.response = resp = self.response_class()
+        resp = self.response_class()
+        cherrypy.serving.load(req, resp)
         self.servings.append((req, resp))
         return req
     
+    def release(self):
+        """Close and de-reference the current request and response. (Core)"""
+        req = cherrypy.serving.request
+        
+        try:
+            req.close()
+        except:
+            cherrypy.log(traceback=True)
+        
+        try:
+            self.servings.remove((req, cherrypy.serving.response))
+        except ValueError:
+            pass
+        
+        cherrypy.serving.clear()
+    
     def monitor(self):
-        """Check timeout on all responses."""
+        """Check timeout on all responses. (Internal)"""
         if self.state == STARTED:
             for req, resp in self.servings:
                 resp.check_timeout()

cherrypy/_cpmodpy.py

                     response = request.run(method, path, qs, sproto, headers, rfile)
                     break
                 except cherrypy.InternalRedirect, ir:
-                    request.close()
+                    cherrypy.engine.release()
                     prev = request
                     
                     if not recursive:
                     rfile = StringIO.StringIO()
             
             send_response(req, response.status, response.header_list, response.body)
-            request.close()
+            cherrypy.engine.release()
     except:
         tb = format_exc()
         cherrypy.log(tb)

cherrypy/_cpserver.py

         """Restart all HTTP servers."""
         self.stop()
         self.start()
+    
+    def base(self):
+        """Return the base (scheme://host) for this server manager."""
+        if self.socket_file:
+            return self.socket_file
+        
+        host = self.socket_host
+        if not host:
+            # The empty string signifies INADDR_ANY. Look up the host name,
+            # which should be the safest thing to spit out in a URL.
+            host = socket.gethostname()
+        
+        port = self.socket_port
+        
+        if self.ssl_certificate:
+            scheme = "https"
+            if port != 443:
+                host += ":%s" % port
+        else:
+            scheme = "http"
+            if port != 80:
+                host += ":%s" % port
+        
+        return "%s://%s" % (scheme, host)
 
 
 def check_port(host, port):

cherrypy/_cptools.py

         decorated function's _cp_config attribute.
     
     CherryPy config:
-        Hookpoints are places in the CherryPy request-handling process
-        which may hand off control to registered callbacks. The Request
-        object possesses a "hooks" attribute (a HookMap) for manipulating
-        this. If a tool exposes a "_setup" callable, it will be called
+        If a tool exposes a "_setup" callable, it will be called
         once per Request (if the feature is "turned on" via config).
 
 Tools may be implemented as any object with a namespace. The builtins

cherrypy/_cpwsgi.py

             return "".join(b)
     
     def close(self):
-        if hasattr(self.request, "close"):
-            try:
-                self.request.close()
-            except:
-                _cherrypy.log(traceback=True)
+        _cherrypy.engine.release()
     
     def get_engine_request(self, environ, cpapp):
         """Return a Request object from the CherryPy Engine using environ."""