Browse Source

Add Support for FLASK_ENV (#2570)

This introduces environments to Flask
pull/2584/head
Armin Ronacher 7 years ago committed by GitHub
parent
commit
2433522d29
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 15
      docs/cli.rst
  2. 43
      docs/config.rst
  3. 6
      docs/patterns/packages.rst
  4. 9
      docs/quickstart.rst
  5. 16
      docs/server.rst
  6. 6
      docs/tutorial/packaging.rst
  7. 73
      flask/app.py
  8. 29
      flask/cli.py
  9. 14
      flask/helpers.py
  10. 17
      tests/test_helpers.py

15
docs/cli.rst

@ -116,6 +116,16 @@ context will be active, and the app instance will be imported. ::
Use :meth:`~Flask.shell_context_processor` to add other automatic imports. Use :meth:`~Flask.shell_context_processor` to add other automatic imports.
Environments
------------
.. versionadded:: 1.0
The environment in which the Flask app should run is set by the
:envvar:`FLASK_ENV` environment variable. If not set it defaults to
``production``. The other default environment which is known is
``development``. If the env is set to ``development`` the debug mode is
for instance automatically enabled.
Debug Mode Debug Mode
---------- ----------
@ -123,11 +133,14 @@ Debug Mode
Set the :envvar:`FLASK_DEBUG` environment variable to override the Set the :envvar:`FLASK_DEBUG` environment variable to override the
application's :attr:`~Flask.debug` flag. The value ``1`` enables it, ``0`` application's :attr:`~Flask.debug` flag. The value ``1`` enables it, ``0``
disables it. Forcing the debug flag on also enables the debugger and reloader disables it. Forcing the debug flag on also enables the debugger and reloader
when running the development server. :: when running the development server.
::
$ FLASK_DEBUG=1 flask run $ FLASK_DEBUG=1 flask run
* Serving Flask app "hello" * Serving Flask app "hello"
* Forcing debug mode on * Forcing debug mode on
* Env production
* 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 inotify reloader * Restarting with inotify reloader
* Debugger is active! * Debugger is active!

43
docs/config.rst

@ -27,35 +27,52 @@ The :attr:`~flask.Flask.config` is actually a subclass of a dictionary and
can be modified just like any dictionary:: can be modified just like any dictionary::
app = Flask(__name__) app = Flask(__name__)
app.config['DEBUG'] = True app.config['TESTING'] = True
Certain configuration values are also forwarded to the Certain configuration values are also forwarded to the
:attr:`~flask.Flask` object so you can read and write them from there:: :attr:`~flask.Flask` object so you can read and write them from there::
app.debug = True app.testing = True
To update multiple keys at once you can use the :meth:`dict.update` To update multiple keys at once you can use the :meth:`dict.update`
method:: method::
app.config.update( app.config.update(
DEBUG=True, TESTING=True,
SECRET_KEY=b'_5#y2L"F4Q8z\n\xec]/' SECRET_KEY=b'_5#y2L"F4Q8z\n\xec]/'
) )
.. admonition:: Debug Mode with the ``flask`` Script Environment and Debug Features
------------------------------
If you use the :command:`flask` script to start a local development Some values are special in that they can show unexpected behavior when
server, to enable the debug mode, you need to export the ``FLASK_DEBUG`` changed late. In particular that applies to the Flask environment and
environment variable before running the server:: debug mode.
$ export FLASK_DEBUG=1 If you use the :command:`flask` script to start a local development server
$ flask run for instance you should tell Flask that you want to work in the
development environment. For safety reasons we default the flask
environment to production mode instead of development. This is done
because development mode can turn on potentially unsafe features such as
the debugger by default.
(On Windows you need to use ``set`` instead of ``export``). To control the environment and such fundamental features Flask provides
the two environment variables :envvar:`FLASK_ENV` and :envvar:`FLASK_DEBUG`.
In versions of Flask older than 1.0 the :envvar:`FLASK_ENV` environment
variable did not exist.
``app.debug`` and ``app.config['DEBUG']`` are not compatible with The most common way to switch Flask to development mode is to tell it to
  the :command:`flask` script. They only worked when using ``Flask.run()`` work on the ``development`` environment::
method.
$ export FLASK_ENV=development
$ flask run
(On Windows you need to use ``set`` instead of ``export``).
While you can attempt to flip the environment and debug flag separately in
the Flask config from the config file this is strongly discouraged as
those flags are often loaded early and changing them late might not apply
to all systems and extensions.
Builtin Configuration Values Builtin Configuration Values
---------------------------- ----------------------------

6
docs/patterns/packages.rst

@ -65,10 +65,10 @@ that tells Flask where to find the application instance::
export FLASK_APP=yourapplication export FLASK_APP=yourapplication
If you are outside of the project directory make sure to provide the exact If you are outside of the project directory make sure to provide the exact
path to your application directory. Similarly you can turn on "debug path to your application directory. Similarly you can turn on the
mode" with this environment variable:: development features like this::
export FLASK_DEBUG=true export FLASK_ENV=development
In order to install and run the application you need to issue the following In order to install and run the application you need to issue the following
commands:: commands::

9
docs/quickstart.rst

@ -130,13 +130,16 @@ That is not very nice and Flask can do better. If you enable debug
support the server will reload itself on code changes, and it will also support the server will reload itself on code changes, and it will also
provide you with a helpful debugger if things go wrong. provide you with a helpful debugger if things go wrong.
To enable debug mode you can export the ``FLASK_DEBUG`` environment variable To enable all development features (and to disable the debug mode) you can
export the ``FLASK_ENV`` environment variable and set it to
``development``
before running the server:: before running the server::
$ export FLASK_DEBUG=1 $ export FLASK_ENV=development
$ flask run $ flask run
(On Windows you need to use ``set`` instead of ``export``). (On Windows you need to use ``set`` instead of ``export`` and on Flask
versions older than 1.0 you need to export ``FLASK_DEBUG=1`` instead).
This does the following things: This does the following things:

16
docs/server.rst

@ -12,12 +12,13 @@ but you can also continue using the :meth:`Flask.run` method.
Command Line Command Line
------------ ------------
The :command:`flask` command line script (:ref:`cli`) is strongly recommended for The :command:`flask` command line script (:ref:`cli`) is strongly
development because it provides a superior reload experience due to how it recommended for development because it provides a superior reload
loads the application. The basic usage is like this:: experience due to how it loads the application. The basic usage is like
this::
$ export FLASK_APP=my_application $ export FLASK_APP=my_application
$ export FLASK_DEBUG=1 $ export FLASK_ENV=development
$ flask run $ flask run
This will enable the debugger, the reloader and then start the server on This will enable the debugger, the reloader and then start the server on
@ -29,6 +30,13 @@ disabled::
$ flask run --no-reload $ flask run --no-reload
.. note::
On older Flask version (before 1.0) the :envvar:`FLASK_ENV`
environment variable is not supported and you need to enable the
debug mode separately by setting the :envvar:`FLASK_DEBUG` environment
variable to ``1``.
In Code In Code
------- -------

6
docs/tutorial/packaging.rst

@ -78,11 +78,13 @@ With that out of the way, you should be able to start up the application.
Do this on Mac or Linux with the following commands in ``flaskr/``:: Do this on Mac or Linux with the following commands in ``flaskr/``::
export FLASK_APP=flaskr export FLASK_APP=flaskr
export FLASK_DEBUG=true export FLASK_ENV=development
flask run flask run
(In case you are on Windows you need to use ``set`` instead of ``export``). (In case you are on Windows you need to use ``set`` instead of ``export``).
The :envvar:`FLASK_DEBUG` flag enables or disables the interactive debugger. The :envvar:`FLASK_ENV` flag if set to ``development`` turns on all
development features such as enabling the interactive debugger.
*Never leave debug mode activated in a production system*, because it will *Never leave debug mode activated in a production system*, because it will
allow users to execute code on the server! allow users to execute code on the server!

73
flask/app.py

@ -27,7 +27,7 @@ from .config import Config, ConfigAttribute
from .ctx import AppContext, RequestContext, _AppCtxGlobals from .ctx import AppContext, RequestContext, _AppCtxGlobals
from .globals import _request_ctx_stack, g, request, session from .globals import _request_ctx_stack, g, request, session
from .helpers import _PackageBoundObject, \ from .helpers import _PackageBoundObject, \
_endpoint_from_view_func, find_package, get_debug_flag, \ _endpoint_from_view_func, find_package, get_env, get_debug_flag, \
get_flashed_messages, locked_cached_property, url_for get_flashed_messages, locked_cached_property, url_for
from .logging import create_logger from .logging import create_logger
from .sessions import SecureCookieSessionInterface from .sessions import SecureCookieSessionInterface
@ -196,15 +196,6 @@ class Flask(_PackageBoundObject):
#: .. versionadded:: 0.11 #: .. versionadded:: 0.11
config_class = Config config_class = Config
#: The debug flag. Set this to ``True`` to enable debugging of the
#: application. In debug mode the debugger will kick in when an unhandled
#: exception occurs and the integrated server will automatically reload
#: the application if changes in the code are detected.
#:
#: This attribute can also be configured from the config with the ``DEBUG``
#: configuration key. Defaults to ``False``.
debug = ConfigAttribute('DEBUG')
#: The testing flag. Set this to ``True`` to enable the test mode of #: The testing flag. Set this to ``True`` to enable the test mode of
#: Flask extensions (and in the future probably also Flask itself). #: Flask extensions (and in the future probably also Flask itself).
#: For example this might activate test helpers that have an #: For example this might activate test helpers that have an
@ -278,7 +269,8 @@ class Flask(_PackageBoundObject):
#: Default configuration parameters. #: Default configuration parameters.
default_config = ImmutableDict({ default_config = ImmutableDict({
'DEBUG': get_debug_flag(default=False), 'ENV': None,
'DEBUG': None,
'TESTING': False, 'TESTING': False,
'PROPAGATE_EXCEPTIONS': None, 'PROPAGATE_EXCEPTIONS': None,
'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None,
@ -647,7 +639,10 @@ class Flask(_PackageBoundObject):
root_path = self.root_path root_path = self.root_path
if instance_relative: if instance_relative:
root_path = self.instance_path root_path = self.instance_path
return self.config_class(root_path, self.default_config) defaults = dict(self.default_config)
defaults['ENV'] = get_env()
defaults['DEBUG'] = get_debug_flag()
return self.config_class(root_path, defaults)
def auto_find_instance_path(self): def auto_find_instance_path(self):
"""Tries to locate the instance path if it was not provided to the """Tries to locate the instance path if it was not provided to the
@ -790,25 +785,38 @@ class Flask(_PackageBoundObject):
rv.update(processor()) rv.update(processor())
return rv return rv
def _reconfigure_for_run_debug(self, debug): #: The environment value. This is typically set from outside the
"""The ``run`` commands will set the application's debug flag. Some #: process by setting the `FLASK_ENV` environment variable and can be
application configuration may already be calculated based on the #: used to quickly switch between different environments like
previous debug value. This method will recalculate affected values. #: `production` and `development`. If not set this defaults to
#: `production`.
Called by the :func:`flask.cli.run` command or :meth:`Flask.run` env = ConfigAttribute('ENV')
method if the debug flag is set explicitly in the call.
def _get_debug(self):
:param debug: the new value of the debug flag return self.config['DEBUG']
def _set_debug(self, value):
.. versionadded:: 1.0 self._set_debug_value(value)
Reconfigures ``app.jinja_env.auto_reload``.
""" #: The debug flag. If this is ``True`` it enables debugging of the
self.debug = debug #: application. In debug mode the debugger will kick in when an
#: unhandled exception occurs and the integrated server will
#: automatically reload the application if changes in the code are
#: detected.
#:
#: This value should only be configured by the :envvar:`FLASK_DEBUG`
#: environment variable. Changing it by other means will not yield
#: consistent results. The default value depends on the Flask
#: environment and will be true for the development environment and false
#: otherwise.
debug = property(_get_debug, _set_debug)
del _get_debug, _set_debug
def _set_debug_value(self, value):
self.config['DEBUG'] = value
self.jinja_env.auto_reload = self.templates_auto_reload self.jinja_env.auto_reload = self.templates_auto_reload
def run( def run(self, host=None, port=None, debug=None,
self, host=None, port=None, debug=None, load_dotenv=True, **options load_dotenv=True, **options):
):
"""Runs the application on a local development server. """Runs the application on a local development server.
Do not use ``run()`` in a production setting. It is not intended to Do not use ``run()`` in a production setting. It is not intended to
@ -872,11 +880,11 @@ class Flask(_PackageBoundObject):
load_dotenv() load_dotenv()
if debug is not None: if debug is not None:
self._reconfigure_for_run_debug(bool(debug)) self.debug = bool(debug)
_host = '127.0.0.1' _host = '127.0.0.1'
_port = 5000 _port = 5000
server_name = self.config.get("SERVER_NAME") server_name = self.config.get('SERVER_NAME')
sn_host, sn_port = None, None sn_host, sn_port = None, None
if server_name: if server_name:
@ -1055,7 +1063,8 @@ class Flask(_PackageBoundObject):
return iter(self._blueprint_order) return iter(self._blueprint_order)
@setupmethod @setupmethod
def add_url_rule(self, rule, endpoint=None, view_func=None, provide_automatic_options=None, **options): def add_url_rule(self, rule, endpoint=None, view_func=None,
provide_automatic_options=None, **options):
"""Connects a URL rule. Works exactly like the :meth:`route` """Connects a URL rule. Works exactly like the :meth:`route`
decorator. If a view_func is provided it will be registered with the decorator. If a view_func is provided it will be registered with the
endpoint. endpoint.

29
flask/cli.py

@ -25,7 +25,7 @@ import click
from . import __version__ from . import __version__
from ._compat import getargspec, iteritems, reraise from ._compat import getargspec, iteritems, reraise
from .globals import current_app from .globals import current_app
from .helpers import get_debug_flag from .helpers import get_debug_flag, get_env
try: try:
import dotenv import dotenv
@ -341,9 +341,8 @@ class ScriptInfo(object):
else: else:
for path in ('wsgi.py', 'app.py'): for path in ('wsgi.py', 'app.py'):
import_name = prepare_import(path) import_name = prepare_import(path)
app = locate_app( app = locate_app(self, import_name, None,
self, import_name, None, raise_if_not_found=False raise_if_not_found=False)
)
if app: if app:
break break
@ -357,8 +356,10 @@ class ScriptInfo(object):
debug = get_debug_flag() debug = get_debug_flag()
# Update the app's debug flag through the descriptor so that other
# values repopulate as well.
if debug is not None: if debug is not None:
app._reconfigure_for_run_debug(debug) app.debug = debug
self._loaded_app = app self._loaded_app = app
return app return app
@ -432,10 +433,8 @@ class FlaskGroup(AppGroup):
from :file:`.env` and :file:`.flaskenv` files. from :file:`.env` and :file:`.flaskenv` files.
""" """
def __init__( def __init__(self, add_default_commands=True, create_app=None,
self, add_default_commands=True, create_app=None, add_version_option=True, load_dotenv=True, **extra):
add_version_option=True, load_dotenv=True, **extra
):
params = list(extra.pop('params', None) or ()) params = list(extra.pop('params', None) or ())
if add_version_option: if add_version_option:
@ -610,6 +609,13 @@ def run_command(info, host, port, reload, debugger, eager_loading,
""" """
from werkzeug.serving import run_simple from werkzeug.serving import run_simple
if get_env() == 'production':
click.secho('Warning: Detected a production environment. Do not '
'use `flask run` for production use.',
fg='red')
click.secho('Use a production ready WSGI server instead',
dim=True)
debug = get_debug_flag() debug = get_debug_flag()
if reload is None: if reload is None:
reload = bool(debug) reload = bool(debug)
@ -629,6 +635,7 @@ def run_command(info, host, port, reload, debugger, eager_loading,
# we won't print anything. # we won't print anything.
if info.app_import_path is not None: if info.app_import_path is not None:
print(' * Serving Flask app "%s"' % info.app_import_path) print(' * Serving Flask app "%s"' % info.app_import_path)
print(' * Env %s' % get_env())
if debug is not None: if debug is not None:
print(' * Forcing debug mode %s' % (debug and 'on' or 'off')) print(' * Forcing debug mode %s' % (debug and 'on' or 'off'))
@ -649,11 +656,11 @@ def shell_command():
import code import code
from flask.globals import _app_ctx_stack from flask.globals import _app_ctx_stack
app = _app_ctx_stack.top.app app = _app_ctx_stack.top.app
banner = 'Python %s on %s\nApp: %s%s\nInstance: %s' % ( banner = 'Python %s on %s\nApp: %s [%s]\nInstance: %s' % (
sys.version, sys.version,
sys.platform, sys.platform,
app.import_name, app.import_name,
app.debug and ' [debug]' or '', app.env,
app.instance_path, app.instance_path,
) )
ctx = {} ctx = {}

14
flask/helpers.py

@ -46,10 +46,20 @@ _os_alt_seps = list(sep for sep in [os.path.sep, os.path.altsep]
if sep not in (None, '/')) if sep not in (None, '/'))
def get_debug_flag(default=None): def get_env():
val = os.environ.get('FLASK_ENV')
if not val:
val = 'production'
return val
def get_debug_flag():
val = os.environ.get('FLASK_DEBUG') val = os.environ.get('FLASK_DEBUG')
if not val: if not val:
return default env = get_env()
if env == 'development':
return True
return False
return val.lower() not in ('0', 'false', 'no') return val.lower() not in ('0', 'false', 'no')

17
tests/test_helpers.py

@ -21,7 +21,7 @@ from werkzeug.http import http_date, parse_cache_control_header, \
import flask import flask
from flask._compat import StringIO, text_type from flask._compat import StringIO, text_type
from flask.helpers import get_debug_flag from flask.helpers import get_debug_flag, get_env
def has_encoding(name): def has_encoding(name):
@ -886,7 +886,7 @@ class TestSafeJoin(object):
class TestHelpers(object): class TestHelpers(object):
@pytest.mark.parametrize('debug, expected_flag, expected_default_flag', [ @pytest.mark.parametrize('debug, expected_flag, expected_default_flag', [
('', None, True), ('', False, False),
('0', False, False), ('0', False, False),
('False', False, False), ('False', False, False),
('No', False, False), ('No', False, False),
@ -898,7 +898,18 @@ class TestHelpers(object):
assert get_debug_flag() is None assert get_debug_flag() is None
else: else:
assert get_debug_flag() == expected_flag assert get_debug_flag() == expected_flag
assert get_debug_flag(default=True) == expected_default_flag assert get_debug_flag() == expected_default_flag
@pytest.mark.parametrize('env, ref_env, debug', [
('', 'production', False),
('production', 'production', False),
('development', 'development', True),
('other', 'other', False),
])
def test_get_env(self, monkeypatch, env, ref_env, debug):
monkeypatch.setenv('FLASK_ENV', env)
assert get_debug_flag() == debug
assert get_env() == ref_env
def test_make_response(self): def test_make_response(self):
app = flask.Flask(__name__) app = flask.Flask(__name__)

Loading…
Cancel
Save