2

First time using a web framework, hope to get advice on the correct approach.

My aim is to have a server which can return static files based on a url passed in. I use Flask as my web framework and I intent to use CherryPy as my web server. The web describes many ways of setting up Flask with CherryPy and I am not sure if I am doing it correctly.

Resources I have been using:

A simplified version of my Flask app, test.py:

from flask import Flask
from flask import request
from flask import send_from_directory
import os

FOLDER='contents'
ROOT=os.path.abspath(os.path.join('.', FOLDER))

@app.route("/get")
def route_3():
    return flask.send_from_directory(os.path.join(ROOT, 'p01', 'p02'), 'file12.zip', as_attachment=True)

if __name__ == "__main__":
    app.config.update(DEBUG=True)
    app.run()

My script for to run CherryPy:

import os
import cherrypy
from test import app
from cherrypy import wsgiserver

def option_1():
    cherrypy.tree.graft(app, '/')

    # If I comment this out, the server works
    #cherrypy.tree.mount(None, '/', config={
    #        '/': {
    #                'tools.staticdir.on': True,
    #                'tools.staticdir.dir': app.static_folder
    #            },
    #    })

    cherrypy.config.update({'server.socket_port': 5000})

    cherrypy.engine.start()
    cherrypy.engine.block()


def option2():
    d = wsgiserver.WSGIPathInfoDispatcher({'/': app})
    server = wsgiserver.CherryPyWSGIServer(('127.0.0.1', 5000), d)
    try:
       server.start()
    except KeyboardInterrupt:
       server.stop()

 if __name__ == '__main__':
    #option_1()
    option_2()

I have two questions:

  1. In terms of setting up CherryPy to run Flask, both option_1 and option_2 works, so what is the difference between the two?
  2. It is recommended to have the web server serve the static files as opposed to the web framework. Am I doing this correctly? When I read the response header, the server is not ' Werkzeug', so I am assuming that the CherryPy server is sending it.
Community
  • 1
  • 1
wschang
  • 448
  • 4
  • 14
  • I use cherrypy as a replacement to the in-built dev server. In deployment I have nginx serve "/static" and then the rest is proxied back to gunicorn or uwsgi. I would not worry to much about this as you should _NEVER_ use the Werkzeug test server to deploy. If you prefer you could just replace the test server with CherryPy as a whole http://flask.pocoo.org/snippets/24/ – Joe Doherty Jan 30 '14 at 09:44
  • @JoeDoherty. Yes that is my goal, to replace the Werkzeug server with CherryPy. The problem is I am not sure if I have done it correctly. Thanks for the link; I did use it for option_2; just forgot to quote it – wschang Jan 30 '14 at 21:34

2 Answers2

7

Option 1 is high-level CherryPy

option_1 actually uses the entirety of the CherryPy web framework (which their documentation refers to as the APPLICATION layer) to mount your WSGI application - plugins, tools, etc. can be utilized fully. To serve static files with CherryPy you would want to change your commented out code to be something like this:

cherrypy.tree.mount(None, '/static', config={
    '/': {
        'tools.staticdir.on': True,
        'tools.staticdir.dir': app.static_folder
    },
})

Option 2 is low-level CherryPy

option_2, on the other hand, simply makes use of CherryPy's WSGI server implementation (the CORE layer) to serve your Flask app - it does not use any of the more framework-like aspects of CherryPy. If you are fine with serving your static files through Flask's routing layer as well, you can even remove the WSGIPathInfoDispatcher middleware and directly mount app under the CherryPyWSGIServer. If you want CherryPy to manage the /static route serving then you will want to mount an instance of cherrypy.tools.staticdir.handler under the /static route, like so:

static_handler = tools.staticdir.handler(section='/', dir=app.static_folder)
d = wsgiserver.WSGIPathInfoDispatcher({'/': app, '/static': static_handler})
Sean Vieira
  • 155,703
  • 32
  • 311
  • 293
  • Thanks @Sean that's useful! In that case, I think option_2 will suit my needs since I do not intent to use CherryPy's framework. An extra question: In what scenario would you choose option_1 though? If you are going to use another web framework to do the work, why would you still want to keep cherrypy's one? – wschang Feb 01 '14 at 12:20
  • @gilamesh - in a case where some portions of your application's functionality could be more easily created with CherryPy, you could avoid re-inventing the wheel for your framework of choice and just mount your CherryPy-based portion of the application alongside your other framework. – Sean Vieira Feb 03 '14 at 13:46
  • I'm having trouble getting option 2 to work - http://stackoverflow.com/questions/29356166/keyerror-with-cherrypy-wsgiserver-serving-static-files – Rob Watts Mar 30 '15 at 21:49
1

If you want to replace the development server provided inside Werkzeug, I use the following in Flask-Script to replace runserver(). This will however in __main__

def runserver():
    """
    Overwriting the Flask Script runserver() default.
    CherryPy is much more stable than the built-in Flask dev server
    """
    debug_app = DebuggedApplication(app, True)
    cherrypy.tree.graft(debug_app, '/')
    cherrypy.config.update({
        'engine.autoreload_on': True,
        'server.socket_port': 5000,
        'server.socket_host': '0.0.0.0'
    })
    try:
        cherrypy.engine.start()
        cherrypy.engine.block()
    except KeyboardInterrupt:
        cherrypy.engine.stop()

If you do not have DebuggedApplication wrapper the DEBUG = True will not work. You may need to adjust this slightly based on your application. It will also provide Ctrl+C

Joe

Joe Doherty
  • 3,778
  • 2
  • 31
  • 37
  • Thanks @Joe for the tip! I am afraid this is not what I am after; I use Werkeug as the development server and plan CherryPy as the acutal one. Reason being is my needs are modest and I am still new to setting up web server – wschang Feb 01 '14 at 12:23