Search code examples
pythoncherrypybottle

How to launch a Bottle application over a CherryPy standalone web server?


I have a python web app developed using the bottle framework. My bottle app is web API that provide methods that return JSon data, so no static content is needed. I am trying to deploy it to production using a CherryPy server which is supposed to be robust for production applications.

My web_api.py file (my bottle app) looks something like this:

from bottle import Bottle, request

app = Bottle()

@app.get('/stuff')
def do_stuff():
    '''
    Method that does stuff.
    '''
    stuff = {'data': 'some data'}
    # Return the environment info as Json data
    return stuff

I have a server.py file to launch the Bottle app over the CherryPy server that looks like this:

from my_package.web_api import app
from cherrypy.wsgiserver import CherryPyWSGIServer

server = CherryPyWSGIServer(
    ('0.0.0.0', 80),
    app,
    server_name='My_App',
    numthreads=30)

server.start()

so when I run my server using this command:

python server.py

My server is successfully started and start listening in port 80 as expected. However once I start my web server I cannot stop it any more. I have tried Ctrl + C which works with the development server but has no effect here. Am I starting the server the right way? How do I stop it once it is running? Is this the correct way to launch a Bottle app over CherryPy?

BTW, I am running python 2.7 in Windows 8.


Solution

  • Your code is correct, you just need to add a try/catch statement:

    from my_package.web_api import app
    from cherrypy.wsgiserver import CherryPyWSGIServer
    
    server = CherryPyWSGIServer(
        ('0.0.0.0', 80),
        app,
        server_name='My_App',
        numthreads=30)
    
    try:
        server.start()
    except KeyboardInterrupt:
        server.stop()
    

    You might wanna also consider to do some logging with wsgi-request-logger or something similar.

    This are three alternative ways on hosting a WSGI application within cherrypy:

    import cherrypy as cp
    from cherrypy.wsgiserver import CherryPyWSGIServer
    from cherrypy.process.servers import ServerAdapter
    
    
    from bottle import Bottle
    
    app = Bottle()
    
    @app.get('/stuff')
    def do_stuff():
        '''
        Method that does stuff.
        '''
        stuff = {'data': 'some dataX'}
        return stuff
    
    def run_decoupled(app, host='0.0.0.0', port=8080, **config):
        server = CherryPyWSGIServer((host, port), app, **config)
        try:
            server.start()
        except KeyboardInterrupt:
            server.stop()
    
    def run_in_cp_tree(app, host='0.0.0.0', port=8080, **config):
        cp.tree.graft(app, '/')
        cp.config.update(config)
        cp.config.update({
            'server.socket_port': port,
            'server.socket_host': host
        })
        cp.engine.signals.subscribe() # optional
        cp.engine.start()
        cp.engine.block()
    
    def run_with_adapter(app, host='0.0.0.0', port=8080, config=None, **kwargs):
        cp.server.unsubscribe()
        bind_addr = (host, port)
        cp.server = ServerAdapter(cp.engine,
                                  CherryPyWSGIServer(bind_addr, app, **kwargs),
                                  bind_addr).subscribe()
        if config:
            cp.config.update(config)
        cp.engine.signals.subscribe() # optional
        cp.engine.start()
        cp.engine.block()
    

    The run_in_cp_tree and run_with_adapter functions are using the cherrypy engine, which enables the use of plugins to have off-the-shelf auto-reload, pidfile, daemonization, signal management and some more goodies, along with the possibility to create one of your own.

    Notice that you can also use the WSGIPathInfoDispatcher to attach multiple wsgi applications on the CherryPyWSGIServer.