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.
I made a minimal example to demonstrate my problem - it's on GitHub here: https://github.com/ewels/flask-subcommand-issue
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
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.
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).
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!
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!