Merge branch '2.0.x'

This commit is contained in:
David Lord 2021-05-20 21:05:59 -07:00
commit 83f7efa047
No known key found for this signature in database
GPG key ID: 7A1C87E3F5BC42A8
11 changed files with 76 additions and 44 deletions

View file

@ -31,6 +31,8 @@ Unreleased
- Roll back a change to the order that URL matching was done. The - Roll back a change to the order that URL matching was done. The
URL is again matched after the session is loaded, so the session is URL is again matched after the session is loaded, so the session is
available in custom URL converters. :issue:`4053` available in custom URL converters. :issue:`4053`
- Re-add deprecated ``Config.from_json``, which was accidentally
removed early. :issue:`4078`
Version 2.0.0 Version 2.0.0

View file

@ -49,10 +49,10 @@ html_context = {
] ]
} }
html_sidebars = { html_sidebars = {
"index": ["project.html", "localtoc.html", "searchbox.html"], "index": ["project.html", "localtoc.html", "searchbox.html", "ethicalads.html"],
"**": ["localtoc.html", "relations.html", "searchbox.html"], "**": ["localtoc.html", "relations.html", "searchbox.html", "ethicalads.html"],
} }
singlehtml_sidebars = {"index": ["project.html", "localtoc.html"]} singlehtml_sidebars = {"index": ["project.html", "localtoc.html", "ethicalads.html"]}
html_static_path = ["_static"] html_static_path = ["_static"]
html_favicon = "_static/flask-icon.png" html_favicon = "_static/flask-icon.png"
html_logo = "_static/flask-icon.png" html_logo = "_static/flask-icon.png"

View file

@ -246,7 +246,7 @@ of the argument like ``<converter:variable_name>``. ::
@app.route('/user/<username>') @app.route('/user/<username>')
def show_user_profile(username): def show_user_profile(username):
# show the user profile for that user # show the user profile for that user
return f'User {username}' return f'User {escape(username)}'
@app.route('/post/<int:post_id>') @app.route('/post/<int:post_id>')
def show_post(post_id): def show_post(post_id):
@ -256,7 +256,7 @@ of the argument like ``<converter:variable_name>``. ::
@app.route('/path/<path:subpath>') @app.route('/path/<path:subpath>')
def show_subpath(subpath): def show_subpath(subpath):
# show the subpath after /path/ # show the subpath after /path/
return f'Subpath {subpath}' return f'Subpath {escape(subpath)}'
Converter types: Converter types:

View file

@ -18,11 +18,11 @@ blinker==1.4
# via -r requirements/tests.in # via -r requirements/tests.in
certifi==2020.12.5 certifi==2020.12.5
# via requests # via requests
cfgv==3.2.0 cfgv==3.3.0
# via pre-commit # via pre-commit
chardet==4.0.0 chardet==4.0.0
# via requests # via requests
click==8.0.0 click==8.0.1
# via pip-tools # via pip-tools
distlib==0.3.1 distlib==0.3.1
# via virtualenv # via virtualenv
@ -44,9 +44,9 @@ imagesize==1.2.0
# via sphinx # via sphinx
iniconfig==1.1.1 iniconfig==1.1.1
# via pytest # via pytest
jinja2==3.0.0 jinja2==3.0.1
# via sphinx # via sphinx
markupsafe==2.0.0 markupsafe==2.0.1
# via jinja2 # via jinja2
mypy-extensions==0.4.3 mypy-extensions==0.4.3
# via mypy # via mypy
@ -60,7 +60,7 @@ packaging==20.9
# pytest # pytest
# sphinx # sphinx
# tox # tox
pallets-sphinx-themes==2.0.0 pallets-sphinx-themes==2.0.1
# via -r requirements/docs.in # via -r requirements/docs.in
pep517==0.10.0 pep517==0.10.0
# via pip-tools # via pip-tools
@ -102,7 +102,7 @@ sphinx-issues==1.2.0
# via -r requirements/docs.in # via -r requirements/docs.in
sphinx-tabs==3.0.0 sphinx-tabs==3.0.0
# via -r requirements/docs.in # via -r requirements/docs.in
git+https://github.com/sphinx-doc/sphinx.git@96dbe5e3 sphinx==4.0.2
# via # via
# -r requirements/docs.in # -r requirements/docs.in
# pallets-sphinx-themes # pallets-sphinx-themes

View file

@ -1,5 +1,5 @@
Pallets-Sphinx-Themes Pallets-Sphinx-Themes
git+https://github.com/sphinx-doc/sphinx.git@96dbe5e3 # https://github.com/sphinx-doc/sphinx/issues/921 Sphinx
sphinx-issues sphinx-issues
sphinxcontrib-log-cabinet sphinxcontrib-log-cabinet
sphinx-tabs sphinx-tabs

View file

@ -20,15 +20,15 @@ idna==2.10
# via requests # via requests
imagesize==1.2.0 imagesize==1.2.0
# via sphinx # via sphinx
jinja2==3.0.0 jinja2==3.0.1
# via sphinx # via sphinx
markupsafe==2.0.0 markupsafe==2.0.1
# via jinja2 # via jinja2
packaging==20.9 packaging==20.9
# via # via
# pallets-sphinx-themes # pallets-sphinx-themes
# sphinx # sphinx
pallets-sphinx-themes==2.0.0 pallets-sphinx-themes==2.0.1
# via -r requirements/docs.in # via -r requirements/docs.in
pygments==2.9.0 pygments==2.9.0
# via # via
@ -46,7 +46,7 @@ sphinx-issues==1.2.0
# via -r requirements/docs.in # via -r requirements/docs.in
sphinx-tabs==3.0.0 sphinx-tabs==3.0.0
# via -r requirements/docs.in # via -r requirements/docs.in
git+https://github.com/sphinx-doc/sphinx.git@96dbe5e3 sphinx==4.0.2
# via # via
# -r requirements/docs.in # -r requirements/docs.in
# pallets-sphinx-themes # pallets-sphinx-themes

View file

@ -354,6 +354,8 @@ class Blueprint(Scaffold):
bp_options["url_prefix"] = ( bp_options["url_prefix"] = (
state.url_prefix.rstrip("/") + "/" + bp_url_prefix.lstrip("/") state.url_prefix.rstrip("/") + "/" + bp_url_prefix.lstrip("/")
) )
else:
bp_options["url_prefix"] = state.url_prefix
bp_options["name_prefix"] = options.get("name_prefix", "") + self.name + "." bp_options["name_prefix"] = options.get("name_prefix", "") + self.name + "."
blueprint.register(app, bp_options) blueprint.register(app, bp_options)

View file

@ -202,6 +202,31 @@ class Config(dict):
return self.from_mapping(obj) return self.from_mapping(obj)
def from_json(self, filename: str, silent: bool = False) -> bool:
"""Update the values in the config from a JSON file. The loaded
data is passed to the :meth:`from_mapping` method.
:param filename: The path to the JSON file. This can be an
absolute path or relative to the config root path.
:param silent: Ignore the file if it doesn't exist.
.. deprecated:: 2.0.0
Will be removed in Flask 2.1. Use :meth:`from_file` instead.
This was removed early in 2.0.0, was added back in 2.0.1.
.. versionadded:: 0.11
"""
import warnings
from . import json
warnings.warn(
"'from_json' is deprecated and will be removed in Flask"
" 2.1. Use 'from_file(path, json.load)' instead.",
DeprecationWarning,
stacklevel=2,
)
return self.from_file(filename, json.load, silent=silent)
def from_mapping( def from_mapping(
self, mapping: t.Optional[t.Mapping[str, t.Any]] = None, **kwargs: t.Any self, mapping: t.Optional[t.Mapping[str, t.Any]] = None, **kwargs: t.Any
) -> bool: ) -> bool:

View file

@ -2,8 +2,8 @@ import typing as t
if t.TYPE_CHECKING: if t.TYPE_CHECKING:
from _typeshed.wsgi import WSGIApplication # noqa: F401
from werkzeug.datastructures import Headers # noqa: F401 from werkzeug.datastructures import Headers # noqa: F401
from wsgiref.types import WSGIApplication # noqa: F401
from .wrappers import Response # noqa: F401 from .wrappers import Response # noqa: F401
# The possible types that are directly convertible or are a Response object. # The possible types that are directly convertible or are a Response object.

View file

@ -868,3 +868,17 @@ def test_nested_blueprint_url_prefix(app, client):
assert client.get("/parent/child/").data == b"Child" assert client.get("/parent/child/").data == b"Child"
assert client.get("/parent/child/grandchild/").data == b"Grandchild" assert client.get("/parent/child/grandchild/").data == b"Grandchild"
assert client.get("/parent/child/orange/").data == b"Apple" assert client.get("/parent/child/orange/").data == b"Apple"
def test_nested_blueprint_url_prefix_only_parent_prefix(app, client):
parent = flask.Blueprint("parent", __name__)
child = flask.Blueprint("child", __name__)
@child.route("/child-endpoint")
def child_index():
return "Child"
parent.register_blueprint(child)
app.register_blueprint(parent, url_prefix="/parent")
assert client.get("/parent/child-endpoint").data == b"Child"

View file

@ -5,6 +5,7 @@ import ssl
import sys import sys
import types import types
from functools import partial from functools import partial
from pathlib import Path
from unittest.mock import patch from unittest.mock import patch
import click import click
@ -29,8 +30,8 @@ from flask.cli import run_command
from flask.cli import ScriptInfo from flask.cli import ScriptInfo
from flask.cli import with_appcontext from flask.cli import with_appcontext
cwd = os.getcwd() cwd = Path.cwd()
test_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "test_apps")) test_path = (Path(__file__) / ".." / "test_apps").resolve()
@pytest.fixture @pytest.fixture
@ -152,29 +153,25 @@ def test_find_best_app(test_apps):
( (
("test", cwd, "test"), ("test", cwd, "test"),
("test.py", cwd, "test"), ("test.py", cwd, "test"),
("a/test", os.path.join(cwd, "a"), "test"), ("a/test", cwd / "a", "test"),
("test/__init__.py", cwd, "test"), ("test/__init__.py", cwd, "test"),
("test/__init__", cwd, "test"), ("test/__init__", cwd, "test"),
# nested package # nested package
( (
os.path.join(test_path, "cliapp", "inner1", "__init__"), test_path / "cliapp" / "inner1" / "__init__",
test_path, test_path,
"cliapp.inner1", "cliapp.inner1",
), ),
( (
os.path.join(test_path, "cliapp", "inner1", "inner2"), test_path / "cliapp" / "inner1" / "inner2",
test_path, test_path,
"cliapp.inner1.inner2", "cliapp.inner1.inner2",
), ),
# dotted name # dotted name
("test.a.b", cwd, "test.a.b"), ("test.a.b", cwd, "test.a.b"),
(os.path.join(test_path, "cliapp.app"), test_path, "cliapp.app"), (test_path / "cliapp.app", test_path, "cliapp.app"),
# not a Python file, will be caught during import # not a Python file, will be caught during import
( (test_path / "cliapp" / "message.txt", test_path, "cliapp.message.txt"),
os.path.join(test_path, "cliapp", "message.txt"),
test_path,
"cliapp.message.txt",
),
), ),
) )
def test_prepare_import(request, value, path, result): def test_prepare_import(request, value, path, result):
@ -193,7 +190,7 @@ def test_prepare_import(request, value, path, result):
request.addfinalizer(reset_path) request.addfinalizer(reset_path)
assert prepare_import(value) == result assert prepare_import(value) == result
assert sys.path[0] == path assert sys.path[0] == str(path)
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -278,9 +275,8 @@ def test_scriptinfo(test_apps, monkeypatch):
assert obj.load_app() is app assert obj.load_app() is app
# import app with module's absolute path # import app with module's absolute path
cli_app_path = os.path.abspath( cli_app_path = str(test_path / "cliapp" / "app.py")
os.path.join(os.path.dirname(__file__), "test_apps", "cliapp", "app.py")
)
obj = ScriptInfo(app_import_path=cli_app_path) obj = ScriptInfo(app_import_path=cli_app_path)
app = obj.load_app() app = obj.load_app()
assert app.name == "testapp" assert app.name == "testapp"
@ -302,19 +298,13 @@ def test_scriptinfo(test_apps, monkeypatch):
pytest.raises(NoAppException, obj.load_app) pytest.raises(NoAppException, obj.load_app)
# import app from wsgi.py in current directory # import app from wsgi.py in current directory
monkeypatch.chdir( monkeypatch.chdir(test_path / "helloworld")
os.path.abspath(
os.path.join(os.path.dirname(__file__), "test_apps", "helloworld")
)
)
obj = ScriptInfo() obj = ScriptInfo()
app = obj.load_app() app = obj.load_app()
assert app.name == "hello" assert app.name == "hello"
# import app from app.py in current directory # import app from app.py in current directory
monkeypatch.chdir( monkeypatch.chdir(test_path / "cliapp")
os.path.abspath(os.path.join(os.path.dirname(__file__), "test_apps", "cliapp"))
)
obj = ScriptInfo() obj = ScriptInfo()
app = obj.load_app() app = obj.load_app()
assert app.name == "testapp" assert app.name == "testapp"
@ -513,7 +503,7 @@ def test_load_dotenv(monkeypatch):
monkeypatch.setenv("EGGS", "3") monkeypatch.setenv("EGGS", "3")
monkeypatch.chdir(test_path) monkeypatch.chdir(test_path)
assert load_dotenv() assert load_dotenv()
assert os.getcwd() == test_path assert Path.cwd() == test_path
# .flaskenv doesn't overwrite .env # .flaskenv doesn't overwrite .env
assert os.environ["FOO"] == "env" assert os.environ["FOO"] == "env"
# set only in .flaskenv # set only in .flaskenv
@ -533,9 +523,8 @@ def test_dotenv_path(monkeypatch):
for item in ("FOO", "BAR", "EGGS"): for item in ("FOO", "BAR", "EGGS"):
monkeypatch._setitem.append((os.environ, item, notset)) monkeypatch._setitem.append((os.environ, item, notset))
cwd = os.getcwd() load_dotenv(test_path / ".flaskenv")
load_dotenv(os.path.join(test_path, ".flaskenv")) assert Path.cwd() == cwd
assert os.getcwd() == cwd
assert "FOO" in os.environ assert "FOO" in os.environ