Re: A more useful command-line wsgiref.simple_server?

Masklinn <[email protected]> Sun, 1 Apr 2012 16:32:00 +0200
Newsgroups gmane.comp.python.web
Message-ID <[email protected]>
On 2012-03-31, at 05:27 , PJ Eby wrote:

> On Fri, Mar 30, 2012 at 2:22 PM, Sasha Hart <[email protected]> wrote:
> 
>> I do really like the idea of having a quick WSGI runner in the stdlib,
>> 
> Regarding modules vs. files, I don't really care that much which way the
> capability is spelled, as long as the file vs. module distinction is
> explicit.  "-m " isn't a lot to add to a command line, and neither is "-f
> ".  If there's no consensus, just require that one or the other be
> specified, and inconvenience both groups of people equally.  ;-)

Let's go with that. I'm not sure if I should be using sub-commands or
options so for now I've used options. UI changes from initial patch:

* Exclusive options for getting the application callable from a script
  (via `exec`) or a module (using `importlib.import_module`)
* Application name specification has been moved to an option (default:
  `application`)
* Option to serve the request only once (default: forever) when mounting
  a custom application
* Option to open a browser window/tab to the (host, port) specified
  (which still default to '' and 8000 respectively)

> python -mwsgiref.simple_server -h
usage: simple_server.py [-h] [-H HOST] [-p PORT] [-s SCRIPT | -m MODULE]
                        [-a APP] [-1] [-b]

Mount and serve a WSGI application. If no script or module is specified,
mounts wsgiref.simple_server.demo_app

optional arguments:
  -h, --help            show this help message and exit
  -H HOST, --host HOST  Host to listen on
  -p PORT, --port PORT  Port to listen on (defaults to 8000)
  -s SCRIPT, --script SCRIPT
                        WSGI script file to execute and get the application
                        from
  -m MODULE, --module MODULE
                        Python module to import and get the application from

Script or module options:
  These options do not apply when mounting the demo_app

  -a APP, --app APP, --app-name APP
                        Name of the application variable in the script or
                        module, application if none is provided
  -1, --once            Only handles a single request, then exits
  -b, --browse          Launch browser to the served URL

_______________________________________________
Web-SIG mailing list
[email protected]
Web SIG: http://www.python.org/sigs/web-sig
Unsubscribe: http://mail.python.org/mailman/options/web-sig/gcpw-web-sig%40m.gmane.org
simple_server.patch (application/octet-stream, 3.1 KB)
diff --git a/Lib/wsgiref/simple_server.py b/Lib/wsgiref/simple_server.py
--- a/Lib/wsgiref/simple_server.py
+++ b/Lib/wsgiref/simple_server.py
@@ -146,11 +146,63 @@ def make_server(
     server.set_app(app)
     return server
 
+if __name__ == '__main__':
+    import argparse
+    import importlib
+    import operator
+    import webbrowser
 
-if __name__ == '__main__':
-    httpd = make_server('', 8000, demo_app)
-    sa = httpd.socket.getsockname()
-    print("Serving HTTP on", sa[0], "port", sa[1], "...")
-    import webbrowser
-    webbrowser.open('http://localhost:8000/xyz?abc')
-    httpd.handle_request()  # serve one request, then exit
+    parser = argparse.ArgumentParser(
+        description="Mount and serve a WSGI application. If no script or "
+                    "module is specified, mounts "
+                    "wsgiref.simple_server.demo_app")
+
+    parser.add_argument('-H', '--host', default='',
+        help="Host to listen on")
+    parser.add_argument('-p', '--port', type=int, default=8000,
+        help="Port to listen on (defaults to %(default)d)")
+
+    group = parser.add_mutually_exclusive_group()
+    group.add_argument('-s', '--script', default=None,
+        help="WSGI script file to execute and get the application from")
+    group.add_argument('-m', '--module', default=None,
+        help="Python module to import and get the application from")
+
+    custom = parser.add_argument_group(title="Script or module options",
+        description="These options do not apply when mounting the demo_app")
+    custom.add_argument('-a', '--app', '--app-name', default='application',
+        help="Name of the application variable in the script or module, "
+             "%(default)s if none is provided")
+    custom.add_argument('-1', '--once', action='store_const', dest='serve',
+        default=operator.methodcaller('serve_forever'),
+        const=operator.methodcaller('handle_request'),
+        help="Only handles a single request, then exits")
+    custom.add_argument('-b', '--browse', action='store_true', default=False,
+        help="Launch browser to the served URL")
+
+
+    args = parser.parse_args()
+
+    if args.script is None and args.module is None:
+        httpd = make_server(args.host, args.port, demo_app)
+        sa = httpd.socket.getsockname()
+        print("Serving HTTP on", sa[0], "port", sa[1], "...")
+        webbrowser.open('http://localhost:8000/xyz?abc')
+        httpd.handle_request()  # serve one request, then exit
+    else:
+        if args.script is not None:
+            variables = {}
+            exec(compile(open(args.script).read(), args.script, 'exec'), variables)
+        else:
+            variables = vars(importlib.import_module(args.module))
+        app = variables[args.app]
+        httpd = make_server(args.host, args.port, app)
+
+        sa = httpd.socket.getsockname()
+        print("Serving HTTP on", sa[0], "port", sa[1], "...")
+
+        if args.browse:
+            webbrowser.open("http://{host}:{port}/".format(
+                host=args.host or 'localhost',
+                port=args.port))
+        args.serve(httpd)