Auto reloading Flask app when source code changes

20,225

Solution 1

The issue here, as stated in other answers, is that it looks like you moved from python run.py to foreman start, or you changed your Procfile from

# Procfile
web: python run.py

to

# Procfile
web: gunicorn --log-level=DEBUG run:app

When you run foreman start, it simply runs the commands that you've specified in the Procfile. (I'm going to guess you're working with Heroku, but even if not, this is nice because it will mimic what's going to run on your server/Heroku dyno/whatever.)

So now, when you run gunicorn --log-level=DEBUG run:app (via foreman start) you are now running your application with gunicorn rather than the built in webserver that comes with Flask. The run:app argument tells gunicorn to look in run.py for a Flask instance named app, import it, and run it. This is where it get's fun: since the run.py is being imported, __name__ == '__main__' is False (see more on that here), and so app.run(debug = True, port = 5000) is never called.

This is what you want (at least in a setting that's available publicly) because the webserver that's built into Flask that's used when app.run() is called has some pretty serious security vulnerabilities. The --log-level=DEBUG may also be a bit deceiving since it uses the word "DEBUG" but it's only telling gunicorn which logging statements to print and which to ignore (check out the Python docs on logging.)

The solution is to run python run.py when running the app locally and working/debugging on it, and only run foreman start when you want to mimic a production environment. Also, since gunicorn only needs to import the app object, you could remove some ambiguity and change your Procfile to

# Procfile
web: gunicorn --log-level=DEBUG app:app

You could also look into Flask Script which has a built in command python manage.py runserver that runs the built in Flask webserver in debug mode.

Solution 2

The solution was to stop using foreman start as stated in the comments and directly execute python run.py.

This way, the app.run method with the debug=True and use_reloader=True configuration parameters take effect.

Solution 3

Sample Application where app is our application and this application had been saved in the file start.py:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hallo():
    return 'Hello World, this is really cool... that rocks... LOL'

now we start the application from the shell with the flag --reload

gunicorn -w 1 -b 127.0.0.1:3032 start:app --reload

and gunicorn reloads the application at the moment the file has changed automaticly. No need to change anything at all.

if you'd love to run this application in the background add the flag -D

gunicorn -D -w 1 -b 127.0.0.1:3032 start:app --reload

-D Demon mode

-w number of workers

-b address and port

start (start.py) :app - application

--reload gunicorns file monitoring

Look at the settings file: http://docs.gunicorn.org/en/latest/settings.html

all options and flags are mentioned there. Have fun!

Share:
20,225
HanSooloo
Author by

HanSooloo

Updated on August 16, 2020

Comments

  • HanSooloo
    HanSooloo almost 4 years

    I know for a fact that Flask, in debug mode, will detect changes to .py source code files and will reload them when new requests come in.

    I used to see this in my app all the time. Change a little text in an @app.route decoration section in my views.py file, and I could see the changes in the browser upon refresh.

    But all of a sudden (can't remember what changed), this doesn't seem to work anymore.

    Q: Where am I going wrong?

    I am running on a OSX 10.9 system with a VENV setup using Python 2.7. I use foreman start in my project root to start it up.

    App structure is like this:

    [Project Root]
    +-[app]
    | +-__init__.py
    | +- views.py
    | +- ...some other files...
    +-[venv]
    +- config.py
    +- Procfile
    +- run.py
    

    The files look like this:

    # Procfile
    web: gunicorn --log-level=DEBUG run:app
    
    # config.py
    contains some app specific configuration information.
    
    # run.py
    from app import app
    
    if __name__ == "__main__":
        app.run(debug = True, port = 5000)
    
    # __init__.py
    from flask import Flask
    from flask.ext.login import LoginManager
    from flask.ext.sqlalchemy import SQLAlchemy
    from flask.ext.mail import Mail
    import os
    
    app = Flask(__name__)
    app.config.from_object('config')
    
    db = SQLAlchemy(app)
    
    #mail sending
    mail = Mail(app)
    
    lm = LoginManager()
    lm.init_app(app)
    lm.session_protection = "strong"
    
    from app import views, models
    
    # app/views.py
    @app.route('/start-scep')
    def start_scep():
        startMessage = '''\
    <html>
    <header>
    <style>
    body { margin:40px 40px;font-family:Helvetica;}
    h1 { font-size:40px; }
    p { font-size:30px; }
    a { text-decoration:none; }
    </style>
    </header>
    
    <p>Some text</p>
    </body>
    </html>\
    '''
        response = make_response(startMessage)
        response.headers['Content-Type'] = "text/html"
        print response.headers
        return response
    
  • user2682863
    user2682863 about 4 years
    just note that --reload is incompatible with the preload option docs.gunicorn.org/en/latest/settings.html#reload