Skip to content

Commit

Permalink
Merge pull request #4722 from pallets/deprecate-config-attrs
Browse files Browse the repository at this point in the history
deprecate config attributes
  • Loading branch information
davidism committed Aug 2, 2022
2 parents 98ca00d + bfdd371 commit cfd5783
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 57 deletions.
5 changes: 5 additions & 0 deletions CHANGES.rst
Expand Up @@ -27,6 +27,11 @@ Unreleased
deprecated, removing the distinction between development and debug
mode. Debug mode should be controlled directly using the ``--debug``
option or ``app.run(debug=True)``. :issue:`4714`
- Some attributes that proxied config keys on ``app`` are deprecated:
``session_cookie_name``, ``send_file_max_age_default``,
``use_x_sendfile``, ``propagate_exceptions``, and
``templates_auto_reload``. Use the relevant config keys instead.
:issue:`4716`
- Add new customization points to the ``Flask`` app object for many
previously global behaviors.

Expand Down
7 changes: 3 additions & 4 deletions docs/api.rst
Expand Up @@ -125,10 +125,9 @@ implementation that Flask is using.

.. admonition:: Notice

The ``PERMANENT_SESSION_LIFETIME`` config key can also be an integer
starting with Flask 0.8. Either catch this down yourself or use
the :attr:`~flask.Flask.permanent_session_lifetime` attribute on the
app which converts the result to an integer automatically.
The :data:`PERMANENT_SESSION_LIFETIME` config can be an integer or ``timedelta``.
The :attr:`~flask.Flask.permanent_session_lifetime` attribute is always a
``timedelta``.


Test Client
Expand Down
171 changes: 137 additions & 34 deletions src/flask/app.py
Expand Up @@ -99,7 +99,7 @@ def iscoroutinefunction(func: t.Any) -> bool:
return inspect.iscoroutinefunction(func)


def _make_timedelta(value: t.Optional[timedelta]) -> t.Optional[timedelta]:
def _make_timedelta(value: t.Union[timedelta, int, None]) -> t.Optional[timedelta]:
if value is None or isinstance(value, timedelta):
return value

Expand Down Expand Up @@ -273,11 +273,35 @@ class Flask(Scaffold):
#: :data:`SECRET_KEY` configuration key. Defaults to ``None``.
secret_key = ConfigAttribute("SECRET_KEY")

#: The secure cookie uses this for the name of the session cookie.
#:
#: This attribute can also be configured from the config with the
#: ``SESSION_COOKIE_NAME`` configuration key. Defaults to ``'session'``
session_cookie_name = ConfigAttribute("SESSION_COOKIE_NAME")
@property
def session_cookie_name(self) -> str:
"""The name of the cookie set by the session interface.
.. deprecated:: 2.2
Will be removed in Flask 2.3. Use ``app.config["SESSION_COOKIE_NAME"]``
instead.
"""
import warnings

warnings.warn(
"'session_cookie_name' is deprecated and will be removed in Flask 2.3. Use"
" 'SESSION_COOKIE_NAME' in 'app.config' instead.",
DeprecationWarning,
stacklevel=2,
)
return self.config["SESSION_COOKIE_NAME"]

@session_cookie_name.setter
def session_cookie_name(self, value: str) -> None:
import warnings

warnings.warn(
"'session_cookie_name' is deprecated and will be removed in Flask 2.3. Use"
" 'SESSION_COOKIE_NAME' in 'app.config' instead.",
DeprecationWarning,
stacklevel=2,
)
self.config["SESSION_COOKIE_NAME"] = value

#: A :class:`~datetime.timedelta` which is used to set the expiration
#: date of a permanent session. The default is 31 days which makes a
Expand All @@ -290,29 +314,70 @@ class Flask(Scaffold):
"PERMANENT_SESSION_LIFETIME", get_converter=_make_timedelta
)

#: A :class:`~datetime.timedelta` or number of seconds which is used
#: as the default ``max_age`` for :func:`send_file`. The default is
#: ``None``, which tells the browser to use conditional requests
#: instead of a timed cache.
#:
#: Configured with the :data:`SEND_FILE_MAX_AGE_DEFAULT`
#: configuration key.
#:
#: .. versionchanged:: 2.0
#: Defaults to ``None`` instead of 12 hours.
send_file_max_age_default = ConfigAttribute(
"SEND_FILE_MAX_AGE_DEFAULT", get_converter=_make_timedelta
)
@property
def send_file_max_age_default(self) -> t.Optional[timedelta]:
"""The default value for ``max_age`` for :func:`~flask.send_file`. The default
is ``None``, which tells the browser to use conditional requests instead of a
timed cache.
#: Enable this if you want to use the X-Sendfile feature. Keep in
#: mind that the server has to support this. This only affects files
#: sent with the :func:`send_file` method.
#:
#: .. versionadded:: 0.2
#:
#: This attribute can also be configured from the config with the
#: ``USE_X_SENDFILE`` configuration key. Defaults to ``False``.
use_x_sendfile = ConfigAttribute("USE_X_SENDFILE")
.. deprecated:: 2.2
Will be removed in Flask 2.3. Use
``app.config["SEND_FILE_MAX_AGE_DEFAULT"]`` instead.
.. versionchanged:: 2.0
Defaults to ``None`` instead of 12 hours.
"""
import warnings

warnings.warn(
"'send_file_max_age_default' is deprecated and will be removed in Flask"
" 2.3. Use 'SEND_FILE_MAX_AGE_DEFAULT' in 'app.config' instead.",
DeprecationWarning,
stacklevel=2,
)
return _make_timedelta(self.config["SEND_FILE_MAX_AGE_DEFAULT"])

@send_file_max_age_default.setter
def send_file_max_age_default(self, value: t.Union[int, timedelta, None]) -> None:
import warnings

warnings.warn(
"'send_file_max_age_default' is deprecated and will be removed in Flask"
" 2.3. Use 'SEND_FILE_MAX_AGE_DEFAULT' in 'app.config' instead.",
DeprecationWarning,
stacklevel=2,
)
self.config["SEND_FILE_MAX_AGE_DEFAULT"] = _make_timedelta(value)

@property
def use_x_sendfile(self) -> bool:
"""Enable this to use the ``X-Sendfile`` feature, assuming the server supports
it, from :func:`~flask.send_file`.
.. deprecated:: 2.2
Will be removed in Flask 2.3. Use ``app.config["USE_X_SENDFILE"]`` instead.
"""
import warnings

warnings.warn(
"'use_x_sendfile' is deprecated and will be removed in Flask 2.3. Use"
" 'USE_X_SENDFILE' in 'app.config' instead.",
DeprecationWarning,
stacklevel=2,
)
return self.config["USE_X_SENDFILE"]

@use_x_sendfile.setter
def use_x_sendfile(self, value: bool) -> None:
import warnings

warnings.warn(
"'use_x_sendfile' is deprecated and will be removed in Flask 2.3. Use"
" 'USE_X_SENDFILE' in 'app.config' instead.",
DeprecationWarning,
stacklevel=2,
)
self.config["USE_X_SENDFILE"] = value

#: The JSON encoder class to use. Defaults to
#: :class:`~flask.json.JSONEncoder`.
Expand Down Expand Up @@ -624,8 +689,18 @@ def propagate_exceptions(self) -> bool:
"""Returns the value of the ``PROPAGATE_EXCEPTIONS`` configuration
value in case it's set, otherwise a sensible default is returned.
.. deprecated:: 2.2
Will be removed in Flask 2.3.
.. versionadded:: 0.7
"""
import warnings

warnings.warn(
"'propagate_exceptions' is deprecated and will be removed in Flask 2.3.",
DeprecationWarning,
stacklevel=2,
)
rv = self.config["PROPAGATE_EXCEPTIONS"]
if rv is not None:
return rv
Expand Down Expand Up @@ -734,20 +809,37 @@ def open_instance_resource(self, resource: str, mode: str = "rb") -> t.IO[t.AnyS
@property
def templates_auto_reload(self) -> bool:
"""Reload templates when they are changed. Used by
:meth:`create_jinja_environment`.
:meth:`create_jinja_environment`. It is enabled by default in debug mode.
This attribute can be configured with :data:`TEMPLATES_AUTO_RELOAD`. If
not set, it will be enabled in debug mode.
.. deprecated:: 2.2
Will be removed in Flask 2.3. Use ``app.config["TEMPLATES_AUTO_RELOAD"]``
instead.
.. versionadded:: 1.0
This property was added but the underlying config and behavior
already existed.
"""
import warnings

warnings.warn(
"'templates_auto_reload' is deprecated and will be removed in Flask 2.3."
" Use 'TEMPLATES_AUTO_RELOAD' in 'app.config' instead.",
DeprecationWarning,
stacklevel=2,
)
rv = self.config["TEMPLATES_AUTO_RELOAD"]
return rv if rv is not None else self.debug

@templates_auto_reload.setter
def templates_auto_reload(self, value: bool) -> None:
import warnings

warnings.warn(
"'templates_auto_reload' is deprecated and will be removed in Flask 2.3."
" Use 'TEMPLATES_AUTO_RELOAD' in 'app.config' instead.",
DeprecationWarning,
stacklevel=2,
)
self.config["TEMPLATES_AUTO_RELOAD"] = value

def create_jinja_environment(self) -> Environment:
Expand All @@ -768,7 +860,12 @@ def create_jinja_environment(self) -> Environment:
options["autoescape"] = self.select_jinja_autoescape

if "auto_reload" not in options:
options["auto_reload"] = self.templates_auto_reload
auto_reload = self.config["TEMPLATES_AUTO_RELOAD"]

if auto_reload is None:
auto_reload = self.debug

options["auto_reload"] = auto_reload

rv = self.jinja_environment(self, **options)
rv.globals.update(
Expand Down Expand Up @@ -898,7 +995,9 @@ def debug(self) -> bool:
@debug.setter
def debug(self, value: bool) -> None:
self.config["DEBUG"] = value
self.jinja_env.auto_reload = self.templates_auto_reload

if self.config["TEMPLATES_AUTO_RELOAD"] is None:
self.jinja_env.auto_reload = value

def run(
self,
Expand Down Expand Up @@ -1541,8 +1640,12 @@ def handle_exception(self, e: Exception) -> Response:
"""
exc_info = sys.exc_info()
got_request_exception.send(self, exception=e)
propagate = self.config["PROPAGATE_EXCEPTIONS"]

if propagate is None:
propagate = self.testing or self.debug

if self.propagate_exceptions:
if propagate:
# Re-raise if called with an active exception, otherwise
# raise the passed in exception.
if exc_info[1] is e:
Expand Down
2 changes: 1 addition & 1 deletion src/flask/helpers.py
Expand Up @@ -414,7 +414,7 @@ def _prepare_send_file_kwargs(**kwargs: t.Any) -> t.Dict[str, t.Any]:

kwargs.update(
environ=request.environ,
use_x_sendfile=current_app.use_x_sendfile,
use_x_sendfile=current_app.config["USE_X_SENDFILE"],
response_class=current_app.response_class,
_root_path=current_app.root_path, # type: ignore
)
Expand Down
8 changes: 6 additions & 2 deletions src/flask/scaffold.py
Expand Up @@ -5,6 +5,7 @@
import sys
import typing as t
from collections import defaultdict
from datetime import timedelta
from functools import update_wrapper

from jinja2 import FileSystemLoader
Expand Down Expand Up @@ -302,12 +303,15 @@ def get_send_file_max_age(self, filename: t.Optional[str]) -> t.Optional[int]:
.. versionadded:: 0.9
"""
value = current_app.send_file_max_age_default
value = current_app.config["SEND_FILE_MAX_AGE_DEFAULT"]

if value is None:
return None

return int(value.total_seconds())
if isinstance(value, timedelta):
return int(value.total_seconds())

return value

def send_static_file(self, filename: str) -> "Response":
"""The view function used to serve files from
Expand Down
7 changes: 2 additions & 5 deletions src/flask/sessions.py
Expand Up @@ -177,11 +177,8 @@ def is_null_session(self, obj: object) -> bool:
return isinstance(obj, self.null_session_class)

def get_cookie_name(self, app: "Flask") -> str:
"""Returns the name of the session cookie.
Uses ``app.session_cookie_name`` which is set to ``SESSION_COOKIE_NAME``
"""
return app.session_cookie_name
"""The name of the session cookie. Uses``app.config["SESSION_COOKIE_NAME"]``."""
return app.config["SESSION_COOKIE_NAME"]

def get_cookie_domain(self, app: "Flask") -> t.Optional[str]:
"""Returns the domain that should be set for the session cookie.
Expand Down
9 changes: 0 additions & 9 deletions tests/test_config.py
@@ -1,7 +1,6 @@
import json
import os
import textwrap
from datetime import timedelta

import pytest

Expand Down Expand Up @@ -207,14 +206,6 @@ def test_session_lifetime():
assert app.permanent_session_lifetime.seconds == 42


def test_send_file_max_age():
app = flask.Flask(__name__)
app.config["SEND_FILE_MAX_AGE_DEFAULT"] = 3600
assert app.send_file_max_age_default.seconds == 3600
app.config["SEND_FILE_MAX_AGE_DEFAULT"] = timedelta(hours=2)
assert app.send_file_max_age_default.seconds == 7200


def test_get_namespace():
app = flask.Flask(__name__)
app.config["FOO_OPTION_1"] = "foo option 1"
Expand Down
2 changes: 0 additions & 2 deletions tests/test_templating.py
Expand Up @@ -397,11 +397,9 @@ def run_simple_mock(*args, **kwargs):
monkeypatch.setattr(werkzeug.serving, "run_simple", run_simple_mock)

app.run()
assert not app.templates_auto_reload
assert not app.jinja_env.auto_reload

app.run(debug=True)
assert app.templates_auto_reload
assert app.jinja_env.auto_reload


Expand Down

0 comments on commit cfd5783

Please sign in to comment.