Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

config option for "flask run" (e.g. --dotenv/-e) #3108

Closed
jab opened this issue Feb 28, 2019 · 9 comments · Fixed by #4646
Closed

config option for "flask run" (e.g. --dotenv/-e) #3108

jab opened this issue Feb 28, 2019 · 9 comments · Fixed by #4646
Assignees
Labels

Comments

@jab
Copy link
Member

jab commented Feb 28, 2019

Would it make sense to allow passing a --config/-c option to flask run, that would then be exposed to users somehow for subsequent passing to e.g. app.config.from_pyfile()?

(I'm imagining this done in a reusable way so that a "flask gunicorn" custom command (the likes of which I've already implemented) could accept and expose a -c value similarly, so that the app could be configured from this same provided path whether running in development or production.)

This would reduce the dependence on an environment variable to specify this. Reason I ask is that many of the Flask users I'm supporting are allergic to environment variables for the reasons mentioned in #3095, and have been missing some way to pass a CLI option to flask run to help configure their apps. Thanks 😊

@davidism
Copy link
Member

davidism commented Mar 1, 2019

I think we might have to disagree about env vars to some degree. We did add support for python-dotenv to read a .flaskenv file so that you don't have to export the vars in your terminal, maybe that mitigates some of your concerns.

The issue I see with adding a config option is that it now dictates one way to configure the app that's not portable to how you'd be deploying to production. If users use this and then use mod_wsgi, for example, they'd have to read the config themselves anyway. It lends preference to from_pyfile as opposed to other functions in Flask or patterns in tutorials. It wouldn't solve the issue with FLASK_APP and FLASK_ENV, which aren't set from config.

I think you might be looking for a custom run command that's more appropriate to your (and probably others) use cases. That might be a good fit for an extension that you'd then install for each of your projects. The docs on Flask's CLI document how extensions can register commands automatically. For example, you could register a run command that made assumptions about where the app is, and what its env and config are.

You could also create your own FlaskGroup in a cli.py file, and register your app with it instead of loading from FLASK_APP. You could add options and extra processing to this cli group. And if you're using setuptools, you can add an entry point to this as a project command instead of the flask command.

@jab
Copy link
Member Author

jab commented Mar 26, 2019

Thanks so much for the helpful reply, @davidism!

I decided to embrace environment variables after all, in a very particular way. After deciding on dotenv as a configuration storage format, and finding Flask CLI's built-in dotenv support only able to get us part of the way there, here's what I did:

I ended up subclassing FlaskGroup to add support for passing (one or more) -e/--dotenv paths as a Flask CLI option, which my lightly-customized flask CLI sources into the environment before then loading a user's app. This allows a user to configure and start their app with a command like path/to/my/project/bin/flask -e qa.env run, instead of something like cd path/to/my/project; set -a; source qa.env; ./bin/flask run (no more $PWD sensitivity!).

Users implement a config_from_environ.py which parses and exposes their app settings from os.environ, and then call app.config.from_object('config_from_environ'). By moving the logic of deciding which environment to read settings for (and then populate into the environment), into the outer flask run layer, it lets the app itself presume that the environ has already been populated and merely read from it.

Since I also provide a flask run-gunicorn custom subcommand, this -e option does end up being portable to production. The tests likewise source a test.env into the environment before importing the app and running its tests. So there is parity in how the app is configured between dev, qa, prod, testing, etc. and there is no need for an app factory function! I should have made it clearer in the description that I agree that dev/prod parity should be a requirement for what this ticket is proposing. I can update the title / description with a better proposal if that makes sense.

If we add the constraint of merely improving Flask's existing dotenv support (in the case that dotenv is installed) to accept a similar -e option, rather than being limited to just sourcing a single, hidden .env file if it exists in the current directory, and given the existence of extensions like https://github.com/doobeh/flask-gunicorn that allow this approach to scale to production, do you think there might actually be some more-universal improvement to flask CLI's dotenv support that we could make here?

Thanks again for the FlaskGroup tip and for your consideration!

@jab jab changed the title --config / -c option for "flask run" config (e.g. --dotenv/-e) option for "flask run" Apr 22, 2019
@jab jab changed the title config (e.g. --dotenv/-e) option for "flask run" config option for "flask run" (e.g. --dotenv/-e) Apr 22, 2019
@davidism
Copy link
Member

davidism commented Jun 1, 2019

After discussion at the sprint, we're thinking of adding a --dotenv (or similarly named) option to change what dotenv file is loaded. This helps in situations where the current directory is above the the application directory, so autodetection doesn't work.

@zhaoyongjie
Copy link

Can we provide a environment var to load specify file?

@qixiangyang
Copy link

Set environment variable (key=value).

Pass variables to the execution environment. Ex.:

$ gunicorn -b 127.0.0.1:8000 --env FOO=1 test:app
and test for the foo variable environment in your application.

from: http://docs.gunicorn.org/en/stable/settings.html

@davidism
Copy link
Member

davidism commented Apr 3, 2020

#3535 discusses being confused by Flask automatically changing the working directory to the directory containing the env file. I'm wondering if we should revisit that behavior, or how it would tie in with a -e argument. It was mainly to support projects that didn't install themselves, so that their imports would work, but that's sort of just papering over a bigger issue. Either the project should use an install (preferred), or they should be more deliberate about their working directory otherwise they'll run into issues later.

@msporleder-work
Copy link

FWIW I got here from #3535 after walking through an strace from another dev where the culprit was a stray .env file.

Even if this is intentional it definitely was not easy to find as flask run just said it couldn't find the app.

Running python ./myapp.py showed a No such file or directory error after flask started, which was pretty confusing to say the least ;). So if you wanted to save the we chdir()'d to /foo because of /foo/.env without telling you for when a file is not found that would have helped me and wouldn't junk up the other output.

@davidism
Copy link
Member

The issue you linked is related but different, and is fixed in the next version.

@davidism
Copy link
Member

davidism commented Jun 15, 2022

I've been doing some work on enabling --app and --env as options (yes, it's happening for real this time, I actually figured it out). However, --env-file is trickier. The file has to get loaded very early so that the environment is set before the processing pipeline runs. But since the pipeline hasn't run yet, the option hasn't been parsed, so it's not possible to get the file yet.

It could be made an eager option, then it could load the env when processing, before any other options are processed. However, I'm not sure if there would be corner cases to this.

@davidism davidism self-assigned this Jun 17, 2022
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jul 2, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants