1. Sylvain Hellegouarch
  2. CherryPy Recipes

Source

CherryPy Recipes / deployment / waitress_ / waitressplugin.py

# -*- coding: utf-8 -*-
import threading

import cherrypy
from cherrypy.process import plugins
from waitress.server import WSGIServer as _WSGIServer

__all__ = ['WaitressServerPlugin']

class WSGIServer(_WSGIServer):
    def loop(self, count=1, timeout=0.0):
        self.asyncore.loop(timeout=timeout, map=self._map, use_poll=True, count=count)
        
    def shutdown(self):
        self.task_dispatcher.shutdown()
        
class WaitressServerPlugin(plugins.SimplePlugin):
    def __init__(self, wsgiapp, **kwargs):
        """ Rather than using the asyncore loop to block
        as waitress itself does, we use the CherryPy mainloop
        to loop through the WSGIserver.

        The advantage of this solution is that the server
        runs in the main thread. The drawbacks is that the
        server loops only when the bus tells it to which will
        add some delay to the request processing.
        """
        plugins.SimplePlugin.__init__(self, cherrypy.engine)
        self.server = WSGIServer(wsgiapp, **kwargs)

    def start(self):
        cherrypy.log("Starting the waitress server's loop")
        cherrypy.log("Serving on http://%s:%s" % (self.server.effective_host,
                                                  self.server.effective_port))
        self.bus.subscribe('main', self.server.loop)
            
    def stop(self):
        cherrypy.log("Stopping the waitress server's loop")
        self.server.shutdown()
        self.bus.unsubscribe('main', self.server.loop)

class ThreadedWaitressServerPlugin(plugins.SimplePlugin):
    def __init__(self, wsgiapp, **kwargs):
        """ Run the waitress server loop into a dedicated thread
        controlled by this plugin so that we can start and stop
        the server along side the bus.

        The advantage is that we let the server handle incoming
        connections and data at its own rate, independantly from
        the bus mainloop. The drawbacks is that the server then
        runs in a different thread.
        """
        plugins.SimplePlugin.__init__(self, cherrypy.engine)
        self.server = WSGIServer(wsgiapp, **kwargs)
        self.runner = threading.Thread(target=self.run, name="waitress server")
        self.runner.daemon = True

    def start(self):
        cherrypy.log("Starting the waitress server's loop")
        cherrypy.log("Serving on http://%s:%s" % (self.server.effective_host,
                                                  self.server.effective_port))
        self.runner.start()
            
    def stop(self):
        cherrypy.log("Stopping the waitress server's loop")
        self.server.shutdown()
        self.runner.join(timeout=1.0)

    def run(self):
        self.server.loop(count=None, timeout=30.0)