Search code examples
pythonflaskpython-click

How to run Flask app by flask run or custom command without set FLASK_APP manually


Use case

I have a python package using a click group to have multiple command line subcommands. In addition to this, I would like to have a small flask application.

The other subcommands are the primary function of the package - not the flask application. As such, I would like the flask commands to be nested under their own group.

Example

I made a minimal example to demonstrate my problem - it's on GitHub here: https://github.com/ewels/flask-subcommand-issue

What works

In the minimal example, I set up a mini flask installation that runs with the fsksc_server command. This is thanks to a setuptools console_scripts entry point hook in setup.py.

The command works perfectly, exactly as you would expect:

$ fsksc_server --help

Usage: fsksc_server [OPTIONS] COMMAND [ARGS]...

  Run the fsksc server flask app

Options:
  --version  Show the flask version
  --help     Show this message and exit.

Commands:
  routes  Show the routes for the app.
  run     Runs a development server.
  shell   Runs a shell in the app context.
$ fsksc_server run

 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

(I haven't set up any routes, so visiting the URL throws a 404, but the server is running fine..)

To get the flask commands in a click subcommand I have used flask add_command with the flask group. This main command is fsksc. The flask subcommand should be shell. The intention is to make fsksc shell run launch the development server.

The commands show up properly, so this part seems to work:

$ fsksc --help

Usage: fsksc [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  cmd1
  cmd2
  server  Run the fsksc server flask app

What doesn't work

When doing anything under the server subcommand, I get a warning message about a NoAppException exception:

$ fsksc server --help

Traceback (most recent call last):
  File "/Users/ewels/miniconda2/envs/work/lib/python2.7/site-packages/Flask-1.0.2-py2.7.egg/flask/cli.py", line 529, in list_commands
    rv.update(info.load_app().cli.list_commands(ctx))
  File "/Users/ewels/miniconda2/envs/work/lib/python2.7/site-packages/Flask-1.0.2-py2.7.egg/flask/cli.py", line 384, in load_app
    'Could not locate a Flask application. You did not provide '
NoAppException: Could not locate a Flask application. You did not provide the "FLASK_APP" environment variable, and a "wsgi.py" or "app.py" module was not found in the current directory.
Usage: fsksc server [OPTIONS] COMMAND [ARGS]...

  Run the fsksc server flask app

Options:
  --version  Show the flask version
  --help     Show this message and exit.

Commands:
  routes  Show the routes for the app.
  run     Runs a development server.
  shell   Runs a shell in the app context.

Trying to run the server gives a similar error:

$ fsksc server run

 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: off
Usage: fsksc server run [OPTIONS]

Error: Could not locate a Flask application. You did not provide the "FLASK_APP" environment variable, and a "wsgi.py" or "app.py" module was not found in the current directory.

Crappy workaround

I can fix this by defining the FLASK_APP environment variable correctly. Then flask run works as expected:

$ export FLASK_APP=/Users/ewels/GitHub/flask-subcommand-issue/fsksc/server/app.py:create_fsksc_app

$ fsksc server run

 * Serving Flask app "/Users/ewels/GitHub/flask-subcommand-issue/fsksc/server/app.py:create_fsksc_app"
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

flask run also works.

But - I don't want to have to get my users to do this! I also don't want to pollute my main command group with the flask subcommands (in reality I have a lot more subcommands in the main group).

The question

What do I need to do to make this work as I intend, without having to define FLASK_APP and as a nested group in click?

Thank you in advance for any help!


Solution

  • Ok, so Grey's answer put me on the right track..

    Unfortunately, because of how flask uses the .flaskenv files, paths have to either be absolute, or relative to the current working directory of the user. Neither of these approaches will work for other users installing the package.

    However, when playing with this I found that doing a simple os.environ['FLASK_APP'] works! It just has to be very early on in the script execution. Then the path can be set dynamically based on the location of the installed script, and I think everything seems to work:

    flaskenv_app_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'fsksc', 'server', 'app.py')
    flaskenv_app_func = '{}:create_fsksc_app'.format(flaskenv_app_path)
    os.environ['FLASK_APP'] = flaskenv_app_func
    

    I've updated the minimal example code with this here: https://github.com/ewels/flask-subcommand-issue

    Thank you for your help Grey Li!