From 99fa3c36abc03cd5b3407df34dce74e879ea377a Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 17 Jun 2022 07:54:06 -0700 Subject: [PATCH 1/2] add --app, --env, --debug, and --env-file CLI options --- CHANGES.rst | 6 ++ src/flask/cli.py | 239 ++++++++++++++++++++++++++++++++++--------- src/flask/helpers.py | 6 +- 3 files changed, 197 insertions(+), 54 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 84ffbf06..0dea8e3a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -27,6 +27,12 @@ Unreleased instance on every request. :issue:`2520`. - A ``flask.cli.FlaskGroup`` Click group can be nested as a sub-command in a custom CLI. :issue:`3263` +- Add ``--app``, ``--env``, and ``--debug`` options to the ``flask`` + CLI, instead of requiring that they are set through environment + variables. :issue:`2836` +- Add ``--env-file`` option to the ``flask`` CLI. This allows + specifying a dotenv file to load in addition to ``.env`` and + ``.flaskenv``. :issue:`3108` Version 2.1.3 diff --git a/src/flask/cli.py b/src/flask/cli.py index a4e366d7..40f1de54 100644 --- a/src/flask/cli.py +++ b/src/flask/cli.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import ast import inspect import os @@ -12,6 +14,7 @@ from threading import Lock from threading import Thread import click +from click.core import ParameterSource from werkzeug.serving import is_running_from_reloader from werkzeug.utils import import_string @@ -20,6 +23,9 @@ from .helpers import get_debug_flag from .helpers import get_env from .helpers import get_load_dotenv +if t.TYPE_CHECKING: + from .app import Flask + class NoAppException(click.UsageError): """Raised if an application cannot be found or loaded.""" @@ -46,8 +52,8 @@ def find_best_app(module): elif len(matches) > 1: raise NoAppException( "Detected multiple Flask applications in module" - f" {module.__name__!r}. Use 'FLASK_APP={module.__name__}:name'" - f" to specify the correct one." + f" '{module.__name__}'. Use '{module.__name__}:name'" + " to specify the correct one." ) # Search for app factory functions. @@ -65,15 +71,15 @@ def find_best_app(module): raise raise NoAppException( - f"Detected factory {attr_name!r} in module {module.__name__!r}," + f"Detected factory '{attr_name}' in module '{module.__name__}'," " but could not call it without arguments. Use" - f" \"FLASK_APP='{module.__name__}:{attr_name}(args)'\"" + f" '{module.__name__}:{attr_name}(args)'" " to specify arguments." ) from e raise NoAppException( "Failed to find Flask application or factory in module" - f" {module.__name__!r}. Use 'FLASK_APP={module.__name__}:name'" + f" '{module.__name__}'. Use '{module.__name__}:name'" " to specify one." ) @@ -253,7 +259,7 @@ def get_version(ctx, param, value): version_option = click.Option( ["--version"], - help="Show the flask version", + help="Show the Flask version.", expose_value=False, callback=get_version, is_flag=True, @@ -338,19 +344,24 @@ class ScriptInfo: onwards as click object. """ - def __init__(self, app_import_path=None, create_app=None, set_debug_flag=True): + def __init__( + self, + app_import_path: str | None = None, + create_app: t.Callable[..., Flask] | None = None, + set_debug_flag: bool = True, + ) -> None: #: Optionally the import path for the Flask application. - self.app_import_path = app_import_path or os.environ.get("FLASK_APP") + self.app_import_path = app_import_path #: Optionally a function that is passed the script info to create #: the instance of the application. self.create_app = create_app #: A dictionary with arbitrary data that can be associated with #: this script info. - self.data = {} + self.data: t.Dict[t.Any, t.Any] = {} self.set_debug_flag = set_debug_flag - self._loaded_app = None + self._loaded_app: Flask | None = None - def load_app(self): + def load_app(self) -> Flask: """Loads the Flask app (if not yet loaded) and returns it. Calling this multiple times will just result in the already loaded app to be returned. @@ -379,9 +390,10 @@ class ScriptInfo: if not app: raise 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.' + "Could not locate a Flask application. Use the" + " 'flask --app' option, 'FLASK_APP' environment" + " variable, or a 'wsgi.py' or 'app.py' file in the" + " current directory." ) if self.set_debug_flag: @@ -442,6 +454,117 @@ class AppGroup(click.Group): return click.Group.group(self, *args, **kwargs) +def _set_app(ctx: click.Context, param: click.Option, value: str | None) -> str | None: + if value is None: + return None + + info = ctx.ensure_object(ScriptInfo) + info.app_import_path = value + return value + + +# This option is eager so the app will be available if --help is given. +# --help is also eager, so --app must be before it in the param list. +# no_args_is_help bypasses eager processing, so this option must be +# processed manually in that case to ensure FLASK_APP gets picked up. +_app_option = click.Option( + ["-A", "--app"], + metavar="IMPORT", + help=( + "The Flask application or factory function to load, in the form 'module:name'." + " Module can be a dotted import or file path. Name is not required if it is" + " 'app', 'application', 'create_app', or 'make_app', and can be 'name(args)' to" + " pass arguments." + ), + is_eager=True, + expose_value=False, + callback=_set_app, +) + + +def _set_env(ctx: click.Context, param: click.Option, value: str | None) -> str | None: + if value is None: + return None + + # Set with env var instead of ScriptInfo.load so that it can be + # accessed early during a factory function. + os.environ["FLASK_ENV"] = value + return value + + +_env_option = click.Option( + ["-E", "--env"], + metavar="NAME", + help=( + "The execution environment name to set in 'app.env'. Defaults to" + " 'production'. 'development' will enable 'app.debug' and start the" + " debugger and reloader when running the server." + ), + expose_value=False, + callback=_set_env, +) + + +def _set_debug(ctx: click.Context, param: click.Option, value: bool) -> bool | None: + # If the flag isn't provided, it will default to False. Don't use + # that, let debug be set by env in that case. + source = ctx.get_parameter_source(param.name) # type: ignore[arg-type] + + if source is not None and source in ( + ParameterSource.DEFAULT, + ParameterSource.DEFAULT_MAP, + ): + return None + + # Set with env var instead of ScriptInfo.load so that it can be + # accessed early during a factory function. + os.environ["FLASK_DEBUG"] = "1" if value else "0" + return value + + +_debug_option = click.Option( + ["--debug/--no-debug"], + help="Set 'app.debug' separately from '--env'.", + expose_value=False, + callback=_set_debug, +) + + +def _env_file_callback( + ctx: click.Context, param: click.Option, value: str | None +) -> str | None: + if value is None: + return None + + import importlib + + try: + importlib.import_module("dotenv") + except ImportError: + raise click.BadParameter( + "python-dotenv must be installed to load an env file.", + ctx=ctx, + param=param, + ) from None + + # Don't check FLASK_SKIP_DOTENV, that only disables automatically + # loading .env and .flaskenv files. + load_dotenv(value) + return value + + +# This option is eager so env vars are loaded as early as possible to be +# used by other options. +_env_file_option = click.Option( + ["-e", "--env-file"], + type=click.Path(exists=True, dir_okay=False), + help="Load environment variables from this file. python-dotenv must be installed.", + is_eager=True, + expose_value=False, + callback=_env_file_callback, +) + + class FlaskGroup(AppGroup): """Special subclass of the :class:`AppGroup` group that supports loading more commands from the configured Flask app. Normally a @@ -460,6 +583,10 @@ class FlaskGroup(AppGroup): :param set_debug_flag: Set the app's debug flag based on the active environment + .. versionchanged:: 2.2 + Added the ``-A/--app``, ``-E/--env``, ``--debug/--no-debug``, + and ``-e/--env-file`` options. + .. versionchanged:: 1.0 If installed, python-dotenv will be used to load environment variables from :file:`.env` and :file:`.flaskenv` files. @@ -467,14 +594,19 @@ class FlaskGroup(AppGroup): def __init__( self, - add_default_commands=True, - create_app=None, - add_version_option=True, - load_dotenv=True, - set_debug_flag=True, - **extra, - ): + add_default_commands: bool = True, + create_app: t.Callable[..., Flask] | None = None, + add_version_option: bool = True, + load_dotenv: bool = True, + set_debug_flag: bool = True, + **extra: t.Any, + ) -> None: params = list(extra.pop("params", None) or ()) + # Processing is done with option callbacks instead of a group + # callback. This allows users to make a custom group callback + # without losing the behavior. --env-file must come first so + # that it is eagerly evaluated before --app. + params.extend((_env_file_option, _app_option, _env_option, _debug_option)) if add_version_option: params.append(version_option) @@ -555,11 +687,13 @@ class FlaskGroup(AppGroup): def make_context( self, - info_name: t.Optional[str], - args: t.List[str], - parent: t.Optional[click.Context] = None, + info_name: str | None, + args: list[str], + parent: click.Context | None = None, **extra: t.Any, ) -> click.Context: + # Attempt to load .env and .flask env files. The --env-file + # option can cause another file to be loaded. if get_load_dotenv(self.load_dotenv): load_dotenv() @@ -570,6 +704,16 @@ class FlaskGroup(AppGroup): return super().make_context(info_name, args, parent=parent, **extra) + def parse_args(self, ctx: click.Context, args: list[str]) -> list[str]: + if not args and self.no_args_is_help: + # Attempt to load --env-file and --app early in case they + # were given as env vars. Otherwise no_args_is_help will not + # see commands from app.cli. + _env_file_option.handle_parse_result(ctx, {}, []) + _app_option.handle_parse_result(ctx, {}, []) + + return super().parse_args(ctx, args) + def _path_is_ancestor(path, other): """Take ``other`` and remove the length of ``path`` from it. Then join it @@ -578,7 +722,7 @@ def _path_is_ancestor(path, other): return os.path.join(path, other[len(path) :].lstrip(os.sep)) == other -def load_dotenv(path=None): +def load_dotenv(path: str | os.PathLike | None = None) -> bool: """Load "dotenv" files in order of precedence to set environment variables. If an env var is already set it is not overwritten, so earlier files in the @@ -591,13 +735,17 @@ def load_dotenv(path=None): :param path: Load the file at this location instead of searching. :return: ``True`` if a file was loaded. - .. versionchanged:: 1.1.0 - Returns ``False`` when python-dotenv is not installed, or when - the given path isn't a file. + .. versionchanged:: 2.0 + The current directory is not changed to the location of the + loaded file. .. versionchanged:: 2.0 When loading the env files, set the default encoding to UTF-8. + .. versionchanged:: 1.1.0 + Returns ``False`` when python-dotenv is not installed, or when + the given path isn't a file. + .. versionadded:: 1.0 """ try: @@ -613,15 +761,15 @@ def load_dotenv(path=None): return False - # if the given path specifies the actual file then return True, - # else False + # Always return after attempting to load a given path, don't load + # the default files. if path is not None: if os.path.isfile(path): return dotenv.load_dotenv(path, encoding="utf-8") return False - new_dir = None + loaded = False for name in (".env", ".flaskenv"): path = dotenv.find_dotenv(name, usecwd=True) @@ -629,12 +777,10 @@ def load_dotenv(path=None): if not path: continue - if new_dir is None: - new_dir = os.path.dirname(path) - dotenv.load_dotenv(path, encoding="utf-8") + loaded = True - return new_dir is not None # at least one file was located and loaded + return loaded # True if at least one file was located and loaded. def show_server_banner(env, debug, app_import_path, eager_loading): @@ -837,9 +983,10 @@ def run_command( This server is for development purposes only. It does not provide the stability, security, or performance of production WSGI servers. - The reloader and debugger are enabled by default if - FLASK_ENV=development or FLASK_DEBUG=1. + The reloader and debugger are enabled by default with the + '--env development' or '--debug' options. """ + app = DispatchingApp(info.load_app, use_eager_loading=eager_loading) debug = get_debug_flag() if reload is None: @@ -849,7 +996,6 @@ def run_command( debugger = debug show_server_banner(get_env(), debug, info.app_import_path, eager_loading) - app = DispatchingApp(info.load_app, use_eager_loading=eager_loading) from werkzeug.serving import run_simple @@ -971,19 +1117,10 @@ cli = FlaskGroup( help="""\ A general utility script for Flask applications. -Provides commands from Flask, extensions, and the application. Loads the -application defined in the FLASK_APP environment variable, or from a wsgi.py -file. Setting the FLASK_ENV environment variable to 'development' will enable -debug mode. - -\b - {prefix}{cmd} FLASK_APP=hello.py - {prefix}{cmd} FLASK_ENV=development - {prefix}flask run -""".format( - cmd="export" if os.name == "posix" else "set", - prefix="$ " if os.name == "posix" else "> ", - ), +An application to load must be given with the '--app' option, +'FLASK_APP' environment variable, or with a 'wsgi.py' or 'app.py' file +in the current directory. +""", ) diff --git a/src/flask/helpers.py b/src/flask/helpers.py index 3b61635c..d1a84b9c 100644 --- a/src/flask/helpers.py +++ b/src/flask/helpers.py @@ -48,9 +48,9 @@ def get_debug_flag() -> bool: def get_load_dotenv(default: bool = True) -> bool: - """Get whether the user has disabled loading dotenv files by setting - :envvar:`FLASK_SKIP_DOTENV`. The default is ``True``, load the - files. + """Get whether the user has disabled loading default dotenv files by + setting :envvar:`FLASK_SKIP_DOTENV`. The default is ``True``, load + the files. :param default: What to return if the env var isn't set. """ From ab1fbef29a073fe5950ea2357f6a3a0d280eb506 Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 17 Jun 2022 09:26:26 -0700 Subject: [PATCH 2/2] prefer --app over FLASK_APP in docs --- docs/cli.rst | 206 ++++++++------------------------- docs/config.rst | 68 ++++------- docs/debugging.rst | 76 ++---------- docs/patterns/appfactories.rst | 69 ++--------- docs/patterns/packages.rst | 69 ++--------- docs/quickstart.rst | 115 +++--------------- docs/reqcontext.rst | 4 +- docs/server.rst | 23 ++-- docs/tutorial/database.rst | 6 +- docs/tutorial/deploy.rst | 32 +---- docs/tutorial/factory.rst | 38 +----- docs/tutorial/install.rst | 2 +- examples/javascript/README.rst | 3 +- examples/tutorial/README.rst | 15 +-- tests/conftest.py | 1 + 15 files changed, 147 insertions(+), 580 deletions(-) diff --git a/docs/cli.rst b/docs/cli.rst index 3be3aaa6..7bab8fac 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -15,40 +15,10 @@ Application Discovery --------------------- The ``flask`` command is installed by Flask, not your application; it must be -told where to find your application in order to use it. The ``FLASK_APP`` -environment variable is used to specify how to load the application. +told where to find your application in order to use it. The ``--app`` +option is used to specify how to load the application. -.. tabs:: - - .. group-tab:: Bash - - .. code-block:: text - - $ export FLASK_APP=hello - $ flask run - - .. group-tab:: Fish - - .. code-block:: text - - $ set -x FLASK_APP hello - $ flask run - - .. group-tab:: CMD - - .. code-block:: text - - > set FLASK_APP=hello - > flask run - - .. group-tab:: Powershell - - .. code-block:: text - - > $env:FLASK_APP = "hello" - > flask run - -While ``FLASK_APP`` supports a variety of options for specifying your +While ``--app`` supports a variety of options for specifying your application, most use cases should be simple. Here are the typical values: (nothing) @@ -56,32 +26,32 @@ application, most use cases should be simple. Here are the typical values: automatically detecting an app (``app`` or ``application``) or factory (``create_app`` or ``make_app``). -``FLASK_APP=hello`` +``--app hello`` The given name is imported, automatically detecting an app (``app`` or ``application``) or factory (``create_app`` or ``make_app``). ---- -``FLASK_APP`` has three parts: an optional path that sets the current working +``--app`` has three parts: an optional path that sets the current working directory, a Python file or dotted import path, and an optional variable name of the instance or factory. If the name is a factory, it can optionally be followed by arguments in parentheses. The following values demonstrate these parts: -``FLASK_APP=src/hello`` +``--app src/hello`` Sets the current working directory to ``src`` then imports ``hello``. -``FLASK_APP=hello.web`` +``--app hello.web`` Imports the path ``hello.web``. -``FLASK_APP=hello:app2`` +``--app hello:app2`` Uses the ``app2`` Flask instance in ``hello``. -``FLASK_APP="hello:create_app('dev')"`` +``--app 'hello:create_app("dev")'`` The ``create_app`` factory in ``hello`` is called with the string ``'dev'`` as the argument. -If ``FLASK_APP`` is not set, the command will try to import "app" or +If ``--app`` is not set, the command will try to import "app" or "wsgi" (as a ".py" file, or package) and try to detect an application instance or factory. @@ -137,8 +107,9 @@ Environments .. versionadded:: 1.0 -The environment in which the Flask app runs is set by the -:envvar:`FLASK_ENV` environment variable. If not set it defaults to +The environment in which the Flask app executes is set by the +``FLASK_ENV`` environment variable. When using the ``flask`` command, it +can also be set with the ``--env`` option. If not set it defaults to ``production``. The other recognized environment is ``development``. Flask and extensions may choose to enable behaviors based on the environment. @@ -147,63 +118,16 @@ If the env is set to ``development``, the ``flask`` command will enable debug mode and ``flask run`` will enable the interactive debugger and reloader. -.. tabs:: +.. code-block:: text - .. group-tab:: Bash - - .. code-block:: text - - $ export FLASK_ENV=development - $ flask run - * Serving Flask app "hello" - * Environment: development - * Debug mode: on - * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) - * Restarting with inotify reloader - * Debugger is active! - * Debugger PIN: 223-456-919 - - .. group-tab:: Fish - - .. code-block:: text - - $ set -x FLASK_ENV development - $ flask run - * Serving Flask app "hello" - * Environment: development - * Debug mode: on - * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) - * Restarting with inotify reloader - * Debugger is active! - * Debugger PIN: 223-456-919 - - .. group-tab:: CMD - - .. code-block:: text - - > set FLASK_ENV=development - > flask run - * Serving Flask app "hello" - * Environment: development - * Debug mode: on - * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) - * Restarting with inotify reloader - * Debugger is active! - * Debugger PIN: 223-456-919 - - .. group-tab:: Powershell - - .. code-block:: text - - > $env:FLASK_ENV = "development" - > flask run - * Serving Flask app "hello" - * Environment: development - * Debug mode: on - * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) - * Restarting with inotify reloader - * Debugger is active! - * Debugger PIN: 223-456-919 + $ flask --app hello --env development run + * Serving Flask app "hello" + * Environment: development + * Debug mode: on + * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) + * Restarting with inotify reloader + * Debugger is active! + * Debugger PIN: 223-456-919 Watch Extra Files with the Reloader @@ -211,72 +135,31 @@ Watch Extra Files with the Reloader When using development mode, the reloader will trigger whenever your Python code or imported modules change. The reloader can watch -additional files with the ``--extra-files`` option, or the -``FLASK_RUN_EXTRA_FILES`` environment variable. Multiple paths are +additional files with the ``--extra-files`` option. Multiple paths are separated with ``:``, or ``;`` on Windows. -.. tabs:: +.. code-block:: text - .. group-tab:: Bash - - .. code-block:: text - - $ flask run --extra-files file1:dirA/file2:dirB/ - # or - $ export FLASK_RUN_EXTRA_FILES=file1:dirA/file2:dirB/ - $ flask run - * Running on http://127.0.0.1:8000/ - * Detected change in '/path/to/file1', reloading - - .. group-tab:: Fish - - .. code-block:: text - - $ flask run --extra-files file1:dirA/file2:dirB/ - # or - $ set -x FLASK_RUN_EXTRA_FILES file1 dirA/file2 dirB/ - $ flask run - * Running on http://127.0.0.1:8000/ - * Detected change in '/path/to/file1', reloading - - .. group-tab:: CMD - - .. code-block:: text - - > flask run --extra-files file1:dirA/file2:dirB/ - # or - > set FLASK_RUN_EXTRA_FILES=file1:dirA/file2:dirB/ - > flask run - * Running on http://127.0.0.1:8000/ - * Detected change in '/path/to/file1', reloading - - .. group-tab:: Powershell - - .. code-block:: text - - > flask run --extra-files file1:dirA/file2:dirB/ - # or - > $env:FLASK_RUN_EXTRA_FILES = "file1:dirA/file2:dirB/" - > flask run - * Running on http://127.0.0.1:8000/ - * Detected change in '/path/to/file1', reloading + $ flask run --extra-files file1:dirA/file2:dirB/ + * Running on http://127.0.0.1:8000/ + * Detected change in '/path/to/file1', reloading Ignore files with the Reloader ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The reloader can also ignore files using :mod:`fnmatch` patterns with -the ``--exclude-patterns`` option, or the ``FLASK_RUN_EXCLUDE_PATTERNS`` -environment variable. Multiple patterns are separated with ``:``, or -``;`` on Windows. +the ``--exclude-patterns`` option. Multiple patterns are separated with +``:``, or ``;`` on Windows. Debug Mode ---------- -Debug mode will be enabled when :envvar:`FLASK_ENV` is ``development``, -as described above. If you want to control debug mode separately, use -:envvar:`FLASK_DEBUG`. The value ``1`` enables it, ``0`` disables it. +Debug mode will be enabled when the execution environment is +``development``, as described above. If you want to control debug mode +separately, use the ``--debug/--no-debug`` option or the ``FLASK_DEBUG`` +environment variable. .. _dotenv: @@ -284,14 +167,21 @@ as described above. If you want to control debug mode separately, use Environment Variables From dotenv --------------------------------- -Rather than setting ``FLASK_APP`` each time you open a new terminal, you can -use Flask's dotenv support to set environment variables automatically. +The ``flask`` command supports setting any option for any command with +environment variables. The variables are named like ``FLASK_OPTION`` or +``FLASK_COMMAND_OPTION``, for example ``FLASK_APP`` or +``FLASK_RUN_PORT``. + +Rather than passing options every time you run a command, or environment +variables every time you open a new terminal, you can use Flask's dotenv +support to set environment variables automatically. If `python-dotenv`_ is installed, running the ``flask`` command will set -environment variables defined in the files :file:`.env` and :file:`.flaskenv`. -This can be used to avoid having to set ``FLASK_APP`` manually every time you -open a new terminal, and to set configuration using environment variables -similar to how some deployment services work. +environment variables defined in the files ``.env`` and ``.flaskenv``. +You can also specify an extra file to load with the ``--env-file`` +option. Dotenv files can be used to avoid having to set ``--app`` or +``FLASK_APP`` manually, and to set configuration using environment +variables similar to how some deployment services work. Variables set on the command line are used over those set in :file:`.env`, which are used over those set in :file:`.flaskenv`. :file:`.flaskenv` should be @@ -612,7 +502,7 @@ Custom Scripts -------------- When you are using the app factory pattern, it may be more convenient to define -your own Click script. Instead of using ``FLASK_APP`` and letting Flask load +your own Click script. Instead of using ``--app`` and letting Flask load your application, you can create your own Click object and export it as a `console script`_ entry point. @@ -646,7 +536,7 @@ Define the entry point in :file:`setup.py`:: ) Install the application in the virtualenv in editable mode and the custom -script is available. Note that you don't need to set ``FLASK_APP``. :: +script is available. Note that you don't need to set ``--app``. :: $ pip install -e . $ wiki run diff --git a/docs/config.rst b/docs/config.rst index 7a5e4da1..12170e90 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -47,61 +47,33 @@ Environment and Debug Features The :data:`ENV` and :data:`DEBUG` config values are special because they may behave inconsistently if changed after the app has begun setting up. -In order to set the environment and debug mode reliably, Flask uses -environment variables. +In order to set the environment and debug mode reliably, pass options to +the ``flask`` command or use environment variables. -The environment is used to indicate to Flask, extensions, and other -programs, like Sentry, what context Flask is running in. It is -controlled with the :envvar:`FLASK_ENV` environment variable and -defaults to ``production``. +The execution environment is used to indicate to Flask, extensions, and +other programs, like Sentry, what context Flask is running in. It is +controlled with the ``FLASK_ENV`` environment variable, or the +``--env`` option when using the ``flask`` command, and defaults to +``production``. -Setting :envvar:`FLASK_ENV` to ``development`` will enable debug mode. -``flask run`` will use the interactive debugger and reloader by default -in debug mode. To control this separately from the environment, use the -:envvar:`FLASK_DEBUG` flag. - -.. versionchanged:: 1.0 - Added :envvar:`FLASK_ENV` to control the environment separately - from debug mode. The development environment enables debug mode. +Setting ``--env development`` will enable debug mode. ``flask run`` will +use the interactive debugger and reloader by default in debug mode. To +control this separately from the environment, use the +``--debug/--no-debug`` option or the ``FLASK_DEBUG`` environment +variable. To switch Flask to the development environment and enable debug mode, -set :envvar:`FLASK_ENV`: +set ``--env``: -.. tabs:: +.. code-block:: text - .. group-tab:: Bash + $ flask --app hello --env development run - .. code-block:: text - - $ export FLASK_ENV=development - $ flask run - - .. group-tab:: Fish - - .. code-block:: text - - $ set -x FLASK_ENV development - $ flask run - - .. group-tab:: CMD - - .. code-block:: text - - > set FLASK_ENV=development - > flask run - - .. group-tab:: Powershell - - .. code-block:: text - - > $env:FLASK_ENV = "development" - > flask run - -Using the environment variables as described above is recommended. While -it is possible to set :data:`ENV` and :data:`DEBUG` in your config or -code, this is strongly discouraged. They can't be read early by the -``flask`` command, and some systems or extensions may have already -configured themselves based on a previous value. +Using the options or environment variables as described above is +recommended. While it is possible to set :data:`ENV` and :data:`DEBUG` +in your config or code, this is strongly discouraged. They can't be read +early by the ``flask`` command, and some systems or extensions may have +already configured themselves based on a previous value. Builtin Configuration Values diff --git a/docs/debugging.rst b/docs/debugging.rst index cd955312..b907c769 100644 --- a/docs/debugging.rst +++ b/docs/debugging.rst @@ -39,45 +39,19 @@ during a request. This debugger should only be used during development. security risk. Do not run the development server or debugger in a production environment. -To enable the debugger, run the development server with the -``FLASK_ENV`` environment variable set to ``development``. This puts -Flask in debug mode, which changes how it handles some errors, and -enables the debugger and reloader. +To enable the debugger, run the development server with the environment +set to ``development``. This puts Flask in debug mode, which changes how +it handles some errors, and enables the debugger and reloader. -.. tabs:: +.. code-block:: text - .. group-tab:: Bash + $ flask --app hello --env development run - .. code-block:: text - - $ export FLASK_ENV=development - $ flask run - - .. group-tab:: Fish - - .. code-block:: text - - $ set -x FLASK_ENV development - $ flask run - - .. group-tab:: CMD - - .. code-block:: text - - > set FLASK_ENV=development - > flask run - - .. group-tab:: Powershell - - .. code-block:: text - - > $env:FLASK_ENV = "development" - > flask run - -``FLASK_ENV`` can only be set as an environment variable. When running +``FLASK_ENV`` can also be set as an environment variable. When running from Python code, passing ``debug=True`` enables debug mode, which is -mostly equivalent. Debug mode can be controlled separately from -``FLASK_ENV`` with the ``FLASK_DEBUG`` environment variable as well. +mostly equivalent. Debug mode can be controlled separately from the +environment with the ``--debug/--no-debug`` option or the +``FLASK_DEBUG`` environment variable. .. code-block:: python @@ -102,37 +76,9 @@ When using an external debugger, the app should still be in debug mode, but it can be useful to disable the built-in debugger and reloader, which can interfere. -When running from the command line: +.. code-block:: text -.. tabs:: - - .. group-tab:: Bash - - .. code-block:: text - - $ export FLASK_ENV=development - $ flask run --no-debugger --no-reload - - .. group-tab:: Fish - - .. code-block:: text - - $ set -x FLASK_ENV development - $ flask run --no-debugger --no-reload - - .. group-tab:: CMD - - .. code-block:: text - - > set FLASK_ENV=development - > flask run --no-debugger --no-reload - - .. group-tab:: Powershell - - .. code-block:: text - - > $env:FLASK_ENV = "development" - > flask run --no-debugger --no-reload + $ flask --app hello --env development run --no-debugger --no-reload When running from Python: diff --git a/docs/patterns/appfactories.rst b/docs/patterns/appfactories.rst index a0e88ab3..415c10fa 100644 --- a/docs/patterns/appfactories.rst +++ b/docs/patterns/appfactories.rst @@ -89,71 +89,20 @@ Using Applications To run such an application, you can use the :command:`flask` command: -.. tabs:: +.. code-block:: text - .. group-tab:: Bash + $ flask run --app hello run - .. code-block:: text +Flask will automatically detect the factory if it is named +``create_app`` or ``make_app`` in ``hello``. You can also pass arguments +to the factory like this: - $ export FLASK_APP=myapp - $ flask run +.. code-block:: text - .. group-tab:: Fish + $ flask run --app hello:create_app(local_auth=True)`` - .. code-block:: text - - $ set -x FLASK_APP myapp - $ flask run - - .. group-tab:: CMD - - .. code-block:: text - - > set FLASK_APP=myapp - > flask run - - .. group-tab:: Powershell - - .. code-block:: text - - > $env:FLASK_APP = "myapp" - > flask run - -Flask will automatically detect the factory (``create_app`` or ``make_app``) -in ``myapp``. You can also pass arguments to the factory like this: - -.. tabs:: - - .. group-tab:: Bash - - .. code-block:: text - - $ export FLASK_APP="myapp:create_app('dev')" - $ flask run - - .. group-tab:: Fish - - .. code-block:: text - - $ set -x FLASK_APP "myapp:create_app('dev')" - $ flask run - - .. group-tab:: CMD - - .. code-block:: text - - > set FLASK_APP="myapp:create_app('dev')" - > flask run - - .. group-tab:: Powershell - - .. code-block:: text - - > $env:FLASK_APP = "myapp:create_app('dev')" - > flask run - -Then the ``create_app`` factory in ``myapp`` is called with the string -``'dev'`` as the argument. See :doc:`/cli` for more detail. +Then the ``create_app`` factory in ``myapp`` is called with the keyword +argument ``local_auth=True``. See :doc:`/cli` for more detail. Factory Improvements -------------------- diff --git a/docs/patterns/packages.rst b/docs/patterns/packages.rst index a30ef3cb..13f8270e 100644 --- a/docs/patterns/packages.rst +++ b/docs/patterns/packages.rst @@ -56,70 +56,17 @@ a big problem, just add a new file called :file:`setup.py` next to the inner ], ) -In order to run the application you need to export an environment variable -that tells Flask where to find the application instance: +Install your application so it is importable: -.. tabs:: - - .. group-tab:: Bash - - .. code-block:: text - - $ export FLASK_APP=yourapplication - - .. group-tab:: Fish - - .. code-block:: text - - $ set -x FLASK_APP yourapplication - - .. group-tab:: CMD - - .. code-block:: text - - > set FLASK_APP=yourapplication - - .. group-tab:: Powershell - - .. code-block:: text - - > $env:FLASK_APP = "yourapplication" - -If you are outside of the project directory make sure to provide the exact -path to your application directory. Similarly you can turn on the -development features like this: - -.. tabs:: - - .. group-tab:: Bash - - .. code-block:: text - - $ export FLASK_ENV=development - - .. group-tab:: Fish - - .. code-block:: text - - $ set -x FLASK_ENV development - - .. group-tab:: CMD - - .. code-block:: text - - > set FLASK_ENV=development - - .. group-tab:: Powershell - - .. code-block:: text - - > $env:FLASK_ENV = "development" - -In order to install and run the application you need to issue the following -commands:: +.. code-block:: text $ pip install -e . - $ flask run + +To use the ``flask`` command and run your application you need to set +the ``--app`` option that tells Flask where to find the application +instance: + + $ flask --app yourapplication run What did we gain from this? Now we can restructure the application a bit into multiple modules. The only thing you have to remember is the diff --git a/docs/quickstart.rst b/docs/quickstart.rst index a6956c32..e6646c6c 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -39,50 +39,20 @@ Save it as :file:`hello.py` or something similar. Make sure to not call your application :file:`flask.py` because this would conflict with Flask itself. -To run the application, use the :command:`flask` command or -:command:`python -m flask`. Before you can do that you need -to tell your terminal the application to work with by exporting the -``FLASK_APP`` environment variable: +To run the application, use the ``flask`` command or +``python -m flask``. You need to tell the Flask where your application +is with the ``-app`` option. -.. tabs:: +.. code-block:: text - .. group-tab:: Bash - - .. code-block:: text - - $ export FLASK_APP=hello - $ flask run - * Running on http://127.0.0.1:5000/ - - .. group-tab:: Fish - - .. code-block:: text - - $ set -x FLASK_APP hello - $ flask run - * Running on http://127.0.0.1:5000/ - - .. group-tab:: CMD - - .. code-block:: text - - > set FLASK_APP=hello - > flask run - * Running on http://127.0.0.1:5000/ - - .. group-tab:: Powershell - - .. code-block:: text - - > $env:FLASK_APP = "hello" - > flask run - * Running on http://127.0.0.1:5000/ + $ flask --app hello run + * Serving Flask app 'hello' (lazy loading) + * Running on http://127.0.0.1:5000 (Press CTRL+C to quit) .. admonition:: Application Discovery Behavior As a shortcut, if the file is named ``app.py`` or ``wsgi.py``, you - don't have to set the ``FLASK_APP`` environment variable. See - :doc:`/cli` for more details. + don't have to use ``--app``. See :doc:`/cli` for more details. This launches a very simple builtin server, which is good enough for testing but probably not what you want to use in production. For @@ -114,34 +84,6 @@ handle that. This tells your operating system to listen on all public IPs. -What to do if the Server does not Start ---------------------------------------- - -In case the :command:`python -m flask` fails or :command:`flask` -does not exist, there are multiple reasons this might be the case. -First of all you need to look at the error message. - -Old Version of Flask -```````````````````` - -Versions of Flask older than 0.11 used to have different ways to start the -application. In short, the :command:`flask` command did not exist, and -neither did :command:`python -m flask`. In that case you have two options: -either upgrade to newer Flask versions or have a look at :doc:`/server` -to see the alternative method for running a server. - -Invalid Import Name -``````````````````` - -The ``FLASK_APP`` environment variable is the name of the module to import at -:command:`flask run`. In case that module is incorrectly named you will get an -import error upon start (or if debug is enabled when you navigate to the -application). It will tell you what it tried to import and why it failed. - -The most common reason is a typo or because you did not actually create an -``app`` object. - - Debug Mode ---------- @@ -162,38 +104,19 @@ error occurs during a request. security risk. Do not run the development server or debugger in a production environment. -To enable all development features, set the ``FLASK_ENV`` environment -variable to ``development`` before calling ``flask run``. +To enable all development features, set the ``--env`` option to +``development``. -.. tabs:: +.. code-block:: text - .. group-tab:: Bash - - .. code-block:: text - - $ export FLASK_ENV=development - $ flask run - - .. group-tab:: Fish - - .. code-block:: text - - $ set -x FLASK_ENV development - $ flask run - - .. group-tab:: CMD - - .. code-block:: text - - > set FLASK_ENV=development - > flask run - - .. group-tab:: Powershell - - .. code-block:: text - - > $env:FLASK_ENV = "development" - > flask run + $ flask --app hello --env development run + * Serving Flask app 'hello' (lazy loading) + * Environment: development + * Debug mode: on + * Running on http://127.0.0.1:5000 (Press CTRL+C to quit) + * Restarting with stat + * Debugger is active! + * Debugger PIN: nnn-nnn-nnn See also: diff --git a/docs/reqcontext.rst b/docs/reqcontext.rst index b67745ed..f395e844 100644 --- a/docs/reqcontext.rst +++ b/docs/reqcontext.rst @@ -227,8 +227,8 @@ associated with it is destroyed. If an error occurs during development, it is useful to delay destroying the data for debugging purposes. When the development server is running in development mode (the -``FLASK_ENV`` environment variable is set to ``'development'``), the -error and data will be preserved and shown in the interactive debugger. +``--env`` option is set to ``'development'``), the error and data will +be preserved and shown in the interactive debugger. This behavior can be controlled with the :data:`PRESERVE_CONTEXT_ON_EXCEPTION` config. As described above, it diff --git a/docs/server.rst b/docs/server.rst index f674bcd7..aa1438a3 100644 --- a/docs/server.rst +++ b/docs/server.rst @@ -19,9 +19,16 @@ Command Line ------------ The ``flask run`` command line script is the recommended way to run the -development server. It requires setting the ``FLASK_APP`` environment -variable to point to your application, and ``FLASK_ENV=development`` to -fully enable development mode. +development server. Use the ``--app`` option to point to your +application, and the ``--env development`` option to fully enable +development mode. + +.. code-block:: text + + $ flask --app hello --env development run + +These options (and any others) can also be set using environment +variables. .. tabs:: @@ -65,11 +72,11 @@ and using the CLI. .. note:: - Prior to Flask 1.0 the ``FLASK_ENV`` environment variable was not - supported and you needed to enable debug mode by exporting - ``FLASK_DEBUG=1``. This can still be used to control debug mode, but - you should prefer setting the development environment as shown - above. + Debug mode can be controlled separately from the development + environment with the ``--debug/--no-debug`` option or the + ``FLASK_DEBUG`` environment variable. This is how older versions of + Flask worked. You should prefer setting the development environment + as shown above. .. _address-already-in-use: diff --git a/docs/tutorial/database.rst b/docs/tutorial/database.rst index b094909e..b2852197 100644 --- a/docs/tutorial/database.rst +++ b/docs/tutorial/database.rst @@ -196,15 +196,13 @@ previous page. If you're still running the server from the previous page, you can either stop the server, or run this command in a new terminal. If you use a new terminal, remember to change to your project directory - and activate the env as described in :doc:`/installation`. You'll - also need to set ``FLASK_APP`` and ``FLASK_ENV`` as shown on the - previous page. + and activate the env as described in :doc:`/installation`. Run the ``init-db`` command: .. code-block:: none - $ flask init-db + $ flask --app flaskr init-db Initialized the database. There will now be a ``flaskr.sqlite`` file in the ``instance`` folder in diff --git a/docs/tutorial/deploy.rst b/docs/tutorial/deploy.rst index 26940240..436ed5e8 100644 --- a/docs/tutorial/deploy.rst +++ b/docs/tutorial/deploy.rst @@ -48,35 +48,9 @@ Pip will install your project along with its dependencies. Since this is a different machine, you need to run ``init-db`` again to create the database in the instance folder. -.. tabs:: + .. code-block:: text - .. group-tab:: Bash - - .. code-block:: text - - $ export FLASK_APP=flaskr - $ flask init-db - - .. group-tab:: Fish - - .. code-block:: text - - $ set -x FLASK_APP flaskr - $ flask init-db - - .. group-tab:: CMD - - .. code-block:: text - - > set FLASK_APP=flaskr - > flask init-db - - .. group-tab:: Powershell - - .. code-block:: text - - > $env:FLASK_APP = "flaskr" - > flask init-db + $ flask --app flaskr init-db When Flask detects that it's installed (not in editable mode), it uses a different directory for the instance folder. You can find it at @@ -127,7 +101,7 @@ first install it in the virtual environment: $ pip install waitress You need to tell Waitress about your application, but it doesn't use -``FLASK_APP`` like ``flask run`` does. You need to tell it to import and +``--app`` like ``flask run`` does. You need to tell it to import and call the application factory to get an application object. .. code-block:: none diff --git a/docs/tutorial/factory.rst b/docs/tutorial/factory.rst index 73081874..cbfecab3 100644 --- a/docs/tutorial/factory.rst +++ b/docs/tutorial/factory.rst @@ -135,43 +135,13 @@ exception, and restarts the server whenever you make changes to the code. You can leave it running and just reload the browser page as you follow the tutorial. -.. tabs:: +.. code-block:: text - .. group-tab:: Bash - - .. code-block:: text - - $ export FLASK_APP=flaskr - $ export FLASK_ENV=development - $ flask run - - .. group-tab:: Fish - - .. code-block:: text - - $ set -x FLASK_APP flaskr - $ set -x FLASK_ENV development - $ flask run - - .. group-tab:: CMD - - .. code-block:: text - - > set FLASK_APP=flaskr - > set FLASK_ENV=development - > flask run - - .. group-tab:: Powershell - - .. code-block:: text - - > $env:FLASK_APP = "flaskr" - > $env:FLASK_ENV = "development" - > flask run + $ flask --app flaskr --env development run You'll see output similar to this: -.. code-block:: none +.. code-block:: text * Serving Flask app "flaskr" * Environment: development @@ -179,7 +149,7 @@ You'll see output similar to this: * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! - * Debugger PIN: 855-212-761 + * Debugger PIN: nnn-nnn-nnn Visit http://127.0.0.1:5000/hello in a browser and you should see the "Hello, World!" message. Congratulations, you're now running your Flask diff --git a/docs/tutorial/install.rst b/docs/tutorial/install.rst index 9e808f14..7380b302 100644 --- a/docs/tutorial/install.rst +++ b/docs/tutorial/install.rst @@ -109,7 +109,7 @@ You can observe that the project is now installed with ``pip list``. wheel 0.30.0 Nothing changes from how you've been running your project so far. -``FLASK_APP`` is still set to ``flaskr`` and ``flask run`` still runs +``--app`` is still set to ``flaskr`` and ``flask run`` still runs the application, but you can call it from anywhere, not just the ``flask-tutorial`` directory. diff --git a/examples/javascript/README.rst b/examples/javascript/README.rst index b6e340df..23c7ce43 100644 --- a/examples/javascript/README.rst +++ b/examples/javascript/README.rst @@ -33,8 +33,7 @@ Run .. code-block:: text - $ export FLASK_APP=js_example - $ flask run + $ flask --app js_example run Open http://127.0.0.1:5000 in a browser. diff --git a/examples/tutorial/README.rst b/examples/tutorial/README.rst index 41f3f6ba..6c7c2fe9 100644 --- a/examples/tutorial/README.rst +++ b/examples/tutorial/README.rst @@ -45,19 +45,10 @@ installing Flaskr:: Run --- -:: +.. code-block:: text - $ export FLASK_APP=flaskr - $ export FLASK_ENV=development - $ flask init-db - $ flask run - -Or on Windows cmd:: - - > set FLASK_APP=flaskr - > set FLASK_ENV=development - > flask init-db - > flask run + $ flask --app flaskr init-db + $ flask --app flaskr --env development run Open http://127.0.0.1:5000 in a browser. diff --git a/tests/conftest.py b/tests/conftest.py index 17ff2f3d..1e1ba0d4 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -18,6 +18,7 @@ def _standard_os_environ(): """ mp = monkeypatch.MonkeyPatch() out = ( + (os.environ, "FLASK_ENV_FILE", monkeypatch.notset), (os.environ, "FLASK_APP", monkeypatch.notset), (os.environ, "FLASK_ENV", monkeypatch.notset), (os.environ, "FLASK_DEBUG", monkeypatch.notset),