Robert Brewer avatar Robert Brewer committed 1c3ef31

Final fix for #461 and #455:

1. There's a new _cpengine module which isolates the "app server" from the "HTTP server" state-management logic. _cpserver.Server inherits from _cpengine.Engine. The _cpengine module should remain clean so that it can be used with few changes in CP 3, at which point we can rewrite the http server API.
2. Fixed an issue with the native HTTP server--it wasn't truly restartable (closed but didn't rebind on restart).
3. Server.start now takes a "server" arg (an instance of an HTTP server). Use it or server_class, not both.
4. Fixed _cpwsgiserver's handling of the URI: "*".

Comments (0)

Files changed (7)

+"""Create and manage the CherryPy application server engine."""
+
+import cgi
+import sys
+import threading
+import time
+import warnings
+
+import cherrypy
+from cherrypy import _cphttptools, filters
+from cherrypy.lib import autoreload, profiler, cptools
+
+# Use a flag to indicate the state of the application server.
+STOPPED = 0
+STARTING = None
+STARTED = 1
+
+
+class Engine(object):
+    """The application server engine, connecting HTTP servers to Requests."""
+    
+    request_class = _cphttptools.Request
+    
+    def __init__(self):
+        self.state = STOPPED
+        
+        self.seen_threads = {}
+        self.interrupt = None
+        
+        # Startup/shutdown hooks
+        self.on_start_server_list = []
+        self.on_stop_server_list = []
+        self.on_start_thread_list = []
+        self.on_stop_thread_list = []
+    
+    def setup(self):
+        # The only reason this method isn't in __init__ is so that
+        # "import cherrypy" can create an Engine() without a circular ref.
+        conf = cherrypy.config.get
+        
+        # Output config options to log
+        if conf("server.log_config_options", True):
+            cherrypy.config.outputConfigMap()
+        
+        # Hmmm...we *could* check config in _start instead, but I think
+        # most people would like CP to fail before autoreload kicks in.
+        err = cherrypy.WrongConfigValue
+        for name, section in cherrypy.config.configs.iteritems():
+            for k, v in section.iteritems():
+                if k == "server.environment":
+                    if v and v not in cherrypy.config.environments:
+                        raise err("'%s' is not a registered environment." % v)
+        
+        if cherrypy.codecoverage:
+            from cherrypy.lib import covercp
+            covercp.start()
+        
+        # If sessions are stored in files and we
+        # use threading, we need a lock on the file
+        if (conf('server.thread_pool') > 1
+            and conf('session.storage_type') == 'file'):
+            cherrypy._sessionFileLock = threading.RLock()
+        
+        # set cgi.maxlen which will limit the size of POST request bodies
+        cgi.maxlen = conf('server.max_request_size')
+        
+        # Set up the profiler if requested.
+        if conf("profiling.on", False):
+            ppath = conf("profiling.path", "")
+            cherrypy.profiler = profiler.Profiler(ppath)
+        else:
+            cherrypy.profiler = None
+        
+        # Initialize the built in filters
+        filters.init()
+    
+    def start(self):
+        """Start the application server engine."""
+        self.state = STARTING
+        self.interrupt = None
+        
+        conf = cherrypy.config.get
+        
+        # Autoreload. Note that, if we're not starting our own HTTP server,
+        # autoreload could do Very Bad Things when it calls sys.exit, but
+        # deployers will just have to be educated and responsible for it.
+        if conf('autoreload.on', False):
+            try:
+                freq = conf('autoreload.frequency', 1)
+                autoreload.main(self._start, freq=freq)
+            except KeyboardInterrupt:
+                cherrypy.log("<Ctrl-C> hit: shutting down autoreloader", "ENGINE")
+                self.stop()
+            except SystemExit:
+                cherrypy.log("SystemExit raised: shutting down autoreloader", "ENGINE")
+                self.stop()
+                # We must raise here: if this is a process spawned by
+                # autoreload, then it must return its error code to
+                # the parent.
+                raise
+            return
+        
+        self._start()
+    
+    def _start(self):
+        for func in self.on_start_server_list:
+            func()
+        self.state = STARTED
+    
+    def block(self):
+        """Block forever (wait for KeyboardInterrupt or SystemExit)."""
+        try:
+            while True:
+                time.sleep(.1)
+                if self.interrupt:
+                    raise self.interrupt
+        except KeyboardInterrupt:
+            cherrypy.log("<Ctrl-C> hit: shutting down app server", "ENGINE")
+            self.stop()
+        except SystemExit:
+            cherrypy.log("SystemExit raised: shutting down app server", "ENGINE")
+            self.stop()
+        except:
+            # Don't bother logging, since we're going to re-raise.
+            self.interrupt = sys.exc_info()[1]
+            self.stop()
+            raise
+    
+    def stop(self):
+        """Stop the application server engine."""
+        for thread_ident, i in self.seen_threads.iteritems():
+            for func in self.on_stop_thread_list:
+                func(i)
+        self.seen_threads.clear()
+        
+        for func in self.on_stop_server_list:
+            func()
+        
+        self.state = STOPPED
+        cherrypy.log("CherryPy shut down", "ENGINE")
+    
+    def restart(self):
+        """Restart the application server engine."""
+        self.stop()
+        self.start()
+    
+    def wait(self):
+        """Block the caller until ready to receive requests (or error)."""
+        while not self.ready:
+            time.sleep(.1)
+            if self.interrupt:
+                msg = "The CherryPy application server errored"
+                raise cherrypy.NotReady(msg, "ENGINE")
+    
+    def _is_ready(self):
+        return bool(self.state == STARTED)
+    ready = property(_is_ready, doc="Return True if the server is ready to"
+                                    " receive requests, False otherwise.")
+    
+    def request(self, clientAddress, remoteHost, scheme="http"):
+        """Obtain an HTTP Request object.
+        
+        clientAddress: the (IP address, port) of the client
+        remoteHost: the IP address of the client
+        scheme: either "http" or "https"; defaults to "http"
+        """
+        if self.state == STOPPED:
+            raise cherrypy.NotReady("The CherryPy server has stopped.")
+        elif self.state == STARTING:
+            raise cherrypy.NotReady("The CherryPy server could not start.")
+        
+        threadID = threading._get_ident()
+        if threadID not in self.seen_threads:
+            
+            if cherrypy.codecoverage:
+                from cherrypy.lib import covercp
+                covercp.start()
+            
+            i = len(self.seen_threads) + 1
+            self.seen_threads[threadID] = i
+            
+            for func in self.on_start_thread_list:
+                func(i)
+        
+        r = self.request_class(clientAddress[0], clientAddress[1],
+                               remoteHost, scheme)
+        cherrypy.serving.request = r
+        cherrypy.serving.response = _cphttptools.Response()
+        return r
+
         self.RequestHandlerClass.protocol_version = httpproto
         
         self.request_queue_size = cherrypy.config.get('server.socket_queue_size')
+    
+    def server_bind(self):
+        """Called by constructor to bind the socket."""
         
         # Select the appropriate server based on config options
         sockFile = cherrypy.config.get('server.socket_file')
             
             self.server_address = sockFile
             self.socket = socket.socket(self.address_family, self.socket_type)
-            self.server_bind()
+            self._server_bind()
         else:
             # AF_INET or AF_INET6 socket
             host = cherrypy.config.get('server.socket_host')
                 # Probably a DNS issue.
                 # Must...refuse...temptation..to..guess...
                 self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-                self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-                self.socket.bind(self.server_address)
+                self._server_bind()
             else:
                 # Get the correct address family for our host (allows IPv6 addresses)
                 for res in info:
                     self.socket_type = socktype
                     try:
                         self.socket = socket.socket(af, socktype, proto)
-                        self.server_bind()
+                        self._server_bind()
                     except socket.error, msg:
                         if self.socket:
                             self.socket.close()
                     break
                 if not self.socket:
                     raise socket.error, msg
-        
-        self.server_activate()
+    
+    def _server_bind(self):
+        if self.allow_reuse_address:
+            self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        self.socket.bind(self.server_address)
     
     def server_activate(self):
         """Override server_activate to set timeout on our listener socket"""
         self.socket.settimeout(1)
         self.socket.listen(self.request_queue_size)
     
-    def server_bind(self):
-        """Called by constructor to bind the socket."""
-        if self.allow_reuse_address:
-            self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-        self.socket.bind(self.server_address)
-    
     def close_request(self, request):
         """Called to clean up an individual request."""
         request.close()
                 raise self.interrupt
             self.handle_request()
         self.server_close()
-    start = serve_forever
+    
+    def start(self):
+        self.server_bind()
+        self.server_activate()
+        self.serve_forever()
     
     def server_close(self):
         self.ready = False
                 time.sleep(.1)
         
         CherryHTTPServer.serve_forever(self)
-    start = serve_forever
     
     def server_close(self):
         """Gracefully shutdown a server that is serve_forever()ing."""
 """Create and manage the CherryPy server."""
 
-import cgi
-import sys
 import threading
 import time
-import warnings
 
 import cherrypy
-from cherrypy import _cphttptools, filters, _cpwsgi
-from cherrypy.lib import autoreload, profiler, cptools
+from cherrypy import _cphttptools
+from cherrypy.lib import cptools
 
-# Use a flag to indicate the state of the application server.
-STOPPED = 0
-STARTING = None
-STARTED = 1
+from cherrypy._cpengine import Engine, STOPPED, STARTING, STARTED
 
 _missing = object()
 
 
-class Server(object):
-    
-    request_class = _cphttptools.Request
+class Server(Engine):
     
     def __init__(self):
-        self.state = STOPPED
-        self.seen_threads = {}
+        Engine.__init__(self)
+        self._is_setup = False
+        self.blocking = True
         
         self.httpserver = None
+        # Starting in 2.2, the "httpserverclass" attr is essentially dead;
+        # no CP code uses it. Inspect "httpserver" instead.
         self.httpserverclass = None
-        self.interrupt = None
         
-        # Set some special attributes for adding hooks
-        self.on_start_server_list = []
-        self.on_start_thread_list = []
-        self.on_stop_server_list = []
-        self.on_stop_thread_list = []
-
         # Backward compatibility:
-        self.onStopServerList = []
-        self.onStartThreadList = []
-        self.onStartServerList = []
-        self.onStopThreadList = []
-
-        self.wsgi_app = _cpwsgi.wsgiApp
-        
-    def start(self, init_only = False, server_class = _missing, **kwargs):
+        self.onStopServerList = self.on_stop_server_list
+        self.onStartThreadList = self.on_start_thread_list
+        self.onStartServerList = self.on_start_server_list
+        self.onStopThreadList = self.on_stop_thread_list
+    
+    def start(self, init_only=False, server_class=_missing, server=None, **kwargs):
         """Main function. MUST be called from the main thread.
         
         Set initOnly to True to keep this function from blocking.
-        Set serverClass to None to skip starting any HTTP server.
+        Set serverClass and server to None to skip starting any HTTP server.
         """
         
         # Read old variable names for backward compatibility
         if 'serverClass' in kwargs:
             server_class = kwargs['serverClass']
         
+        conf = cherrypy.config.get
+        if server is None:
+            if server_class is _missing:
+                server_class = conf("server.class", _missing)
+            if server_class is _missing:
+                import _cpwsgi
+                server_class = _cpwsgi.WSGIServer
+            elif server_class and isinstance(server_class, basestring):
+                # Dynamically load the class from the given string
+                server_class = cptools.attributes(server_class)
+            self.httpserverclass = server_class
+            if server_class is not None:
+                self.httpserver = server_class()
+        else:
+            self.httpserverclass = server.__class__
+            self.httpserver = server
+        
+        self.blocking = not init_only
+        Engine.start(self)
+    
+    def _start(self):
+        if not self._is_setup:
+            self.setup()
+            self._is_setup = True
+        Engine._start(self)
+        self.start_http_server()
+        if self.blocking:
+            self.block()
+    
+    def restart(self):
+        """Restart the application server engine."""
+        self.stop()
         self.state = STARTING
         self.interrupt = None
-        
-        conf = cherrypy.config.get
-        
-        if server_class is _missing:
-            server_class = conf("server.class", _missing)
-        if server_class is _missing:
-            import _cpwsgi
-            server_class = _cpwsgi.WSGIServer
-        elif server_class and isinstance(server_class, basestring):
-            # Dynamically load the class from the given string
-            server_class = cptools.attributes(server_class)
-        
-        self.blocking = not init_only
-        self.httpserverclass = server_class
-        
-        # Hmmm...we *could* check config in _start instead, but I think
-        # most people would like CP to fail before autoreload kicks in.
-        check_config()
-        
-        # Autoreload, but check server_class. If None, we're not starting
-        # our own webserver, and therefore could do Very Bad Things when
-        # autoreload calls sys.exit.
-        if server_class is not None:
-            if conf('autoreload.on', False):
-                try:
-                    freq = conf('autoreload.frequency', 1)
-                    autoreload.main(self._start, freq=freq)
-                except KeyboardInterrupt:
-                    cherrypy.log("<Ctrl-C> hit: shutting down autoreloader", "HTTP")
-                    self.stop()
-                except SystemExit:
-                    cherrypy.log("SystemExit raised: shutting down autoreloader", "HTTP")
-                    self.stop()
-                    # We must raise here: if this is a process spawned by
-                    # autoreload, then it must return its error code to
-                    # the parent.
-                    raise
-                return
-        
         self._start()
     
-    def _start(self):
-        # Output config options to log
-        if cherrypy.config.get("server.log_config_options", True):
-            cherrypy.config.outputConfigMap()
-        
-        try:
-            configure()
-            
-            for func in cherrypy.server.on_start_server_list + cherrypy.server.onStartServerList:
-                func()
-            self.start_http_server()
-            self.state = STARTED
-            
-            if self.blocking:
-                # Block forever (wait for KeyboardInterrupt or SystemExit).
-                while True:
-                    time.sleep(.1)
-                    if self.interrupt:
-                        raise self.interrupt
-        except KeyboardInterrupt:
-            cherrypy.log("<Ctrl-C> hit: shutting down server", "HTTP")
-            self.stop()
-        except SystemExit:
-            cherrypy.log("SystemExit raised: shutting down server", "HTTP")
-            self.stop()
-        except:
-            # Don't bother logging, since we're going to re-raise.
-            self.interrupt = sys.exc_info()[1]
-            self.stop()
-            raise
-    
     def start_http_server(self, blocking=True):
         """Start the requested HTTP server."""
-        if self.httpserver is not None:
-            msg = ("You seem to have an HTTP server still running."
-                   "Please call server.stop_http_server() "
-                   "before continuing.")
-            warnings.warn(msg)
-        
-        if self.httpserverclass is None:
+        if not self.httpserver:
             return
         
         if cherrypy.config.get('server.socket_port'):
         else:
             on_what = "socket file: %s" % cherrypy.config.get('server.socket_file')
         
-        # Instantiate the server.
-        self.httpserver = self.httpserverclass()
-        
         # HTTP servers MUST be started in a new thread, so that the
-        # main thread persists to receive KeyboardInterrupt's. This
-        # wrapper traps an interrupt in the http server's main thread
-        # and shutdowns CherryPy.
+        # main thread persists to receive KeyboardInterrupt's. If an
+        # exception is raised in the http server's main thread then it's
+        # trapped here, and the CherryPy app server is shut down (via
+        # self.interrupt).
         def _start_http():
             try:
                 self.httpserver.start()
             except (KeyboardInterrupt, SystemExit), exc:
                 self.interrupt = exc
+                self.stop()
         threading.Thread(target=_start_http).start()
         
         if blocking:
         
         cherrypy.log("Serving HTTP on %s" % on_what, 'HTTP')
     
+    def wait(self):
+        """Block the caller until ready to receive requests (or error)."""
+        Engine.wait(self)
+        self.wait_for_http_ready()
+    
     def wait_for_http_ready(self):
-        if self.httpserverclass is not None:
-            while not getattr(self.httpserver, "ready", True):
+        if self.httpserver:
+            while not getattr(self.httpserver, "ready", True) and not self.interrupt:
                 time.sleep(.1)
             
             # Wait for port to be occupied
             if cherrypy.config.get('server.socket_port'):
                 host = cherrypy.config.get('server.socket_host')
                 port = cherrypy.config.get('server.socket_port')
-                wait_for_occupied_port(host, port)
-    
-    def request(self, clientAddress, remoteHost, scheme="http"):
-        """Obtain an HTTP Request object.
-        
-        clientAddress: the (IP address, port) of the client
-        remoteHost: the IP address of the client
-        scheme: either "http" or "https"; defaults to "http"
-        """
-        if self.state == STOPPED:
-            raise cherrypy.NotReady("The CherryPy server has stopped.")
-        elif self.state == STARTING:
-            raise cherrypy.NotReady("The CherryPy server could not start.")
-        
-        threadID = threading._get_ident()
-        if threadID not in self.seen_threads:
-            
-            if cherrypy.codecoverage:
-                from cherrypy.lib import covercp
-                covercp.start()
-            
-            i = len(self.seen_threads) + 1
-            self.seen_threads[threadID] = i
-            
-            for func in self.on_start_thread_list + self.onStartThreadList:
-                func(i)
-        
-        r = self.request_class(clientAddress[0], clientAddress[1],
-                              remoteHost, scheme)
-        cherrypy.serving.request = r
-        cherrypy.serving.response = _cphttptools.Response()
-        return r
+                if not host:
+                    host = 'localhost'
+                
+                for trial in xrange(50):
+                    if self.interrupt:
+                        break
+                    try:
+                        check_port(host, port)
+                    except IOError:
+                        break
+                    else:
+                        time.sleep(.1)
+                else:
+                    cherrypy.log("Port %s not bound on %s" %
+                                 (repr(port), repr(host)), 'HTTP')
+                    raise cherrypy.NotReady("Port not bound.")
     
     def stop(self):
         """Stop, including any HTTP servers."""
         self.stop_http_server()
-        
-        for thread_ident, i in self.seen_threads.iteritems():
-            for func in self.on_stop_thread_list + self.onStopThreadList:
-                func(i)
-        self.seen_threads.clear()
-        
-        for func in self.on_stop_server_list + self.onStopServerList:
-            func()
-        
-        self.state = STOPPED
-        cherrypy.log("CherryPy shut down", "HTTP")
+        Engine.stop(self)
     
     def stop_http_server(self):
         """Stop the HTTP server."""
             # httpstop() MUST block until the server is *truly* stopped.
             httpstop()
             cherrypy.log("HTTP Server shut down", "HTTP")
-        
-        self.httpserver = None
-    
-    def restart(self):
-        """Restart, including any HTTP servers."""
-        self.stop()
-        for func in self.on_start_server_list + self.onStartServerList:
-            func()
-        self.start_http_server()
-        self.state = STARTED
-    
-    def wait(self):
-        """Block the caller until ready to receive requests (or error)."""
-        while not self.ready:
-            time.sleep(.1)
-            if self.interrupt:
-                # Something went wrong in server.start,
-                # possibly in another thread. Stop this thread.
-                raise cherrypy.NotReady("The CherryPy server errored", "HTTP")
-    
-    def _is_ready(self):
-        return bool(self.state == STARTED)
-    ready = property(_is_ready, doc="Return True if the server is ready to"
-                                    " receive requests, False otherwise.")
     
     def start_with_callback(self, func, args=None, kwargs=None,
                             server_class = _missing, serverClass = None):
         """Start, then callback the given func in a new thread."""
-
+        
         # Read old name for backward compatibility
         if serverClass is not None:
             server_class = None
-
+        
         if args is None:
             args = ()
         if kwargs is None:
         self.start(server_class = server_class)
 
 
-def check_config():
-    err = cherrypy.WrongConfigValue
-    for name, section in cherrypy.config.configs.iteritems():
-        for k, v in section.iteritems():
-            if k == "server.environment":
-                if v and v not in cherrypy.config.environments:
-                    raise err("'%s' is not a registered environment." % v)
-
-
-def configure():
-    """Perform one-time actions to prepare the CherryPy core."""
-    if cherrypy.codecoverage:
-        from cherrypy.lib import covercp
-        covercp.start()
-    
-    conf = cherrypy.config.get
-    # TODO: config.checkConfigOptions()
-    
-    # If sessions are stored in files and we
-    # use threading, we need a lock on the file
-    if (conf('server.thread_pool') > 1
-        and conf('session.storage_type') == 'file'):
-        cherrypy._sessionFileLock = threading.RLock()
-    
-    # set cgi.maxlen which will limit the size of POST request bodies
-    cgi.maxlen = conf('server.max_request_size')
-    
-    # Set up the profiler if requested.
-    if conf("profiling.on", False):
-        ppath = conf("profiling.path", "")
-        cherrypy.profiler = profiler.Profiler(ppath)
-    else:
-        cherrypy.profiler = None
-    
-    # Initialize the built in filters
-    filters.init()
-
-
 def check_port(host, port):
     """Raise an error if the given port is not free on the given host."""
     sock_file = cherrypy.config.get('server.socket_file')
         self.environ["REQUEST_METHOD"] = method
         
         for mount_point, wsgi_app in self.server.mount_points:
+            if path == "*":
+                # This means, of course, that the first wsgi_app will
+                # always handle a URI of "*".
+                self.environ["SCRIPT_NAME"] = ""
+                self.environ["PATH_INFO"] = "*"
+                self.wsgi_app = wsgi_app
+                break
             # The mount_points list should be sorted by length, descending.
             if path.startswith(mount_point):
                 self.environ["SCRIPT_NAME"] = mount_point

test/test_core.py

         self.assertInBody(msg)
         
         # Test server.throw_errors (ticket #186).
-        httpcls = cherrypy.server.httpserverclass
-        if httpcls:
+        s = cherrypy.server.httpserver
+        if s:
             self.getPage("/error/rethrow")
             self.assertBody("THROWN ERROR: ValueError")
         else:
         self.getPage("/maxrequestsize/index")
         self.assertBody("OK")
         
-        httpcls = cherrypy.server.httpserverclass
-        if httpcls:
+        s = cherrypy.server.httpserver
+        if s:
             cherrypy.config.update({'server.max_request_header_size': 10})
             self.getPage("/maxrequestsize/index")
             self.assertStatus("413 Request Entity Too Large")
         self.getPage('/maxrequestsize/upload', h, "POST", b)
         self.assertBody('Size: 5')
         
-        httpcls = cherrypy.server.httpserverclass
-        if httpcls:
+        s = cherrypy.server.httpserver
+        if s:
             cherrypy.config.update({
                 '%s/maxrequestsize' % self.prefix(): {'server.max_request_body_size': 3}})
             self.getPage('/maxrequestsize/upload', h, "POST", b)

test/test_logdebuginfo_filter.py

         #self.assertInBody('Session data size')
 
     def testBug326(self):
-        httpcls = cherrypy.server.httpserverclass
-        if httpcls and httpcls.__name__ == "WSGIServer":
+        from cherrypy import _cpwsgi
+        s = cherrypy.server.httpserver
+        if s and isinstance(s, _cpwsgi.WSGIServer):
             h = [("Content-type", "multipart/form-data; boundary=x"),
                  ("Content-Length", "110")]
             b = """--x
 --x--
 """
             cherrypy.config.update({
-                ('%s/bug326' % self.prefix): {
+                ('%s/bug326' % self.prefix()): {
                     'server.max_request_body_size': 3,
                     'server.environment': 'development',
                 }

test/test_states.py

 class ServerStateTests(helper.CPWebCase):
     
     def test_0_NormalStateFlow(self):
-        # Without having called "cherrypy.server.start()", we should
-        # get a NotReady error
-        self.assertRaises(cherrypy.NotReady, self.getPage, "/")
+        if not self.server_class:
+            # Without having called "cherrypy.server.start()", we should
+            # get a NotReady error
+            self.assertRaises(cherrypy.NotReady, self.getPage, "/")
         
         # And our db_connection should not be running
         self.assertEqual(db_connection.running, False)
         cherrypy.server.stop()
         self.assertEqual(cherrypy.server.state, 0)
         
-        # Once the server has stopped, we should get a NotReady error again.
-        self.assertRaises(cherrypy.NotReady, self.getPage, "/")
-        
         # Verify that the on_stop_server function was called
         self.assertEqual(db_connection.running, False)
         self.assertEqual(len(db_connection.threads), 0)
+        
+        if not self.server_class:
+            # Once the server has stopped, we should get a NotReady
+            # error again. (If we were running an HTTP server,
+            # then the connection should not even be processed).
+            self.assertRaises(cherrypy.NotReady, self.getPage, "/")
     
     def test_1_Restart(self):
         cherrypy.server.start(True, self.server_class)
             # We must start the server in this, the main thread
             cherrypy.server.start(False, self.server_class)
             # Time passes...
-            self.assertEqual(cherrypy.server.httpserver, None)
             self.assertEqual(cherrypy.server.state, 0)
-            self.assertRaises(cherrypy.NotReady, self.getPage, "/")
             self.assertEqual(db_connection.running, False)
             self.assertEqual(len(db_connection.threads), 0)
             
             
             cherrypy.server.start(False, self.server_class)
             # Time passes...
-            self.assertEqual(cherrypy.server.httpserver, None)
             self.assertEqual(cherrypy.server.state, 0)
-            self.assertRaises(cherrypy.NotReady, self.getPage, "/")
             self.assertEqual(db_connection.running, False)
             self.assertEqual(len(db_connection.threads), 0)
-    
-    def test_3_ConfigErrors(self):
-        cherrypy.config.update({'server.environment': 'destruction'})
-        
-        try:
-            self.assertRaises(cherrypy.WrongConfigValue,
-                    cherrypy.server.start, True, self.server_class)
-        finally:
-            cherrypy.server.stop()
 
 
 db_connection = None
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.