27

I've started learning Flask to develop web applications. What I am really missing is an automatic Browser refresh after any code change (including static files, templates, etc.). This seems to be a standard feature in almost any JavaScript framework. Frontend people have several terms for that: auto reload / refresh, hot reload / refresh (hotreload), live reload / refresh (livereload), ...

Here on Stackoverflow most similar questions are related to the auto-reload of the Flask server (--> https://stackoverflow.com/search?q=flask+auto+reload).

J just want a simple browser refresh.

I googled and tried several things - with no luck:

How can I have a smooth developing experience with Flask without having to hit the F5 key 1000 times a day in a browser just to see the results of my changes?

I think the answer is somewhere near to python-livereload from the link above. So I guess an alternative title of my question could be:

Does somebody have a working example of Flask + python-livereload?

I am to dumb to get it from their documentation :)

EDIT: for the sake of completeness here is the Flask app I am using.

# filename: main.py

from flask import Flask, render_template
from livereload import Server



app = Flask(__name__)

@app.route('/')
def index():
    return "INDEX"

@app.route('/bart')
def use_jinja():
    return render_template('basic.html')



if __name__ == '__main__':
    server = Server(app.wsgi_app)
    server.serve(port=5555)

I start the app with

python main.py
Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
Wlad
  • 2,866
  • 3
  • 28
  • 42

5 Answers5

27

This is an interesting question you've raised so I built a quick and dirty Flask application which utilizes the livereload library. Listed below are the key steps for getting this to work:

  1. Download and install the livereload library:

    pip install livereload

  2. Within your main file which starts up your flask application, run.py in my particular case, wrap the flask application with the Server class provided by livereload.

For example, my run.py file looks like the following:

from app import app
from livereload import Server

if __name__ == '__main__':
    server = Server(app.wsgi_app)
    server.serve()
  1. Start your server again:

    python run.py

  2. Navigate to localhost in the browser and your code changes will be auto-refreshed. For me I took the default port of 5500 provided by livereload so my url looks like the following: http://localhost:5500/.

With those steps you should now be able to take advantage of auto-reloads for your python development, similar to what webpack provides for most frontend frameworks.

For completeness the codebase can be found here

Hopefully that helps!

Nathan
  • 7,853
  • 4
  • 27
  • 50
  • I reproduced exactly the same steps and I end up with a huge traceback. Here's just a port of it: File "C:\Users\Wlad\AppData\Local\Programs\Python\Python37-32\lib\asyncio\selector_events.py", line 83, in close raise RuntimeError("Cannot close a running event loop") RuntimeError: Cannot close a running event loop. I guess this is just another "web development on Windows sucks" issue. Will try on a Linux machine later and give an update here. – Wlad Jul 10 '19 at 17:09
  • 2
    Sounds good, I'll put my sample code on a public github so it will be easy to test for you – Nathan Jul 10 '19 at 17:10
  • Just installed Ubuntu WSL on Windows 10. Same error as on native Windows. Next will try in a Linux VM (virtualbox or hyper-v). And as last resort will try on real Linux :))) – Wlad Jul 10 '19 at 19:31
  • @Wlad I hosted my code on a public repo and pasted the link in my answer if you're still stuck. Were you able to get it working? – Nathan Jul 10 '19 at 21:55
  • I've found out two things. 1) the error I was getting happens only when I do changes to the main.py file which in my case has the app and starts the livereload server. So maybe this is not a good idea and I should separate that. 2) On Windows when editing a template there is no error and the server does not crash, the console even shows which file was changed BUT the browser does not refresh AND even worse - when refreshing manually you won't see the changes applied :( – Wlad Jul 11 '19 at 10:41
  • 1
    @NathanI haven't looked at your code yet but I got it partly working on a Linux VM. Will checkout your code on a real Linux host comming weekend. Thank you so far a lot :) – Wlad Jul 11 '19 at 10:41
  • 1
    @Wlad for your first point take a look at how I structured the application: run.py fires off the application and the init.py file within the app directory creates the flask application. As for your second point just make sure you're saving the files when you make changes as this signals the reload of assets to livereload, similar to how frontend frameworks work with webpack. – Nathan Jul 11 '19 at 14:13
  • 1
    The easiest path forward and what I would consider doing is the following: 1. clone the repo I created 2. download/install the dependencies with a pip install -r requirements.txt 3. run the application with: python run.py and see if the application now works correctly for you. This way you can isolate the problem and will know right away if it's a problem with your code or environment settings. I am developing on a Mac but it shouldn't matter – Nathan Jul 11 '19 at 14:16
  • @Nathon a few months later on a real Linux OS :))) ... I cloned your code and followed the steps. While editing templates reload works nicely. Unfortunately when saving changes to routes.py the server crashes. I reported the issue on your repo: https://github.com/nathanwright1242/flask_livereload_example/issues/1 – Wlad Nov 17 '19 at 22:24
  • 1
    Hi @Wlad, looks like this is an open issue: https://github.com/lepture/python-livereload/issues/170. Perhaps you could try downgrading or restarting the server for route related changes. Cheers! – Nathan Nov 18 '19 at 01:43
  • 3
    This has solved all my frustrations with local flask development. Thank you! – Joseph Honeywood Oct 22 '20 at 11:06
  • 1
    @Nathan how would you make the above work via the "flask run" command after exporting the app variable to the environment? When I tried export FLASK_APP=run.py and then launching via "flask run": 1) server seems to launch, but 2) navigating to the url doesn't load any page. – Aivoric Mar 21 '21 at 11:39
  • 3
    The code doesn't work now – Ax M Nov 13 '21 at 15:08
  • 2
    everyone, please use version 2.5.1 of livereload, its not going to raise errors anymore, but the live reload is broken, sometimes its refreshes, sometimes it doesnt. – alexzander Dec 03 '21 at 23:44
  • Note: run `python wsgi.py` instead of `flask run` – testing_22 May 31 '22 at 14:20
  • I can confirm that downgrading (`pip install livereload==2.5.1`) does not show the error, but it also does not reload the page. – Nils Lindemann Mar 23 '23 at 11:31
1

I use Guard::LiveReload becose solution with python-livereload don't work correct with debug (for me).

In first terminal emulator:

$ cd my-flask-project
$ flask run
 * Serving Flask app 'webface' (lazy loading)
 * Environment: development
 * Debug mode: on
 * Running on http://127.0.0.1:54321/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!

In secondu terminal emulator:

$ cd my-flask-project
$ guard init livereload 

Now I edit Guardfile and add/edit watch. Like this:

  watch(%r{.+\.py})
  watch(%r{webface/.+\.(css|js|html(\.j2)?)})

again in second terminal emulator:

$ guard
17:29:25 - INFO - LiveReload is waiting for a browser to connect.
17:29:25 - INFO - Guard is now watching at '/home/marek/my-flask-project'
[1] guard(main)> 17:29:29 - INFO - Browser connected.
17:29:31 - INFO - Reloading browser: webface/templates/base.html.j2
17:45:41 - INFO - Reloading browser: webface/templates/base.html.j2
[1] guard(main)>

Maybe need to click livereload extention button in browser to connect browser to guard.

MarrekNožka
  • 343
  • 2
  • 6
0

Tried this today, and it worked:

app.py:

# use livereload==2.5.1 only
from flask import Flask, render_template
from livereload import Server

app = Flask(__name__)
app.debug = True

@app.get("/")
def index():
    return render_template("index.html")

# don't use flask run, use python3 app.py
server = Server(app.wsgi_app)
server.watch("templates/*.*")  # or what have you
server.serve(port=5000) # if you want the standard Flask port

in templates/index.html, add the following script before </body>:

<script src="https://cdnjs.cloudflare.com/ajax/libs/livereload-js/3.1.0/livereload.min.js"></script>

Then:

python app.py

Neil McGuigan
  • 46,580
  • 12
  • 123
  • 152
0

For those who want to avoid installing livereload, here's a simple solution from scratch:

livereload.js

from flask import Flask, Response
from textwrap import dedent

class LiveReload:
    def __init__(self, app: Flask = None):
        if app is not None:
            self.init_app(app)

    def init_app(self, app: Flask):
        app.after_request(self.after_request)

    def after_request(self, response: Response):
        if response.status_code != 200:
            return response

        mimetype = response.mimetype or ""
        if not mimetype.startswith("text/html"):
            return response

        if not isinstance(response.response, list):
            return response

        body = b"".join(response.response).decode()
        tag = self.make_tag()
        body = body.replace("</head>", f"{tag}\n</head>")
        response.response = [body.encode("utf8")]
        response.content_length = len(response.response[0])
        return response

    def make_tag(self):
        return dedent("""
            <script>
              document.write('<script src="http://' + (location.host || 'localhost').split(':')[0] +
              ':35729/livereload.js?snipver=1"></' + 'script>')
            </script>
        """).strip()

__init__.py

from flask import Flask
from livereload import LiveReload

app = Flask(__name__)
LiveReload(app)
Carlos Souza
  • 351
  • 6
  • 13
-2

If on Windows:

set FLASK_ENV=development
Alex Pop
  • 184
  • 1
  • 2
  • 13