view piecrust/serving/wrappers.py @ 556:93b656f0af54

serve: Improve debug information in the preview server. Now the debug window only loads debug info on demand.
author Ludovic Chabant <ludovic@chabant.com>
date Wed, 12 Aug 2015 23:18:35 -0700
parents cc6f3dbe3048
children 9ab005db2592
line wrap: on
line source

import os
import logging
import threading
import urllib.request


logger = logging.getLogger(__name__)


def run_werkzeug_server(root_dir, host, port,
                        debug_piecrust=False, sub_cache_dir=None,
                        use_debugger=False, use_reloader=False):
    from werkzeug.serving import run_simple

    def _run_sse_check():
        # We don't want to run the processing loop here if this isn't
        # the actual process that does the serving. In most cases it is,
        # but if we're using Werkzeug's reloader, then it won't be the
        # first time we get there... it will only be the correct process
        # the second time, when the reloading process is spawned, with the
        # `WERKZEUG_RUN_MAIN` variable set.
        return (not use_reloader or
                os.environ.get('WERKZEUG_RUN_MAIN') == 'true')

    app = _get_piecrust_server(root_dir,
                               debug=debug_piecrust,
                               sub_cache_dir=sub_cache_dir,
                               run_sse_check=_run_sse_check)

    # We need to run Werkzeug in a background thread because we may have some
    # SSE responses running. In theory we should be using a proper async
    # server for this kind of stuff, but I'd rather avoid additional
    # dependencies on stuff that's not necessarily super portable.
    # Anyway we run the server in multi-threading mode, but the request
    # threads are not set to `daemon` mode (and there's no way to set that
    # flag without re-implementing `run_simple` apparently). So instead we
    # run the server in a background thread so we keep the main thread to
    # ourselves here, which means we can trap `KeyboardInterrupt`, and set
    # a global flag that will kill all the long-running SSE threads and make
    # this whole thing exit cleanly and properly (hopefully).
    def _inner():
        run_simple(host, port, app,
                   threaded=True,
                   use_debugger=use_debugger,
                   use_reloader=use_reloader)

    t = threading.Thread(name='WerkzeugServer', target=_inner)
    t.start()
    try:
        while t.is_alive():
            t.join(0.5)
    except KeyboardInterrupt:
        shutdown_url = 'http://%s:%s/__piecrust_debug/werkzeug_shutdown' % (
                host, port)
        logger.info("")
        logger.info("Shutting down server...")
        urllib.request.urlopen(shutdown_url)
    finally:
        logger.debug("Terminating push notifications...")
        from piecrust.serving import procloop
        procloop.server_shutdown = True


def run_gunicorn_server(root_dir,
                        debug_piecrust=False, sub_cache_dir=None,
                        gunicorn_options=None):
    from gunicorn.app.base import BaseApplication

    class PieCrustGunicornApplication(BaseApplication):
        def __init__(self, app, options):
            self.app = app
            self.options = options
            super(PieCrustGunicornApplication, self).__init__()

        def load_config(self):
            for k, v in self.options.items():
                if k in self.cfg.settings and v is not None:
                    self.cfg.set(k, v)

        def load(self):
            return self.app

    app = _get_piecrust_server(root_dir,
                               debug=debug_piecrust,
                               sub_cache_dir=sub_cache_dir)

    gunicorn_options = gunicorn_options or {}
    app_wrapper = PieCrustGunicornApplication(app, gunicorn_options)
    app_wrapper.run()


def _get_piecrust_server(root_dir, debug=False, sub_cache_dir=None,
                         run_sse_check=None):
    from piecrust.serving.middlewares import (
            StaticResourcesMiddleware, PieCrustDebugMiddleware)
    from piecrust.serving.server import WsgiServer
    app = WsgiServer(root_dir, debug=debug, sub_cache_dir=sub_cache_dir)
    app = StaticResourcesMiddleware(app)
    app = PieCrustDebugMiddleware(app, root_dir,
                                  sub_cache_dir=sub_cache_dir,
                                  run_sse_check=run_sse_check)
    return app