Merge pull request #4646 from pallets/cli-app-env

This commit is contained in:
David Lord 2022-06-17 09:30:28 -07:00 committed by GitHub
commit ae547270e9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 344 additions and 634 deletions

View file

@ -27,6 +27,12 @@ Unreleased
instance on every request. :issue:`2520`. instance on every request. :issue:`2520`.
- A ``flask.cli.FlaskGroup`` Click group can be nested as a - A ``flask.cli.FlaskGroup`` Click group can be nested as a
sub-command in a custom CLI. :issue:`3263` 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 Version 2.1.3

View file

@ -15,40 +15,10 @@ Application Discovery
--------------------- ---------------------
The ``flask`` command is installed by Flask, not your application; it must be 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`` told where to find your application in order to use it. The ``--app``
environment variable is used to specify how to load the application. option is used to specify how to load the application.
.. tabs:: While ``--app`` supports a variety of options for specifying your
.. 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
application, most use cases should be simple. Here are the typical values: application, most use cases should be simple. Here are the typical values:
(nothing) (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 automatically detecting an app (``app`` or ``application``) or
factory (``create_app`` or ``make_app``). factory (``create_app`` or ``make_app``).
``FLASK_APP=hello`` ``--app hello``
The given name is imported, automatically detecting an app (``app`` The given name is imported, automatically detecting an app (``app``
or ``application``) or factory (``create_app`` or ``make_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 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 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 be followed by arguments in parentheses. The following values demonstrate these
parts: parts:
``FLASK_APP=src/hello`` ``--app src/hello``
Sets the current working directory to ``src`` then imports ``hello``. Sets the current working directory to ``src`` then imports ``hello``.
``FLASK_APP=hello.web`` ``--app hello.web``
Imports the path ``hello.web``. Imports the path ``hello.web``.
``FLASK_APP=hello:app2`` ``--app hello:app2``
Uses the ``app2`` Flask instance in ``hello``. 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'`` The ``create_app`` factory in ``hello`` is called with the string ``'dev'``
as the argument. 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 "wsgi" (as a ".py" file, or package) and try to detect an application
instance or factory. instance or factory.
@ -137,8 +107,9 @@ Environments
.. versionadded:: 1.0 .. versionadded:: 1.0
The environment in which the Flask app runs is set by the The environment in which the Flask app executes is set by the
:envvar:`FLASK_ENV` environment variable. If not set it defaults to ``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``. ``production``. The other recognized environment is ``development``.
Flask and extensions may choose to enable behaviors based on the Flask and extensions may choose to enable behaviors based on the
environment. 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 debug mode and ``flask run`` will enable the interactive debugger and
reloader. reloader.
.. tabs:: .. code-block:: text
.. group-tab:: Bash $ flask --app hello --env development run
* Serving Flask app "hello"
.. code-block:: text * Environment: development
* Debug mode: on
$ export FLASK_ENV=development * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
$ flask run * Restarting with inotify reloader
* Serving Flask app "hello" * Debugger is active!
* Environment: development * Debugger PIN: 223-456-919
* 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
Watch Extra Files with the Reloader 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 When using development mode, the reloader will trigger whenever your
Python code or imported modules change. The reloader can watch Python code or imported modules change. The reloader can watch
additional files with the ``--extra-files`` option, or the additional files with the ``--extra-files`` option. Multiple paths are
``FLASK_RUN_EXTRA_FILES`` environment variable. Multiple paths are
separated with ``:``, or ``;`` on Windows. separated with ``:``, or ``;`` on Windows.
.. tabs:: .. code-block:: text
.. group-tab:: Bash $ flask run --extra-files file1:dirA/file2:dirB/
* Running on http://127.0.0.1:8000/
.. code-block:: text * Detected change in '/path/to/file1', reloading
$ 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
Ignore files with the Reloader Ignore files with the Reloader
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The reloader can also ignore files using :mod:`fnmatch` patterns with The reloader can also ignore files using :mod:`fnmatch` patterns with
the ``--exclude-patterns`` option, or the ``FLASK_RUN_EXCLUDE_PATTERNS`` the ``--exclude-patterns`` option. Multiple patterns are separated with
environment variable. Multiple patterns are separated with ``:``, or ``:``, or ``;`` on Windows.
``;`` on Windows.
Debug Mode Debug Mode
---------- ----------
Debug mode will be enabled when :envvar:`FLASK_ENV` is ``development``, Debug mode will be enabled when the execution environment is
as described above. If you want to control debug mode separately, use ``development``, as described above. If you want to control debug mode
:envvar:`FLASK_DEBUG`. The value ``1`` enables it, ``0`` disables it. separately, use the ``--debug/--no-debug`` option or the ``FLASK_DEBUG``
environment variable.
.. _dotenv: .. _dotenv:
@ -284,14 +167,21 @@ as described above. If you want to control debug mode separately, use
Environment Variables From dotenv Environment Variables From dotenv
--------------------------------- ---------------------------------
Rather than setting ``FLASK_APP`` each time you open a new terminal, you can The ``flask`` command supports setting any option for any command with
use Flask's dotenv support to set environment variables automatically. 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 If `python-dotenv`_ is installed, running the ``flask`` command will set
environment variables defined in the files :file:`.env` and :file:`.flaskenv`. environment variables defined in the files ``.env`` and ``.flaskenv``.
This can be used to avoid having to set ``FLASK_APP`` manually every time you You can also specify an extra file to load with the ``--env-file``
open a new terminal, and to set configuration using environment variables option. Dotenv files can be used to avoid having to set ``--app`` or
similar to how some deployment services work. ``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`, 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 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 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 your application, you can create your own Click object and export it as a
`console script`_ entry point. `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 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 . $ pip install -e .
$ wiki run $ wiki run

View file

@ -47,61 +47,33 @@ Environment and Debug Features
The :data:`ENV` and :data:`DEBUG` config values are special because they The :data:`ENV` and :data:`DEBUG` config values are special because they
may behave inconsistently if changed after the app has begun setting up. may behave inconsistently if changed after the app has begun setting up.
In order to set the environment and debug mode reliably, Flask uses In order to set the environment and debug mode reliably, pass options to
environment variables. the ``flask`` command or use environment variables.
The environment is used to indicate to Flask, extensions, and other The execution environment is used to indicate to Flask, extensions, and
programs, like Sentry, what context Flask is running in. It is other programs, like Sentry, what context Flask is running in. It is
controlled with the :envvar:`FLASK_ENV` environment variable and controlled with the ``FLASK_ENV`` environment variable, or the
defaults to ``production``. ``--env`` option when using the ``flask`` command, and defaults to
``production``.
Setting :envvar:`FLASK_ENV` to ``development`` will enable debug mode. Setting ``--env development`` will enable debug mode. ``flask run`` will
``flask run`` will use the interactive debugger and reloader by default use the interactive debugger and reloader by default in debug mode. To
in debug mode. To control this separately from the environment, use the control this separately from the environment, use the
:envvar:`FLASK_DEBUG` flag. ``--debug/--no-debug`` option or the ``FLASK_DEBUG`` environment
variable.
.. versionchanged:: 1.0
Added :envvar:`FLASK_ENV` to control the environment separately
from debug mode. The development environment enables debug mode.
To switch Flask to the development environment and enable debug mode, 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 Using the options or environment variables as described above is
recommended. While it is possible to set :data:`ENV` and :data:`DEBUG`
$ export FLASK_ENV=development in your config or code, this is strongly discouraged. They can't be read
$ flask run early by the ``flask`` command, and some systems or extensions may have
already configured themselves based on a previous value.
.. 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.
Builtin Configuration Values Builtin Configuration Values

View file

@ -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 security risk. Do not run the development server or debugger in a
production environment. production environment.
To enable the debugger, run the development server with the To enable the debugger, run the development server with the environment
``FLASK_ENV`` environment variable set to ``development``. This puts set to ``development``. This puts Flask in debug mode, which changes how
Flask in debug mode, which changes how it handles some errors, and it handles some errors, and enables the debugger and reloader.
enables the debugger and reloader.
.. tabs:: .. code-block:: text
.. group-tab:: Bash $ flask --app hello --env development run
.. code-block:: text ``FLASK_ENV`` can also be set as an environment variable. When running
$ 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
from Python code, passing ``debug=True`` enables debug mode, which is from Python code, passing ``debug=True`` enables debug mode, which is
mostly equivalent. Debug mode can be controlled separately from mostly equivalent. Debug mode can be controlled separately from the
``FLASK_ENV`` with the ``FLASK_DEBUG`` environment variable as well. environment with the ``--debug/--no-debug`` option or the
``FLASK_DEBUG`` environment variable.
.. code-block:: python .. 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, but it can be useful to disable the built-in debugger and reloader,
which can interfere. which can interfere.
When running from the command line: .. code-block:: text
.. tabs:: $ flask --app hello --env development run --no-debugger --no-reload
.. 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
When running from Python: When running from Python:

View file

@ -89,71 +89,20 @@ Using Applications
To run such an application, you can use the :command:`flask` command: 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 .. code-block:: text
$ flask run
.. group-tab:: Fish $ flask run --app hello:create_app(local_auth=True)``
.. code-block:: text Then the ``create_app`` factory in ``myapp`` is called with the keyword
argument ``local_auth=True``. See :doc:`/cli` for more detail.
$ 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.
Factory Improvements Factory Improvements
-------------------- --------------------

View file

@ -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 Install your application so it is importable:
that tells Flask where to find the application instance:
.. tabs:: .. code-block:: text
.. 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::
$ pip install -e . $ 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 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 into multiple modules. The only thing you have to remember is the

View file

@ -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 your application :file:`flask.py` because this would conflict with Flask
itself. itself.
To run the application, use the :command:`flask` command or To run the application, use the ``flask`` command or
:command:`python -m flask`. Before you can do that you need ``python -m flask``. You need to tell the Flask where your application
to tell your terminal the application to work with by exporting the is with the ``-app`` option.
``FLASK_APP`` environment variable:
.. tabs:: .. code-block:: text
.. group-tab:: Bash $ flask --app hello run
* Serving Flask app 'hello' (lazy loading)
.. code-block:: text * Running on http://127.0.0.1:5000 (Press CTRL+C to quit)
$ 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/
.. admonition:: Application Discovery Behavior .. admonition:: Application Discovery Behavior
As a shortcut, if the file is named ``app.py`` or ``wsgi.py``, you 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 don't have to use ``--app``. See :doc:`/cli` for more details.
:doc:`/cli` for more details.
This launches a very simple builtin server, which is good enough for This launches a very simple builtin server, which is good enough for
testing but probably not what you want to use in production. 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. 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 Debug Mode
---------- ----------
@ -162,38 +104,19 @@ error occurs during a request.
security risk. Do not run the development server or debugger in a security risk. Do not run the development server or debugger in a
production environment. production environment.
To enable all development features, set the ``FLASK_ENV`` environment To enable all development features, set the ``--env`` option to
variable to ``development`` before calling ``flask run``. ``development``.
.. tabs:: .. code-block:: text
.. group-tab:: Bash $ flask --app hello --env development run
* Serving Flask app 'hello' (lazy loading)
.. code-block:: text * Environment: development
* Debug mode: on
$ export FLASK_ENV=development * Running on http://127.0.0.1:5000 (Press CTRL+C to quit)
$ flask run * Restarting with stat
* Debugger is active!
.. group-tab:: Fish * Debugger PIN: nnn-nnn-nnn
.. 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
See also: See also:

View file

@ -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. it is useful to delay destroying the data for debugging purposes.
When the development server is running in development mode (the When the development server is running in development mode (the
``FLASK_ENV`` environment variable is set to ``'development'``), the ``--env`` option is set to ``'development'``), the error and data will
error and data will be preserved and shown in the interactive debugger. be preserved and shown in the interactive debugger.
This behavior can be controlled with the This behavior can be controlled with the
:data:`PRESERVE_CONTEXT_ON_EXCEPTION` config. As described above, it :data:`PRESERVE_CONTEXT_ON_EXCEPTION` config. As described above, it

View file

@ -19,9 +19,16 @@ Command Line
------------ ------------
The ``flask run`` command line script is the recommended way to run the The ``flask run`` command line script is the recommended way to run the
development server. It requires setting the ``FLASK_APP`` environment development server. Use the ``--app`` option to point to your
variable to point to your application, and ``FLASK_ENV=development`` to application, and the ``--env development`` option to fully enable
fully enable development mode. development mode.
.. code-block:: text
$ flask --app hello --env development run
These options (and any others) can also be set using environment
variables.
.. tabs:: .. tabs::
@ -65,11 +72,11 @@ and using the CLI.
.. note:: .. note::
Prior to Flask 1.0 the ``FLASK_ENV`` environment variable was not Debug mode can be controlled separately from the development
supported and you needed to enable debug mode by exporting environment with the ``--debug/--no-debug`` option or the
``FLASK_DEBUG=1``. This can still be used to control debug mode, but ``FLASK_DEBUG`` environment variable. This is how older versions of
you should prefer setting the development environment as shown Flask worked. You should prefer setting the development environment
above. as shown above.
.. _address-already-in-use: .. _address-already-in-use:

View file

@ -196,15 +196,13 @@ previous page.
If you're still running the server from the previous page, you can 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 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 you use a new terminal, remember to change to your project directory
and activate the env as described in :doc:`/installation`. You'll and activate the env as described in :doc:`/installation`.
also need to set ``FLASK_APP`` and ``FLASK_ENV`` as shown on the
previous page.
Run the ``init-db`` command: Run the ``init-db`` command:
.. code-block:: none .. code-block:: none
$ flask init-db $ flask --app flaskr init-db
Initialized the database. Initialized the database.
There will now be a ``flaskr.sqlite`` file in the ``instance`` folder in There will now be a ``flaskr.sqlite`` file in the ``instance`` folder in

View file

@ -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 Since this is a different machine, you need to run ``init-db`` again to
create the database in the instance folder. create the database in the instance folder.
.. tabs:: .. code-block:: text
.. group-tab:: Bash $ flask --app flaskr init-db
.. 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
When Flask detects that it's installed (not in editable mode), it uses 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 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 $ pip install waitress
You need to tell Waitress about your application, but it doesn't use 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. call the application factory to get an application object.
.. code-block:: none .. code-block:: none

View file

@ -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 code. You can leave it running and just reload the browser page as you
follow the tutorial. follow the tutorial.
.. tabs:: .. code-block:: text
.. group-tab:: Bash $ flask --app flaskr --env development run
.. 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
You'll see output similar to this: You'll see output similar to this:
.. code-block:: none .. code-block:: text
* Serving Flask app "flaskr" * Serving Flask app "flaskr"
* Environment: development * 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) * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat * Restarting with stat
* Debugger is active! * 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 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 "Hello, World!" message. Congratulations, you're now running your Flask

View file

@ -109,7 +109,7 @@ You can observe that the project is now installed with ``pip list``.
wheel 0.30.0 wheel 0.30.0
Nothing changes from how you've been running your project so far. 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 the application, but you can call it from anywhere, not just the
``flask-tutorial`` directory. ``flask-tutorial`` directory.

View file

@ -33,8 +33,7 @@ Run
.. code-block:: text .. code-block:: text
$ export FLASK_APP=js_example $ flask --app js_example run
$ flask run
Open http://127.0.0.1:5000 in a browser. Open http://127.0.0.1:5000 in a browser.

View file

@ -45,19 +45,10 @@ installing Flaskr::
Run Run
--- ---
:: .. code-block:: text
$ export FLASK_APP=flaskr $ flask --app flaskr init-db
$ export FLASK_ENV=development $ flask --app flaskr --env development run
$ flask init-db
$ flask run
Or on Windows cmd::
> set FLASK_APP=flaskr
> set FLASK_ENV=development
> flask init-db
> flask run
Open http://127.0.0.1:5000 in a browser. Open http://127.0.0.1:5000 in a browser.

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import ast import ast
import inspect import inspect
import os import os
@ -12,6 +14,7 @@ from threading import Lock
from threading import Thread from threading import Thread
import click import click
from click.core import ParameterSource
from werkzeug.serving import is_running_from_reloader from werkzeug.serving import is_running_from_reloader
from werkzeug.utils import import_string 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_env
from .helpers import get_load_dotenv from .helpers import get_load_dotenv
if t.TYPE_CHECKING:
from .app import Flask
class NoAppException(click.UsageError): class NoAppException(click.UsageError):
"""Raised if an application cannot be found or loaded.""" """Raised if an application cannot be found or loaded."""
@ -46,8 +52,8 @@ def find_best_app(module):
elif len(matches) > 1: elif len(matches) > 1:
raise NoAppException( raise NoAppException(
"Detected multiple Flask applications in module" "Detected multiple Flask applications in module"
f" {module.__name__!r}. Use 'FLASK_APP={module.__name__}:name'" f" '{module.__name__}'. Use '{module.__name__}:name'"
f" to specify the correct one." " to specify the correct one."
) )
# Search for app factory functions. # Search for app factory functions.
@ -65,15 +71,15 @@ def find_best_app(module):
raise raise
raise NoAppException( 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" " 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." " to specify arguments."
) from e ) from e
raise NoAppException( raise NoAppException(
"Failed to find Flask application or factory in module" "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." " to specify one."
) )
@ -253,7 +259,7 @@ def get_version(ctx, param, value):
version_option = click.Option( version_option = click.Option(
["--version"], ["--version"],
help="Show the flask version", help="Show the Flask version.",
expose_value=False, expose_value=False,
callback=get_version, callback=get_version,
is_flag=True, is_flag=True,
@ -338,19 +344,24 @@ class ScriptInfo:
onwards as click object. 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. #: 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 #: Optionally a function that is passed the script info to create
#: the instance of the application. #: the instance of the application.
self.create_app = create_app self.create_app = create_app
#: A dictionary with arbitrary data that can be associated with #: A dictionary with arbitrary data that can be associated with
#: this script info. #: this script info.
self.data = {} self.data: t.Dict[t.Any, t.Any] = {}
self.set_debug_flag = set_debug_flag 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 """Loads the Flask app (if not yet loaded) and returns it. Calling
this multiple times will just result in the already loaded app to this multiple times will just result in the already loaded app to
be returned. be returned.
@ -379,9 +390,10 @@ class ScriptInfo:
if not app: if not app:
raise NoAppException( raise NoAppException(
"Could not locate a Flask application. You did not provide " "Could not locate a Flask application. Use the"
'the "FLASK_APP" environment variable, and a "wsgi.py" or ' " 'flask --app' option, 'FLASK_APP' environment"
'"app.py" module was not found in the current directory.' " variable, or a 'wsgi.py' or 'app.py' file in the"
" current directory."
) )
if self.set_debug_flag: if self.set_debug_flag:
@ -442,6 +454,117 @@ class AppGroup(click.Group):
return click.Group.group(self, *args, **kwargs) 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): class FlaskGroup(AppGroup):
"""Special subclass of the :class:`AppGroup` group that supports """Special subclass of the :class:`AppGroup` group that supports
loading more commands from the configured Flask app. Normally a 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 :param set_debug_flag: Set the app's debug flag based on the active
environment environment
.. versionchanged:: 2.2
Added the ``-A/--app``, ``-E/--env``, ``--debug/--no-debug``,
and ``-e/--env-file`` options.
.. versionchanged:: 1.0 .. versionchanged:: 1.0
If installed, python-dotenv will be used to load environment variables If installed, python-dotenv will be used to load environment variables
from :file:`.env` and :file:`.flaskenv` files. from :file:`.env` and :file:`.flaskenv` files.
@ -467,14 +594,19 @@ class FlaskGroup(AppGroup):
def __init__( def __init__(
self, self,
add_default_commands=True, add_default_commands: bool = True,
create_app=None, create_app: t.Callable[..., Flask] | None = None,
add_version_option=True, add_version_option: bool = True,
load_dotenv=True, load_dotenv: bool = True,
set_debug_flag=True, set_debug_flag: bool = True,
**extra, **extra: t.Any,
): ) -> None:
params = list(extra.pop("params", None) or ()) 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: if add_version_option:
params.append(version_option) params.append(version_option)
@ -555,11 +687,13 @@ class FlaskGroup(AppGroup):
def make_context( def make_context(
self, self,
info_name: t.Optional[str], info_name: str | None,
args: t.List[str], args: list[str],
parent: t.Optional[click.Context] = None, parent: click.Context | None = None,
**extra: t.Any, **extra: t.Any,
) -> click.Context: ) -> 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): if get_load_dotenv(self.load_dotenv):
load_dotenv() load_dotenv()
@ -570,6 +704,16 @@ class FlaskGroup(AppGroup):
return super().make_context(info_name, args, parent=parent, **extra) 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): def _path_is_ancestor(path, other):
"""Take ``other`` and remove the length of ``path`` from it. Then join it """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 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. """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 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. :param path: Load the file at this location instead of searching.
:return: ``True`` if a file was loaded. :return: ``True`` if a file was loaded.
.. versionchanged:: 1.1.0 .. versionchanged:: 2.0
Returns ``False`` when python-dotenv is not installed, or when The current directory is not changed to the location of the
the given path isn't a file. loaded file.
.. versionchanged:: 2.0 .. versionchanged:: 2.0
When loading the env files, set the default encoding to UTF-8. 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 .. versionadded:: 1.0
""" """
try: try:
@ -613,15 +761,15 @@ def load_dotenv(path=None):
return False return False
# if the given path specifies the actual file then return True, # Always return after attempting to load a given path, don't load
# else False # the default files.
if path is not None: if path is not None:
if os.path.isfile(path): if os.path.isfile(path):
return dotenv.load_dotenv(path, encoding="utf-8") return dotenv.load_dotenv(path, encoding="utf-8")
return False return False
new_dir = None loaded = False
for name in (".env", ".flaskenv"): for name in (".env", ".flaskenv"):
path = dotenv.find_dotenv(name, usecwd=True) path = dotenv.find_dotenv(name, usecwd=True)
@ -629,12 +777,10 @@ def load_dotenv(path=None):
if not path: if not path:
continue continue
if new_dir is None:
new_dir = os.path.dirname(path)
dotenv.load_dotenv(path, encoding="utf-8") 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): 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 This server is for development purposes only. It does not provide
the stability, security, or performance of production WSGI servers. the stability, security, or performance of production WSGI servers.
The reloader and debugger are enabled by default if The reloader and debugger are enabled by default with the
FLASK_ENV=development or FLASK_DEBUG=1. '--env development' or '--debug' options.
""" """
app = DispatchingApp(info.load_app, use_eager_loading=eager_loading)
debug = get_debug_flag() debug = get_debug_flag()
if reload is None: if reload is None:
@ -849,7 +996,6 @@ def run_command(
debugger = debug debugger = debug
show_server_banner(get_env(), debug, info.app_import_path, eager_loading) 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 from werkzeug.serving import run_simple
@ -971,19 +1117,10 @@ cli = FlaskGroup(
help="""\ help="""\
A general utility script for Flask applications. A general utility script for Flask applications.
Provides commands from Flask, extensions, and the application. Loads the An application to load must be given with the '--app' option,
application defined in the FLASK_APP environment variable, or from a wsgi.py 'FLASK_APP' environment variable, or with a 'wsgi.py' or 'app.py' file
file. Setting the FLASK_ENV environment variable to 'development' will enable in the current directory.
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 "> ",
),
) )

View file

@ -48,9 +48,9 @@ def get_debug_flag() -> bool:
def get_load_dotenv(default: bool = True) -> bool: def get_load_dotenv(default: bool = True) -> bool:
"""Get whether the user has disabled loading dotenv files by setting """Get whether the user has disabled loading default dotenv files by
:envvar:`FLASK_SKIP_DOTENV`. The default is ``True``, load the setting :envvar:`FLASK_SKIP_DOTENV`. The default is ``True``, load
files. the files.
:param default: What to return if the env var isn't set. :param default: What to return if the env var isn't set.
""" """

View file

@ -18,6 +18,7 @@ def _standard_os_environ():
""" """
mp = monkeypatch.MonkeyPatch() mp = monkeypatch.MonkeyPatch()
out = ( out = (
(os.environ, "FLASK_ENV_FILE", monkeypatch.notset),
(os.environ, "FLASK_APP", monkeypatch.notset), (os.environ, "FLASK_APP", monkeypatch.notset),
(os.environ, "FLASK_ENV", monkeypatch.notset), (os.environ, "FLASK_ENV", monkeypatch.notset),
(os.environ, "FLASK_DEBUG", monkeypatch.notset), (os.environ, "FLASK_DEBUG", monkeypatch.notset),