Bottle web framework - How to stop? - python

When starting a bottle webserver without a thread or a subprocess, there's no problem. To exit the bottle app -> CTRL + c.
In a thread, how can I programmatically stop the bottle web server ?
I didn't find a stop() method or something like that in the documentation. Is there a reason ?

For the default (WSGIRef) server, this is what I do (actually it is a cleaner approach of Vikram Pudi's suggestion):
from bottle import Bottle, ServerAdapter
class MyWSGIRefServer(ServerAdapter):
server = None
def run(self, handler):
from wsgiref.simple_server import make_server, WSGIRequestHandler
if self.quiet:
class QuietHandler(WSGIRequestHandler):
def log_request(*args, **kw): pass
self.options['handler_class'] = QuietHandler
self.server = make_server(self.host, self.port, handler, **self.options)
self.server.serve_forever()
def stop(self):
# self.server.server_close() <--- alternative but causes bad fd exception
self.server.shutdown()
app = Bottle()
#app.route('/')
def index():
return 'Hello world'
#app.route('/stop') # not working from here, it has to come from another thread
def stopit():
server.stop()
server = MyWSGIRefServer(port=80)
try:
app.run(server=server)
except:
print('Bye')
When I want to stop the bottle application, from another thread, I do the following:
server.stop()

I had trouble closing a bottle server from within a request as bottle seems to run requests in subprocesses.
I eventually found the solution was to do:
sys.stderr.close()
inside the request (that got passed up to the bottle server and axed it).

An updated version of mike's answer.
from bottlepy.bottle import WSGIRefServer, run
from threading import Thread
import time
class MyServer(WSGIRefServer):
def run(self, app): # pragma: no cover
from wsgiref.simple_server import WSGIRequestHandler, WSGIServer
from wsgiref.simple_server import make_server
import socket
class FixedHandler(WSGIRequestHandler):
def address_string(self): # Prevent reverse DNS lookups please.
return self.client_address[0]
def log_request(*args, **kw):
if not self.quiet:
return WSGIRequestHandler.log_request(*args, **kw)
handler_cls = self.options.get('handler_class', FixedHandler)
server_cls = self.options.get('server_class', WSGIServer)
if ':' in self.host: # Fix wsgiref for IPv6 addresses.
if getattr(server_cls, 'address_family') == socket.AF_INET:
class server_cls(server_cls):
address_family = socket.AF_INET6
srv = make_server(self.host, self.port, app, server_cls, handler_cls)
self.srv = srv ### THIS IS THE ONLY CHANGE TO THE ORIGINAL CLASS METHOD!
srv.serve_forever()
def shutdown(self): ### ADD SHUTDOWN METHOD.
self.srv.shutdown()
# self.server.server_close()
def begin():
run(server=server)
server = MyServer(host="localhost", port=8088)
Thread(target=begin).start()
time.sleep(2) # Shut down server after 2 seconds
server.shutdown()
The class WSGIRefServer is entirely copied with only 1 line added to the run() method is added. Also add a simple shutdown() method. Unfortunately, this is necessary because of the way bottle creates the run() method.

You can make your thread a daemon by setting the daemon property to True before calling start.
mythread = threading.Thread()
mythread.daemon = True
mythread.start()
A deamon thread will stop whenever the main thread that it is running in is killed or dies. The only problem is that you won't be able to make the thread run any code on exit and if the thread is in the process of doing something, it will be stopped immediately without being able to finish the method it is running.
There's no way in Python to actually explicitly stop a thread. If you want to have more control over being able to stop your server you should look into Python Processes from the multiprocesses module.

Since bottle doesn't provide a mechanism, it requires a hack. This is perhaps the cleanest one if you are using the default WSGI server:
In bottle's code the WSGI server is started with:
srv.serve_forever()
If you have started bottle in its own thread, you can stop it using:
srv.shutdown()
To access the srv variable in your code, you need to edit the bottle source code and make it global. After changing the bottle code, it would look like:
srv = None #make srv global
class WSGIRefServer(ServerAdapter):
def run(self, handler): # pragma: no cover
global srv #make srv global
...

Here's one option: provide custom server (same as default), that records itself:
import bottle
class WSGI(bottle.WSGIRefServer):
instances = []
def run(self, *args, **kw):
self.instances.append(self)
super(WSGI, self).run(*args, **kw)
# some other thread:
bottle.run(host=ip_address, port=12345, server=WSGI)
# control thread:
logging.warn("servers are %s", WSGI.instances)

This is exactly the same method than sepero's and mike's answer, but now much simpler with Bottle version 0.13+:
from bottle import W, run, route
from threading import Thread
import time
#route('/')
def index():
return 'Hello world'
def shutdown():
time.sleep(5)
server.srv.shutdown()
server = WSGIRefServer(port=80)
Thread(target=shutdown).start()
run(server=server)
Also related: https://github.com/bottlepy/bottle/issues/1229 and https://github.com/bottlepy/bottle/issues/1230.
Another example with a route http://localhost/stop to do the shutdown:
from bottle import WSGIRefServer, run, route
from threading import Thread
#route('/')
def index():
return 'Hello world'
#route('/stop')
def stopit():
Thread(target=shutdown).start()
def shutdown():
server.srv.shutdown()
server = WSGIRefServer(port=80)
run(server=server)
PS: it requires at least Bottle 0.13dev.

Console log of Bottle server tells us that the official way of shutting down the server is "Hit Ctrl-C":
Bottle v0.12.19 server starting up (using WSGIRefServer())...
Listening on http://localhost:8080/
Hit Ctrl-C to quit.
Why not simply follow it programmatically?
Hitting "Ctrl-C" is nothing but sending SIGINT to the process, and we can achieve it with just built-in modules:
Get current PID with os.getpid().
Kill the process with os.kill(). Remember passing SIGINT so it will be exactly same as "Hit Ctrl-C".
Wrap the 'kill' in another thread and start it after a few seconds so the client won't get error.
Here is the server code:
from bottle import route, run
import os
import signal
from threading import Thread
import time
#route('/hello')
def return_hello():
return 'Hello'
#route('/stop')
def handle_stop_request():
# Handle "stop server" request from client: start a new thread to stop the server
Thread(target=shutdown_server).start()
return ''
def shutdown_server():
time.sleep(2)
pid = os.getpid() # Get process ID of the current Python script
os.kill(pid, signal.SIGINT)
# Kill the current script process with SIGINT, which does same as "Ctrl-C"
run(host='localhost', port=8080)
Here is the client code:
import requests
def request_service(service_key):
url = f'http://127.0.0.1:8080/{service_key}'
response = requests.get(url)
content = response.content.decode('utf-8')
print(content)
request_service('hello')
request_service('stop')
Note that in function "handle_stop_request" we didn't stop the server immediately but rather started a thread then returned empty string. With this mechanism, when a client requests "http://127.0.0.1:8080/stop", it can get response (the empty string) normally. After that, the server will shutdown. If we otherwise shutdown the server in function "handle_stop_request", the server will close the connection before returning to the client, and hence the client will get "ConnectionError".
Server side output:
Bottle v0.12.19 server starting up (using WSGIRefServer())...
Listening on http://localhost:8080/
Hit Ctrl-C to quit.
127.0.0.1 - - [23/Nov/2021 11:18:08] "GET /hello HTTP/1.1" 200 5
127.0.0.1 - - [23/Nov/2021 11:18:08] "GET /stop HTTP/1.1" 200 0
Client side output:
Hello
The code was tested under Python 3.7 and Bottle 0.12.

I suppose that the bottle webserver runs forever until it terminates. There are no methonds like stop().
But you can make something like this:
from bottle import route, run
import threading, time, os, signal, sys, operator
class MyThread(threading.Thread):
def __init__(self, target, *args):
threading.Thread.__init__(self, target=target, args=args)
self.start()
class Watcher:
def __init__(self):
self.child = os.fork()
if self.child == 0:
return
else:
self.watch()
def watch(self):
try:
os.wait()
except KeyboardInterrupt:
print 'KeyBoardInterrupt'
self.kill()
sys.exit()
def kill(self):
try:
os.kill(self.child, signal.SIGKILL)
except OSError: pass
def background_process():
while 1:
print('background thread running')
time.sleep(1)
#route('/hello/:name')
def index(name='World'):
return '<b>Hello %s!</b>' % name
def main():
Watcher()
MyThread(background_process)
run(host='localhost', port=8080)
if __name__ == "__main__":
main()
Then you can use Watcher.kill() when you need to kill your server.
Here is the code of run() function of the bottle:
try:
app = app or default_app()
if isinstance(app, basestring):
app = load_app(app)
if not callable(app):
raise ValueError("Application is not callable: %r" % app)
for plugin in plugins or []:
app.install(plugin)
if server in server_names:
server = server_names.get(server)
if isinstance(server, basestring):
server = load(server)
if isinstance(server, type):
server = server(host=host, port=port, **kargs)
if not isinstance(server, ServerAdapter):
raise ValueError("Unknown or unsupported server: %r" % server)
server.quiet = server.quiet or quiet
if not server.quiet:
stderr("Bottle server starting up (using %s)...\n" % repr(server))
stderr("Listening on http://%s:%d/\n" % (server.host, server.port))
stderr("Hit Ctrl-C to quit.\n\n")
if reloader:
lockfile = os.environ.get('BOTTLE_LOCKFILE')
bgcheck = FileCheckerThread(lockfile, interval)
with bgcheck:
server.run(app)
if bgcheck.status == 'reload':
sys.exit(3)
else:
server.run(app)
except KeyboardInterrupt:
pass
except (SyntaxError, ImportError):
if not reloader: raise
if not getattr(server, 'quiet', False): print_exc()
sys.exit(3)
finally:
if not getattr(server, 'quiet', False): stderr('Shutdown...\n')
As you can see there are no other way to get off the run loop, except some exceptions.
The server.run function depends on the server you use, but there are no universal quit-method anyway.

This equally kludgy hack has the advantage that is doesn't have you copy-paste any code from bottle.py:
# The global server instance.
server = None
def setup_monkey_patch_for_server_shutdown():
"""Setup globals to steal access to the server reference.
This is required to initiate shutdown, unfortunately.
(Bottle could easily remedy that.)"""
# Save the original function.
from wsgiref.simple_server import make_server
# Create a decorator that will save the server upon start.
def stealing_make_server(*args, **kw):
global server
server = make_server(*args, **kw)
return server
# Patch up wsgiref itself with the decorated function.
import wsgiref.simple_server
wsgiref.simple_server.make_server = stealing_make_server
setup_monkey_patch_for_server_shutdown()
def shutdown():
"""Request for the server to shutdown."""
server.shutdown()

I've found this solution to be the easiest, but it does require that the "psutil" package is installed, to get the current process. It also requires the "signals" module, but that's part of the standard library.
#route('/shutdown')
def shutdown():
current_process = psutil.Process()
current_process.send_signal(signal.CTRL_C_EVENT)
return 'Shutting down the web server'
Hope that's of use to someone!

This question was top in my google search, so i will post my answer:
When the server is started with the Bottle() class, it has a method close() to stop the server. From the source code:
""" Close the application and all installed plugins. """
For example:
class Server:
def __init__(self, host, port):
self._host = host
self._port = port
self._app = Bottle()
def stop(self):
# close ws server
self._app.close()
def foo(self):
# More methods, routes...
Calling stop method will stop de server.

Related

Flask Web server App on Window Python program is not able to shutdown on program exist

I have python version 3.9, I am using the default development web server that is provided by the flask on windows OS (laptop). I need to run the app on Windows machine, and it should be able to start and stop the app on demand.
Here is what I tried so far. when I run the application either in VS code, I need to force stop the app, If running in the command windows I have to kill the python app from task manager.
With the following code changes I can exit the app by sending the SIGTERM to proess itself, as both the Flask, and simplehttpserver do not provide an API for exiting the process.
from flask import Flask, request, g, make_response
import queue
import time
import os
from threading import Thread
import signal
import ctypes
from multiprocessing import Process
import sys
OUTPUT_EMPTY_LIMIT = 3
DEFAULT_PORT = 9090
SHUTDOWN = False
EXIT_CHARS = "Qq\x11\x18" # 'Q', 'q', Ctrl-Q, ESC
print("Name = {}".format(__name__))
app = Flask(__name__)
def shutdown_app():
global SHUTDOWN
SHUTDOWN = True
#app.before_request
def before_request():
if SHUTDOWN:
return 'Shutting down...'
def run_app(port, host, debug, threaded):
app.run(port=port, host=host, debug=debug, threaded=threaded)
#app.route("/")
def app_help():
return "Hello, World from help!"
#app.route("/square/<int:number>")
def square(number):
return str(number**2)
# This doesnt kill the app as well.
#app.route("/stop")
def stop():
func = request.environ.get('werkzeug.server.shutdown')
if func is None:
raise RuntimeError('Not running with the werkzeug Server')
func()
return 'Server shutting down...'
exit(0)
if __name__ == "__main__":
port = 5000
host = '0.0.0.0'
debug = False
threaded = True
process = Process(target=run_app, args=(port, host, debug, threaded))
process.start()
while True:
ch = input("Enter 'Q' to stop server and exit: ")
if (ch == 'Q'):
break
shutdown_app()
process.terminate()
#process.kill()
#process.join()
print("Exiting the app")
# os.kill(os.getpid(), signal.SIGINT)
os._exit(0)
I am new to Python and have tried both google search and chatGPT to find a way to shutdown Flask App and the web server that is started on a windows operating system.
I have also tried the other answeres on the stack overflow 15562446
I also tried the link Shutdown The Simple Server
I've found while testing that werkzeug.server.shutdown does nothing, and it needs to be shutdown using sys.exit(0) on my windows machine.
In your code a value is returned before the exit() is run - therefore it does not shut down. One way to do what you want is using deferred callbacks.
from flask import after_this_request
#app.get('/stop')
def shutdown():
#after_this_request
def response_processor(response):
#response.call_on_close
def shutdown():
import sys
sys.exit("Stop API Called")
return response
return 'Server shutting down...',200

flask: stop server after exception

I would like to stop my flask server as soon as an unhandled exception occurs.
Here is an example:
from flask import Flask
app = Flask(__name__)
#app.route('/')
def hello_world():
1/0 # argh, exception
return 'Hello World!'
if __name__ == '__main__':
app.run(port=12345)
If you run this and go to localhost:12345, your browser tells you "internal server error" and the python console logs a DivisionByZero exception.
But the server app doesn't crash. Flask wraps your routes into its own error handling and it only prints the exception.
I would like to make the server stop as soon as a route produces an exception. But I didn't find this behaviour in the API. You can specify an errorhandler but that is only to give custom error messages to the client after your route failed.
Stopping Flask requires getting into Werkzeug internals. See http://flask.pocoo.org/snippets/67/
Extracted from a single-user app:
from flask import request
#app.route('/quit')
def shutdown():
...
shutdown_hook = request.environ.get('werkzeug.server.shutdown')
if shutdown_hook is not None:
shutdown_hook()
return Response("Bye", mimetype='text/plain')
The shutdown_hook bit is what you'd need in an exception handler.
from multiprocessing import Process
server = Process(target=app.run)
server.start()
# ...
server.terminate()
server.join()
or if Threaded to run Flask Web Server in background around other python code:
import threading
import ctypes
webserverurl = 127.0.0.1
webserverport = 8080
class thread_with_exception(threading.Thread):
def __init__(self, name):
threading.Thread.__init__(self)
self.name = name
self.interval = 1
self.daemon = True
def __del__(self):
print(f"Thread terminated");
def run(self):
# target function of the thread class
try:
while True:
print(f"{self.name} Waiting for Web")
app.run(host=webserverurl, port=webserverport)
finally:
print('ended')
def get_id(self):
# returns id of the respective thread
if hasattr(self, '_thread_id'):
return self._thread_id
for id, thread in threading._active.items():
if thread is self:
return id
def raise_exception(self):
thread_id = self.get_id()
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(thread_id), ctypes.py_object(SystemExit))
print(f"{self.name} terminated")
if res > 1:
ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(thread_id), 0)
print('Exception raise failure')
t1 = thread_with_exception('Web Server 1')
t1.start()
time.sleep(10)
t1.raise_exception()
# del t1
credit to Andrew Abrahamowicz here:
https://code.activestate.com/recipes/496960-thread2-killable-threads
ctypes.c_long( required for Linux else Windows only: https://stackoverflow.com/a/61638782/3426192
other ways to do this here: https://w3guides.com/tutorial/how-to-close-a-flask-web-server-with-python

Why can't this Python multiprocessing process be terminated from Kivy?

I'm trying to run a django development server from within a Kivy application. This did work out quite well so far.
Now i want to allow the user to continue working with the program while the server is running. My idea was to create a multiprocessing.Process for the httpd.serve_forever() to avoid a complete lock of the main program. Did work well. This is the code in my internal_django module:
import multiprocessing
import os
import time
from wsgiref.simple_server import make_server
def django_wsgi_application():
PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
settings_module = "djangosettings"#%s.djangosettings" % PROJECT_ROOT.split(os.sep)[-1]
os.environ.update({"DJANGO_SETTINGS_MODULE":settings_module})
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
return application
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
class DjangoServer():
__metaclass__ = Singleton
def start(self):
self.httpd = make_server('', 8000, django_wsgi_application())
self.server = multiprocessing.Process(target=self.httpd.serve_forever)
self.server.start()
print "Now serving on port 8000..."
print "Server Process PID = %s" %self.server.pid
def stop(self):
print("shutdown initiated")
print "Server Process PID = %s" %self.server.pid
while self.server.is_alive():
self.server.terminate()
print("Server should have shut down")
time.sleep(1)
print("Server is_alive: %s" %self.server.is_alive())
self.server.join()
print("server process joined")
if __name__ == "__main__":
server = DjangoServer()
server.start()
time.sleep(3)
server.stop()
When i run this code, everything works as expected. This is what is being put out in the console:
Now serving on port 8000...
Server Process PID = 1406
shutdown initiated
Server Process PID = 1406
Server should have shut down
Server is_alive: False
server process joined
Next step was to provide a way to stop the server from within the Kivy application. For that i just wanted to use my DjangoServer class as i did before:
from internal_django import DjangoServer
class StartScreen(Screen):
def start_server(self):
server = DjangoServer()
server.start()
class StopScreen(Screen):
def stop_server(self):
server = DjangoServer()
server.stop()
But when doing so, the process once started never quits. My first idea was that the Singleton did not work as expected, and that i try to quit the wrong process. but as you can see in the output, the PID's are identical. The server receives the terminate command, but just continues to work. This is what the console looks like:
Now serving on port 8000...
Server Process PID = 1406
shutdown initiated
Server Process PID = 1406
Server should have shut down
Server should have shut down
Server should have shut down
Server should have shut down
Server should have shut down
Server should have shut down
Server should have shut down
Server should have shut down
(and so on, until i manually kill the server process)
Am i using multiprocessing in a completely wrong way? Is Kivy somehow interfering with the process?
I think the problems here might be two:
A signal handler is intercepting the TERM request sent by Process.terminate() and ignores it. To verify that simply use the signal.getsignal(signal.SIGTERM) from within the new process and print the results. To circumvent such issue you can reset the default behavior with signal.signal(signal.SIGTERM, signal.SIG_DFL), nevertheless keep in mind that there might be a reason why SIGTERM is silenced by the frameworks (I'm not familiar neither with Django nor with Kivy).
If you're using Python 2 you must consider that the interpreter does not process signals if it's blocked on a synchronization primitive from threading library (Locks, Semaphores..) or on a native C call. The serve_forever() function might fall in these cases (use the force of the source!). Quick check could be trying to run the code on Python 3 and see whether it works or not.
A quick and dirty solution consists in waiting a small amount of time and send a SIGKILL if the process is still alive.
import os
import signal
process.terminate()
process.join(1)
if process.is_alive() and os.name != 'nt':
try:
os.kill(process.pid, signal.SIGKILL)
process.join()
except OSError:
return # process might have died while checking it
On windows you cannot kill a process in such simple way that's why I test the os.name.
It's a pretty raw approach so I'd rather recommend to find the cause of the issue.
What happens if you call terminate(), then join() and skip the while loop? Also, I shuffle the code a little and factor some code into _create_server(). Please let me know if this works out for you.
class DjangoServer():
__metaclass__ = Singleton
def _create_server(self):
httpd = make_server('', 8000, django_wsgi_application())
print "Now serving on port {}...".format(httpd.server_port)
httpd.serve_forever()
def start(self):
self.server = multiprocessing.Process(target=self._create_server)
self.server.start()
print "Server Process PID = %s" %self.server.pid
def stop(self):
print("shutdown initiated")
print "Server Process PID = %s" %self.server.pid
self.server.terminate()
self.server.join()
print("server process terminated")

How do I stop Tornado web server? [duplicate]

This question already has answers here:
Stopping a tornado application
(3 answers)
Closed 1 year ago.
I've been playing around a bit with the Tornado web server and have come to a point where I want to stop the web server (for example during unit testing). The following simple example exists on the Tornado web page:
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
application = tornado.web.Application([
(r"/", MainHandler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
Once tornado.ioloop.IOLoop.instance().start() is called, it blocks the program (or current thread). Reading the source code for the IOLoop object gives this example in the documentation for the stop function:
To use asynchronous methods from otherwise-synchronous code (such as
unit tests), you can start and stop the event loop like this:
ioloop = IOLoop()
async_method(ioloop=ioloop, callback=ioloop.stop)
ioloop.start()
ioloop.start() will return after async_method has run its callback,
whether that callback was invoked before or after ioloop.start.
However, I have no idea how to integrate this into my program. I actually have a class that encapsulates the web server (having it's own start and stop functions), but as soon as I call start, the program (or tests) will of course block anyway.
I've tried to start the web server in another process (using the multiprocessing package). This is the class that is wrapping the web server:
class Server:
def __init__(self, port=8888):
self.application = tornado.web.Application([ (r"/", Handler) ])
def server_thread(application, port):
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(port)
tornado.ioloop.IOLoop.instance().start()
self.process = Process(target=server_thread,
args=(self.application, port,))
def start(self):
self.process.start()
def stop(self):
ioloop = tornado.ioloop.IOLoop.instance()
ioloop.add_callback(ioloop.stop)
However, stop does not seem to entirely stop the web server since it is still running in the next test, even with this test setup:
def setup_method(self, _function):
self.server = Server()
self.server.start()
time.sleep(0.5) # Wait for web server to start
def teardown_method(self, _function):
self.kstore.stop()
time.sleep(0.5)
How can I start and stop a Tornado web server from within a Python program?
I just ran into this and found this issue myself, and using info from this thread came up with the following. I simply took my working stand alone Tornado code (copied from all the examples) and moved the actual starting code into a function. I then called the function as a threading thread. My case different as the threading call was done from my existing code where I just imported the startTornado and stopTornado routines.
The suggestion above seemed to work great, so I figured I would supply the missing example code. I tested this code under Linux on a FC16 system (and fixed my initial type-o).
import tornado.ioloop, tornado.web
class Handler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
application = tornado.web.Application([ (r"/", Handler) ])
def startTornado():
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
def stopTornado():
tornado.ioloop.IOLoop.instance().stop()
if __name__ == "__main__":
import time, threading
threading.Thread(target=startTornado).start()
print "Your web server will self destruct in 2 minutes"
time.sleep(120)
stopTornado()
Hope this helps the next person.
Here is the solution how to stop Torando from another thread. Schildmeijer provided a good hint, but it took me a while to actually figure the final example that works.
Please see below:
import threading
import tornado.ioloop
import tornado.web
import time
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world!\n")
def start_tornado(*args, **kwargs):
application = tornado.web.Application([
(r"/", MainHandler),
])
application.listen(8888)
print "Starting Torando"
tornado.ioloop.IOLoop.instance().start()
print "Tornado finished"
def stop_tornado():
ioloop = tornado.ioloop.IOLoop.instance()
ioloop.add_callback(ioloop.stop)
print "Asked Tornado to exit"
def main():
t = threading.Thread(target=start_tornado)
t.start()
time.sleep(5)
stop_tornado()
t.join()
if __name__ == "__main__":
main()
In case you do no want to bother with threads, you could catch a keyboard interrupt signal :
try:
tornado.ioloop.IOLoop.instance().start()
# signal : CTRL + BREAK on windows or CTRL + C on linux
except KeyboardInterrupt:
tornado.ioloop.IOLoop.instance().stop()
There is a problem with Zaar Hai's solution, namely that it leaves the socket open. The reason I was looking for a solution to stop Tornado is I'm running unit tests against my app server and I needed a way to start/stop the server between tests to have a clear state (empty session, etc.). By leaving the socket open, the second test always ran into an Address already in use error. So I came up with the following:
import logging as log
from time import sleep
from threading import Thread
import tornado
from tornado.httpserver import HTTPServer
server = None
thread = None
def start_app():
def start():
global server
server = HTTPServer(create_app())
server.listen(TEST_PORT, TEST_HOST)
tornado.ioloop.IOLoop.instance().start()
global thread
thread = Thread(target=start)
thread.start()
# wait for the server to fully initialize
sleep(0.5)
def stop_app():
server.stop()
# silence StreamClosedError Tornado is throwing after it is stopped
log.getLogger().setLevel(log.FATAL)
ioloop = tornado.ioloop.IOLoop.instance()
ioloop.add_callback(ioloop.stop)
thread.join()
So the main idea here is to keep a reference to the HTTPServer instance and call its stop() method. And create_app() just returns an Application instance configured with handlers. Now you can use these methods in your unit tests like this:
class FoobarTest(unittest.TestCase):
def setUp(self):
start_app()
def tearDown(self):
stop_app()
def test_foobar(self):
# here the server is up and running, so you can make requests to it
pass
To stop the entire ioloop you simply invoke the ioloop.stop method when you have finished the unit test. (Remember that the only (documented) thread safe method is ioloop.add_callback, ie. if the unit tests is executed by another thread, you could wrap the stop invocation in a callback)
If its enough to stop the http web server you invoke the httpserver.stop() method
If you need this behavior for unit testing, take a look at tornado.testing.AsyncTestCase.
By default, a new IOLoop is constructed for each test and is available as self.io_loop. This IOLoop should be used in the construction of HTTP clients/servers, etc. If the code being tested requires a global IOLoop, subclasses should override get_new_ioloop to return it.
If you need to start and stop an IOLoop for some other purpose and can't call ioloop.stop() from a callback for some reason, a multi-threaded implementation is possible. To avoid race conditions, however, you need to synchronize access to the ioloop, which is actually impossible. Something like the following will result in deadlock:
Thread 1:
with lock:
ioloop.start()
Thread 2:
with lock:
ioloop.stop()
because thread 1 will never release the lock (start() is blocking) and thread 2 will wait till the lock is released to stop the ioloop.
The only way to do it is for thread 2 to call ioloop.add_callback(ioloop.stop), which will call stop() on thread 1 in the event loop's next iteration. Rest assured, ioloop.add_callback() is thread-safe.
Tornado's IOloop.instance() has trouble stopping from an external signal when run under multiprocessing.Process.
The only solution I came up with that works consistently, is by using Process.terminate():
import tornado.ioloop, tornado.web
import time
import multiprocessing
class Handler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
application = tornado.web.Application([ (r"/", Handler) ])
class TornadoStop(Exception):
pass
def stop():
raise TornadoStop
class worker(multiprocessing.Process):
def __init__(self):
multiprocessing.Process.__init__(self)
application.listen(8888)
self.ioloop = tornado.ioloop.IOLoop.instance()
def run(self):
self.ioloop.start()
def stop(self, timeout = 0):
self.ioloop.stop()
time.sleep(timeout)
self.terminate()
if __name__ == "__main__":
w = worker()
print 'starting server'
w.start()
t = 2
print 'waiting {} seconds before stopping'.format(t)
for i in range(t):
time.sleep(1)
print i
print 'stopping'
w.stop(1)
print 'stopped'
We want to use a multiprocessing.Process with a tornado.ioloop.IOLoop to work around the cPython GIL for performance and independency. To get access to the IOLoop we need to use Queue to pass the shutdown signal through.
Here is a minimalistic example:
class Server(BokehServer)
def start(self, signal=None):
logger.info('Starting server on http://localhost:%d'
% (self.port))
if signal is not None:
def shutdown():
if not signal.empty():
self.stop()
tornado.ioloop.PeriodicCallback(shutdown, 1000).start()
BokehServer.start(self)
self.ioloop.start()
def stop(self, *args, **kwargs): # args important for signals
logger.info('Stopping server...')
BokehServer.stop(self)
self.ioloop.stop()
The Process
import multiprocessing as mp
import signal
from server import Server # noqa
class ServerProcess(mp.Process):
def __init__(self, *args, **kwargs):
self.server = Server(*args, **kwargs)
self.shutdown_signal = _mp.Queue(1)
mp.Process.__init__(self)
signal.signal(signal.SIGTERM, self.server.stop)
signal.signal(signal.SIGINT, self.server.stop)
def run(self):
self.server.start(signal=self.shutdown_signal)
def stop(self):
self.shutdown_signal.put(True)
if __name__ == '__main__':
p = ServerProcess()
p.start()
Cheers!
Just add this before the start():
IOLoop.instance().add_timeout(10,IOLoop.instance().stop)
It will register the stop function as a callback in the loop and lauch it 10 second after the start

How to stop BaseHTTPServer.serve_forever() in a BaseHTTPRequestHandler subclass?

I am running my HTTPServer in a separate thread (using the threading module which has no way to stop threads...) and want to stop serving requests when the main thread also shuts down.
The Python documentation states that BaseHTTPServer.HTTPServer is a subclass of SocketServer.TCPServer, which supports a shutdown method, but it is missing in HTTPServer.
The whole BaseHTTPServer module has very little documentation :(
Another way to do it, based on http://docs.python.org/2/library/basehttpserver.html#more-examples, is: instead of serve_forever(), keep serving as long as a condition is met, with the server checking the condition before and after each request. For example:
import CGIHTTPServer
import BaseHTTPServer
KEEP_RUNNING = True
def keep_running():
return KEEP_RUNNING
class Handler(CGIHTTPServer.CGIHTTPRequestHandler):
cgi_directories = ["/cgi-bin"]
httpd = BaseHTTPServer.HTTPServer(("", 8000), Handler)
while keep_running():
httpd.handle_request()
I should start by saying that "I probably wouldn't do this myself, but I have in the past". The serve_forever (from SocketServer.py) method looks like this:
def serve_forever(self):
"""Handle one request at a time until doomsday."""
while 1:
self.handle_request()
You could replace (in subclass) while 1 with while self.should_be_running, and modify that value from a different thread. Something like:
def stop_serving_forever(self):
"""Stop handling requests"""
self.should_be_running = 0
# Make a fake request to the server, to really force it to stop.
# Otherwise it will just stop on the next request.
# (Exercise for the reader.)
self.make_a_fake_request_to_myself()
Edit: I dug up the actual code I used at the time:
class StoppableRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer):
stopped = False
allow_reuse_address = True
def __init__(self, *args, **kw):
SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, *args, **kw)
self.register_function(lambda: 'OK', 'ping')
def serve_forever(self):
while not self.stopped:
self.handle_request()
def force_stop(self):
self.server_close()
self.stopped = True
self.create_dummy_request()
def create_dummy_request(self):
server = xmlrpclib.Server('http://%s:%s' % self.server_address)
server.ping()
The event-loops ends on SIGTERM, Ctrl+C or when shutdown() is called.
server_close() must be called after server_forever() to close the listening socket.
import http.server
class StoppableHTTPServer(http.server.HTTPServer):
def run(self):
try:
self.serve_forever()
except KeyboardInterrupt:
pass
finally:
# Clean-up server (close socket, etc.)
self.server_close()
Simple server stoppable with user action (SIGTERM, Ctrl+C, ...):
server = StoppableHTTPServer(("127.0.0.1", 8080),
http.server.BaseHTTPRequestHandler)
server.run()
Server running in a thread:
import threading
server = StoppableHTTPServer(("127.0.0.1", 8080),
http.server.BaseHTTPRequestHandler)
# Start processing requests
thread = threading.Thread(None, server.run)
thread.start()
# ... do things ...
# Shutdown server
server.shutdown()
thread.join()
In my python 2.6 installation, I can call it on the underlying TCPServer - it still there inside your HTTPServer:
TCPServer.shutdown
>>> import BaseHTTPServer
>>> h=BaseHTTPServer.HTTPServer(('',5555), BaseHTTPServer.BaseHTTPRequestHandler)
>>> h.shutdown
<bound method HTTPServer.shutdown of <BaseHTTPServer.HTTPServer instance at 0x0100D800>>
>>>
I think you can use [serverName].socket.close()
In python 2.7, calling shutdown() works but only if you are serving via serve_forever, because it uses async select and a polling loop. Running your own loop with handle_request() ironically excludes this functionality because it implies a dumb blocking call.
From SocketServer.py's BaseServer:
def serve_forever(self, poll_interval=0.5):
"""Handle one request at a time until shutdown.
Polls for shutdown every poll_interval seconds. Ignores
self.timeout. If you need to do periodic tasks, do them in
another thread.
"""
self.__is_shut_down.clear()
try:
while not self.__shutdown_request:
# XXX: Consider using another file descriptor or
# connecting to the socket to wake this up instead of
# polling. Polling reduces our responsiveness to a
# shutdown request and wastes cpu at all other times.
r, w, e = select.select([self], [], [], poll_interval)
if self in r:
self._handle_request_noblock()
finally:
self.__shutdown_request = False
self.__is_shut_down.set()
Heres part of my code for doing a blocking shutdown from another thread, using an event to wait for completion:
class MockWebServerFixture(object):
def start_webserver(self):
"""
start the web server on a new thread
"""
self._webserver_died = threading.Event()
self._webserver_thread = threading.Thread(
target=self._run_webserver_thread)
self._webserver_thread.start()
def _run_webserver_thread(self):
self.webserver.serve_forever()
self._webserver_died.set()
def _kill_webserver(self):
if not self._webserver_thread:
return
self.webserver.shutdown()
# wait for thread to die for a bit, then give up raising an exception.
if not self._webserver_died.wait(5):
raise ValueError("couldn't kill webserver")
This is a simplified version of Helgi's answer for python 3.7:
import threading
import time
from http.server import ThreadingHTTPServer, SimpleHTTPRequestHandler
class MyServer(threading.Thread):
def run(self):
self.server = ThreadingHTTPServer(('localhost', 8000), SimpleHTTPRequestHandler)
self.server.serve_forever()
def stop(self):
self.server.shutdown()
if __name__ == '__main__':
s = MyServer()
s.start()
print('thread alive:', s.is_alive()) # True
time.sleep(2)
s.stop()
print('thread alive:', s.is_alive()) # False
This method I use successfully (Python 3) to stop the server from the web application itself (a web page):
import http.server
import os
import re
class PatientHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
stop_server = False
base_directory = "/static/"
# A file to use as an "server stopped user information" page.
stop_command = "/control/stop.html"
def send_head(self):
self.path = os.path.normpath(self.path)
if self.path == PatientHTTPRequestHandler.stop_command and self.address_string() == "127.0.0.1":
# I wanted that only the local machine could stop the server.
PatientHTTPRequestHandler.stop_server = True
# Allow the stop page to be displayed.
return http.server.SimpleHTTPRequestHandler.send_head(self)
if self.path.startswith(PatientHTTPRequestHandler.base_directory):
return http.server.SimpleHTTPRequestHandler.send_head(self)
else:
return self.send_error(404, "Not allowed", "The path you requested is forbidden.")
if __name__ == "__main__":
httpd = http.server.HTTPServer(("127.0.0.1", 8080), PatientHTTPRequestHandler)
# A timeout is needed for server to check periodically for KeyboardInterrupt
httpd.timeout = 1
while not PatientHTTPRequestHandler.stop_server:
httpd.handle_request()
This way, pages served via base address http://localhost:8080/static/ (example http://localhost:8080/static/styles/common.css) will be served by the default handler, an access to http://localhost:8080/control/stop.html from the server's computer will display stop.html then stop the server, any other option will be forbidden.
I tried all above possible solution and ended up with having a "sometime" issue - somehow it did not really do it - so I ended up making a dirty solution that worked all the time for me:
If all above fails, then brute force kill your thread using something like this:
import subprocess
cmdkill = "kill $(ps aux|grep '<name of your thread> true'|grep -v 'grep'|awk '{print $2}') 2> /dev/null"
subprocess.Popen(cmdkill, stdout=subprocess.PIPE, shell=True)
import http.server
import socketserver
import socket as sck
import os
import threading
class myserver:
def __init__(self, PORT, LOCATION):
self.thrd = threading.Thread(None, self.run)
self.Directory = LOCATION
self.Port = PORT
hostname = sck.gethostname()
ip_address = sck.gethostbyname(hostname)
self.url = 'http://' + ip_address + ':' + str(self.Port)
Handler = http.server.SimpleHTTPRequestHandler
self.httpd = socketserver.TCPServer(("", PORT), Handler)
print('Object created, use the start() method to launch the server')
def run(self):
print('listening on: ' + self.url )
os.chdir(self.Directory)
print('myserver object started')
print('Use the objects stop() method to stop the server')
self.httpd.serve_forever()
print('Quit handling')
print('Sever stopped')
print('Port ' + str(self.Port) + ' should be available again.')
def stop(self):
print('Stopping server')
self.httpd.shutdown()
self.httpd.server_close()
print('Need just one more request before shutting down'
def start(self):
self.thrd.start()
def help():
helpmsg = '''Create a new server-object by initialising
NewServer = webserver3.myserver(Port_number, Directory_String)
Then start it using NewServer.start() function
Stop it using NewServer.stop()'''
print(helpmsg)
Not a experience python programmer, just wanting to share my comprehensive solution. Mostly based on snippets here and there. I usually import this script in my console and it allows me to set up multiple servers for different locations using their specific ports, sharing my content with other devices on the network.
Here's a context-flavored version for Python 3.7+ which I prefer because it cleans up automatically and you can specify the directory to serve:
from contextlib import contextmanager
from functools import partial
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
from threading import Thread
#contextmanager
def http_server(host: str, port: int, directory: str):
server = ThreadingHTTPServer(
(host, port), partial(SimpleHTTPRequestHandler, directory=directory)
)
server_thread = Thread(target=server.serve_forever, name="http_server")
server_thread.start()
try:
yield
finally:
server.shutdown()
server_thread.join()
def usage_example():
import time
with http_server("127.0.0.1", 8087, "."):
# now you can use the web server
time.sleep(100)

Categories