Bottle web framework - How to stop?

前端 未结 11 1229
不知归路
不知归路 2020-12-14 06:55

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, ho

相关标签:
11条回答
  • 2020-12-14 07:27

    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
            ...
    
    0 讨论(0)
  • 2020-12-14 07:31

    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.

    0 讨论(0)
  • 2020-12-14 07:34

    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.

    0 讨论(0)
  • 2020-12-14 07:42

    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()
    
    0 讨论(0)
  • 2020-12-14 07:43

    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.

    0 讨论(0)
  • 2020-12-14 07:43

    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)
    
    0 讨论(0)
提交回复
热议问题