In Flask 0.11 a flask
CLI was introduced. Both the docs and the changelog state this is recommended.
Starting with Flask 0.11 there are multiple built-in ways to run a development server. The best one is the flask command line utility but you can also continue using the
Flask.run()
method.Command Line
The flask command line script (Command Line Interface) is strongly recommended for development because it provides a superior reload experience due to how it loads the application. The basic usage is like this:
$ export FLASK_APP=my_application $ export FLASK_DEBUG=1 $ flask run
- Added
flask
and theflask.cli
module to start the local
debug server through the click CLI system. This is recommended over the oldflask.run()
method as it works faster and more reliable due to a
different design and also replacesFlask-Script
.
So far I didn't notice this "superior reload experience". I fail to see the point of using the CLI over a custom script.
If using Flask.run
, I would simply write a python file:
#!/usr/bin/env python3
from my_app import app
if __name__ == '__main__':
app.run(debug=True)
If using the CLI, one would have to specify environment variables. In the CLI docs is stated that this can be integrated in the activate
script of virtualenvwrapper. Personally I consider this to be part of the application and think it should be under version control. Alas, a shell script is needed:
#!/usr/bin/env bash
export FLASK_APP=my_app:app
export FLASK_DEBUG=1
flask run
Of course this will be accompanied by an additional bat script as soon as any Windows users start to collaborate.
Also the first option allows setup written in Python before starting the actual app.
This allows for example
- to parse command line arguments in Python
- to setup logging before running the app
They seem to promote that it's possible to add custom commands. I fail to see why this is better than writing simple Python scripts, optionally exposed through entry points.
Example logging output when using a configured logger using the Python run script:
$ ./run.py
DEBUG 21:51:22 main.py:95) Configured logging
INFO 21:51:22 _internal.py:87) * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
INFO 21:51:22 _internal.py:87) * Restarting with inotify reloader
DEBUG 21:51:22 main.py:95) Configured logging
WARNING 21:51:22 _internal.py:87) * Debugger is active!
INFO 21:51:22 _internal.py:87) * Debugger pin code: 263-225-431
DEBUG 21:51:25 inotify_buffer.py:61) in-event <InotifyEvent: src_path=b'my_app/main.py', wd=272, mask=IN_MODIFY, cookie=0, name=b'main.py'>
DEBUG 21:51:25 inotify_buffer.py:61) in-event <InotifyEvent: src_path=b'my_app/main.py', wd=272, mask=IN_MODIFY, cookie=0, name=b'main.py'>
INFO 21:51:25 _internal.py:87) * Detected change in 'my_app/main.py', reloading
INFO 21:51:26 _internal.py:87) * Restarting with inotify reloader
DEBUG 21:51:26 main.py:95) Configured logging
WARNING 21:51:26 _internal.py:87) * Debugger is active!
INFO 21:51:26 _internal.py:87) * Debugger pin code: 263-225-431
Example logging output when using a configured logger using the CLI:, notice that the root logger couldn't be setup early enough in the process.
$ ./run.sh
* Serving Flask app "appsemble.api.main:app"
* Forcing debug mode on
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with inotify reloader
DEBUG 21:51:33 main.py:95) Configured logging
* Debugger is active!
* Debugger pin code: 187-758-498
DEBUG 21:51:34 main.py:95) Configured logging
DEBUG 21:51:37 inotify_buffer.py:61) in-event <InotifyEvent: src_path=b'my_app/main.py', wd=272, mask=IN_MODIFY, cookie=0, name=b'main.py'>
DEBUG 21:51:37 inotify_buffer.py:61) in-event <InotifyEvent: src_path=b'my_app/main.py', wd=272, mask=IN_MODIFY, cookie=0, name=b'main.py'>
* Detected change in 'my_app/main.py', reloading
INFO 21:51:37 _internal.py:87) * Detected change in 'my_app/main.py', reloading
* Restarting with inotify reloader
INFO 21:51:38 _internal.py:87) * Restarting with inotify reloader
* Debugger is active!
* Debugger pin code: 187-758-498
DEBUG 21:51:38 main.py:95) Configured logging
My actual question is simply:
Why is flask CLI recommended over Flask.run
?
Best Answer
In the development server docs, they state there are issues with calling run() and automatically reloading code:
They claim the CLI doesn't suffer from this problem.
The first commit that seems to touch on this issue is this: https://github.com/pallets/flask/commit/3bdb90f06b9d3167320180d4a5055dcd949bf72f
And there Armin Ronacher wrote:
As mentioned by Aaron Hall, it seems like the use of run() could be problematic due to the fact that all objects which are instances of classes defined in the modules being replaced won't be reinstantiated, and whenever a module is reloaded, the modules it imports aren't reloaded as well.
The details about this may be found for Python 3 at: https://docs.python.org/3/library/importlib.html?highlight=importlib#module-importlib
It states:
So, by creating a new process and killing the old one, you naturally eliminate all obsolete references.
Also, Flask's CLI uses the 'click' module, making it very easy to add custom commands, but most importantly, besides fixing the reloading bug, the CLI offers a standardised way to run applications and add custom commands. This sounds like a very good thing, because it makes familiarity with Flask more transferable between different teams and applications, rather than having multiple ways to do the same thing.
It seems like a genuine way to make Flask more in accordance to the Zen of Python: