A more useful command-line wsgiref.simple_server?
Masklinn <[email protected]> Thu, 29 Mar 2012 12:02:46 +0200
| Newsgroups | gmane.comp.python.web |
|---|---|
| Message-ID | <[email protected]> |
Moving here as suggested by Terry Reedy as this list may be more
interested than -ideas (note: some feedback already used to revise
the original proposal, and a very basic patch — with no tests — is
provided for the current CPython default branch)
Currently, calling wsgiref.simple_server simply mounts the (bundled)
demo app.
I think that's a bit of a lost opportunity: the community seems to have
mostly standardized on a wsgi script providing an application callable
in its global namespace (though details may differ, mod_wsgi does not
care for the script's name and mandates an `application` callable while
e.g. gunicorn wants a Python module and the callable name must be
configured), and it would be nice if simple_server could take such a
script and mount the application provided:
* This would allow testing that the script has no error without having
to go through mounting it in e.g. mod_wsgi
* It would make trivial/test applications (e.g. dynamic responders to
local JS) simpler to bootstrap as there would be no need for the
half-dozen lines of wsgiref.simple_server bootstrapping and "hard"
dependency on wsgiref,
import wsgiref.simple_server
def application(environ, start_response):
'code'
if __name__ == '__main__':
httpd = make_server('', 8000, application)
httpd.serve_forever()
could become:
def application(environ, start_response):
'code'
Since wsgiref already supports `python -mwsgiref.simple_server`, the
changes would be pretty simple:
* an optional positional argument of the form `script[:app]`, the script
is exec'd, the application (called "application" by default) is
extracted and then mounted in simple_server. If no script is specified,
just mount `demo_app` as before
* Add -H/--host -p/--port options to, respectively, the hostname and the
port to bind the server to.
* The current -msimple_server uses `handle_request` and only replies once,
to increase the usability of the CLI tool use `serve_forever` *when and
only when the mounted application is not demo_app*. It also avoids
opening a hardcoded example URL on launch.
This way the current sanity test/"PHPInfo" demo app works as it did before,
but it becomes possible to very easily serve a WSGI script with almost no
overhead in the script itself.
Attachment: patch performing the above-specified alterations, using
argparse for arguments parsing and generation of help.
_______________________________________________
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, 1.8 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
@@ -15,6 +15,8 @@ import sys
import urllib.parse
from wsgiref.handlers import SimpleHandler
+import argparse
+
__version__ = "0.2"
__all__ = ['WSGIServer', 'WSGIRequestHandler', 'demo_app', 'make_server']
@@ -146,11 +148,36 @@ def make_server(
server.set_app(app)
return server
+def app_script(filename):
+ app_variable = 'application'
+ if ':' in filename:
+ filename, app_variable = filename.split(':')
+
+ variables = {}
+ exec(compile(open(filename).read(), filename, 'exec'), variables)
+
+ return variables[app_variable]
+
+parser = argparse.ArgumentParser(
+ description="Mount and serve a WSGI application")
+parser.add_argument('script',
+ type=app_script, default=demo_app, nargs='?',
+ metavar="script[:app]",
+ help="WSGI script to load the application from, loads demo_app if no script "
+ "is provided. By default, tries mounting the 'application' variable "
+ "from the script.")
+parser.add_argument('-H', '--host', help="Host to listen on",
+ default='')
+parser.add_argument('-p', '--port', type=int, default=8000,
+ help="Port to listen on (defaults to %(default)d)")
if __name__ == '__main__':
- httpd = make_server('', 8000, demo_app)
+ args = parser.parse_args()
+ httpd = make_server(args.host, args.port, args.script)
sa = httpd.socket.getsockname()
print("Serving HTTP on", sa[0], "port", sa[1], "...")
+ if args.script is not demo_app:
+ httpd.serve_forever()
import webbrowser
webbrowser.open('http://localhost:8000/xyz?abc')
httpd.handle_request() # serve one request, then exit