Permalink
Browse files

Make web.py applications stoppable in stand-alone mode

web.py applications typically run in WSGI mode, but when used in embedded
environments, it may be more useful to run the application in stand-alone
server mode (a.k.a. 'app.run()').

Also, running the application via 'app.run()' in a different thread kills
the out-of-the-box behavior of stopping the application with Ctrl-C.

This patch adds the 'stop(self)' method to the application class, so that
applications running stand-alone can be stopped easily with 'app.stop()'.

Credits: llc at stackoverflow: http://stackoverflow.com/questions/5389111

Related to: #100
  • Loading branch information...
Irfy committed Dec 16, 2011
1 parent cd4ddf8 commit 23d24d88c932253bd59fcea41368962f09f29ccb
Showing with 30 additions and 4 deletions.
  1. +19 −0 test/application.py
  2. +7 −1 web/application.py
  3. +2 −1 web/httpserver.py
  4. +2 −2 web/wsgi.py
View
@@ -1,5 +1,6 @@
import webtest
import time
+import threading
import web
import urllib
@@ -324,5 +325,23 @@ def f(script_name=""):
self.assertEquals(f(''), 'foo=bar; Path=/')
self.assertEquals(f('/admin'), 'foo=bar; Path=/admin/')
+ def test_stopsimpleserver(self):
+ urls = (
+ '/', 'index',
+ )
+ class index:
+ def GET(self):
+ pass
+ app = web.application(urls, locals())
+ thread = threading.Thread(target=app.run)
+
+ thread.start()
+ time.sleep(1)
+ self.assertTrue(thread.is_alive())
+
+ app.stop()
+ thread.join(timeout=1)
+ self.assertFalse(thread.is_alive())
+
if __name__ == '__main__':
webtest.main()
View
@@ -48,6 +48,7 @@ def __init__(self, mapping=(), fvars={}, autoreload=None):
self.add_processor(loadhook(self._load))
self.add_processor(unloadhook(self._unload))
+ self._simpleserver = None
if autoreload:
def main_module_name():
@@ -308,7 +309,12 @@ def run(self, *middleware):
`middleware` is a list of WSGI middleware which is applied to the resulting WSGI
function.
"""
- return wsgi.runwsgi(self.wsgifunc(*middleware))
+ return wsgi.runwsgi(self.wsgifunc(*middleware), self)
+
+ def stop(self):
+ if self._simpleserver:
+ self._simpleserver.stop()
+ self._simpleserver = None
def cgirun(self, *middleware):
"""
View
@@ -131,7 +131,7 @@ def __init__(self, func, server_address):
print "http://%s:%d/" % server_address
WSGIServer(func, server_address).serve_forever()
-def runsimple(func, server_address=("0.0.0.0", 8080)):
+def runsimple(func, app, server_address=("0.0.0.0", 8080)):
"""
Runs [CherryPy][cp] WSGI server hosting WSGI app `func`.
The directory `static/` is hosted statically.
@@ -142,6 +142,7 @@ def runsimple(func, server_address=("0.0.0.0", 8080)):
func = LogMiddleware(func)
server = WSGIServer(server_address, func)
+ app._simpleserver = server
if server.ssl_adapter:
print "https://%s:%d/" % server_address
View
@@ -21,7 +21,7 @@ def runscgi(func, addr=('localhost', 4000)):
import flup.server.scgi as flups
return flups.WSGIServer(func, bindAddress=addr, debug=False).run()
-def runwsgi(func):
+def runwsgi(func, app):
"""
Runs a WSGI-compatible `func` using FCGI, SCGI, or a simple web server,
as appropriate based on context and `sys.argv`.
@@ -51,7 +51,7 @@ def runwsgi(func):
else:
return runscgi(func)
- return httpserver.runsimple(func, validip(listget(sys.argv, 1, '')))
+ return httpserver.runsimple(func, app, validip(listget(sys.argv, 1, '')))
def _is_dev_mode():
# quick hack to check if the program is running in dev mode.

0 comments on commit 23d24d8

Please sign in to comment.