Julian Julian - 4 months ago 64
Python Question

How to use Flask-Script and Gunicorn

I'm working on on a Flask app using Flask's built in dev server. I start it using Flask-Script. I want to switch to using Gunicorn as the web server. To do so, do I need to write some sort of integration code between Flask-Script and Gunicorn? Or is Flask-Script irrelevant to running the app using Gunicorn?

Thanks in advance!

Props to @sean-lynch. The following is working, tested code based on his answer.
The changes I made were:


  • Options that aren't recognized by Gunicorn are removed from
    sys.argv
    in
    remove_non_gunicorn_command_line_args()
    before trying to start the server. Otherwise Gunicorn throws an error with a message like this:
    error: unrecognized arguments: --port 5010
    . I remove
    -p
    because, even though it doesn't cause the error, that's only because Gunicorn thinks its the short form of its
    pidfile
    option, which is obviously not what's intended.

  • GunicornServer.handle() signature modified to match the method it overrides i.e. Command.handle()



-

from flask_script import Command
from gunicorn.app.base import Application

class GunicornServer(Command):

description = 'Run the app within Gunicorn'

def __init__(self, host='127.0.0.1', port=8000, workers=6):

self.port = port
self.host = host
self.workers = workers

def get_options(self):
return (
Option('-t', '--host',
dest='host',
default=self.host),

Option('-p', '--port',
dest='port',
type=int,
default=self.port),

Option('-w', '--workers',
dest='workers',
type=int,
default=self.workers),
)

def handle(self, app, *args, **kwargs):

host = kwargs['host']
port = kwargs['port']
workers = kwargs['workers']

def remove_non_gunicorn_command_line_args():
import sys
args_to_remove = ['--port','-p']
def args_filter(name_or_value):
keep = not args_to_remove.count(name_or_value)
if keep:
previous = sys.argv[sys.argv.index(name_or_value) - 1]
keep = not args_to_remove.count(previous)
return keep
sys.argv = filter(args_filter, sys.argv)

remove_non_gunicorn_command_line_args()

from gunicorn import version_info
if version_info < (0, 9, 0):
from gunicorn.arbiter import Arbiter
from gunicorn.config import Config
arbiter = Arbiter(Config({'bind': "%s:%d" % (host, int(port)),'workers': workers}), app)
arbiter.run()
else:
class FlaskApplication(Application):
def init(self, parser, opts, args):
return {
'bind': '{0}:{1}'.format(host, port),
'workers': workers
}

def load(self):
return app

FlaskApplication().run()

manager.add_command('gunicorn', GunicornServer())

Answer

As Dhaivat said, you can just use your Flask app directly with Gunicorn.

If you still want to use Flask-Script, you will need to create a custom Command. I don't have any experience with Gunicorn, but I found a similar solution for Flask-Actions and ported it to Flask-Script, although be warned, it's untested.

from flask_script import Command, Option

class GunicornServer(Command):

    description = 'Run the app within Gunicorn'

    def __init__(self, host='127.0.0.1', port=8000, workers=4):
        self.port = port
        self.host = host
        self.workers = workers

    def get_options(self):
        return (
            Option('-H', '--host',
                   dest='host',
                   default=self.host),

            Option('-p', '--port',
                   dest='port',
                   type=int,
                   default=self.port),

            Option('-w', '--workers',
                   dest='workers',
                   type=int,
                   default=self.workers),
        )

    def handle(self, app, host, port, workers):

        from gunicorn import version_info

        if version_info < (0, 9, 0):
            from gunicorn.arbiter import Arbiter
            from gunicorn.config import Config
            arbiter = Arbiter(Config({'bind': "%s:%d" % (host, int(port)),'workers': workers}), app)
            arbiter.run()
        else:
            from gunicorn.app.base import Application

            class FlaskApplication(Application):
                def init(self, parser, opts, args):
                    return {
                        'bind': '{0}:{1}'.format(host, port),
                        'workers': workers 
                    }

                def load(self):
                    return app

            FlaskApplication().run()

You can then either register it to replace Flask's local development server at python manage.py runserver

manager.add_command("runserver", GunicornServer())

or register as a new command such as python manage.py gunicorn

manager.add_command("gunicorn", GunicornServer())

Edit June 2016: With the latest version of Flask-Script, change the method handle with __call__. old flask-script vs new flask-script