diff piecrust/commands/builtin/serving.py @ 219:d7a548ebcd58

serve: Add server sent events for showing pipeline errors in the debug window. The server can now run an endpoint that streams pipeline status, including errors or "fixed" statuses. As a result, I had to investigate using event-loop based server alternatives, before I figured out the correct flag to set in Werkzeug. Support for Gunicorn is therefore now possible, although disabled by default. I will come in handy though when proper support for CMS-mode is enabled.
author Ludovic Chabant <ludovic@chabant.com>
date Mon, 02 Feb 2015 08:34:44 -0800
parents f3aa511eef99
children 4f00bb99400e
line wrap: on
line diff
--- a/piecrust/commands/builtin/serving.py	Sat Jan 31 17:36:01 2015 -0800
+++ b/piecrust/commands/builtin/serving.py	Mon Feb 02 08:34:44 2015 -0800
@@ -13,25 +13,73 @@
         self.description = "Runs a local web server to serve your website."
 
     def setupParser(self, parser, app):
-        parser.add_argument('-p', '--port',
+        parser.add_argument(
+                '-p', '--port',
                 help="The port for the web server",
                 default=8080)
-        parser.add_argument('-a', '--address',
+        parser.add_argument(
+                '-a', '--address',
                 help="The host for the web server",
                 default='localhost')
-        parser.add_argument('--use-reloader',
+        parser.add_argument(
+                '--use-reloader',
                 help="Restart the server when PieCrust code changes",
                 action='store_true')
-        parser.add_argument('--use-debugger',
+        parser.add_argument(
+                '--use-debugger',
                 help="Show the debugger when an error occurs",
                 action='store_true')
+        parser.add_argument(
+                '--wsgi',
+                help="The WSGI server implementation to use",
+                choices=['werkzeug', 'gunicorn'],
+                default='werkzeug')
 
     def run(self, ctx):
+        host = ctx.args.address
+        port = int(ctx.args.port)
+        debug = ctx.args.debug or ctx.args.use_debugger
+
         server = Server(
                 ctx.app.root_dir,
-                host=ctx.args.address,
-                port=ctx.args.port,
-                debug=(ctx.args.debug or ctx.args.use_debugger),
+                debug=debug,
                 use_reloader=ctx.args.use_reloader)
-        server.run()
+        app = server.getWsgiApp()
+
+        if ctx.args.wsgi == 'werkzeug':
+            from werkzeug.serving import run_simple
+            run_simple(host, port, app,
+                       threaded=True,
+                       use_debugger=debug,
+                       use_reloader=ctx.args.use_reloader)
+
+        elif ctx.args.wsgi == 'gunicorn':
+            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
+
+            options = {
+                    'bind': '%s:%s' % (host, port),
+                    'accesslog': '-',
+                    'worker_class': 'gaiohttp',
+                    'workers': 2,
+                    'timeout': 999999}
+            if debug:
+                options['loglevel'] = 'debug'
+            if ctx.args.use_reloader:
+                options['reload'] = True
+            app_wrapper = PieCrustGunicornApplication(app, options)
+            app_wrapper.run()
+