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
URL is again matched after the session is loaded, so the session is
available in custom URL converters. :issue:`4053`
- Re-add deprecated ``Config.from_json``, which was accidentally
removed early. :issue:`4078`
Version 2.0.0

View file

@ -49,10 +49,10 @@ html_context = {
]
}
html_sidebars = {
"index": ["project.html", "localtoc.html", "searchbox.html"],
"**": ["localtoc.html", "relations.html", "searchbox.html"],
"index": ["project.html", "localtoc.html", "searchbox.html", "ethicalads.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_favicon = "_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>')
def show_user_profile(username):
# show the user profile for that user
return f'User {username}'
return f'User {escape(username)}'
@app.route('/post/<int:post_id>')
def show_post(post_id):
@ -256,7 +256,7 @@ of the argument like ``<converter:variable_name>``. ::
@app.route('/path/<path:subpath>')
def show_subpath(subpath):
# show the subpath after /path/
return f'Subpath {subpath}'
return f'Subpath {escape(subpath)}'
Converter types:

View file

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

View file

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

View file

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

View file

@ -354,6 +354,8 @@ class Blueprint(Scaffold):
bp_options["url_prefix"] = (
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 + "."
blueprint.register(app, bp_options)

View file

@ -202,6 +202,31 @@ class Config(dict):
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(
self, mapping: t.Optional[t.Mapping[str, t.Any]] = None, **kwargs: t.Any
) -> bool:

View file

@ -2,8 +2,8 @@ import typing as t
if t.TYPE_CHECKING:
from _typeshed.wsgi import WSGIApplication # noqa: F401
from werkzeug.datastructures import Headers # noqa: F401
from wsgiref.types import WSGIApplication # noqa: F401
from .wrappers import Response # noqa: F401
# 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/grandchild/").data == b"Grandchild"
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 types
from functools import partial
from pathlib import Path
from unittest.mock import patch
import click
@ -29,8 +30,8 @@ from flask.cli import run_command
from flask.cli import ScriptInfo
from flask.cli import with_appcontext
cwd = os.getcwd()
test_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "test_apps"))
cwd = Path.cwd()
test_path = (Path(__file__) / ".." / "test_apps").resolve()
@pytest.fixture
@ -152,29 +153,25 @@ def test_find_best_app(test_apps):
(
("test", 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__", cwd, "test"),
# nested package
(
os.path.join(test_path, "cliapp", "inner1", "__init__"),
test_path / "cliapp" / "inner1" / "__init__",
test_path,
"cliapp.inner1",
),
(
os.path.join(test_path, "cliapp", "inner1", "inner2"),
test_path / "cliapp" / "inner1" / "inner2",
test_path,
"cliapp.inner1.inner2",
),
# dotted name
("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
(
os.path.join(test_path, "cliapp", "message.txt"),
test_path,
"cliapp.message.txt",
),
(test_path / "cliapp" / "message.txt", test_path, "cliapp.message.txt"),
),
)
def test_prepare_import(request, value, path, result):
@ -193,7 +190,7 @@ def test_prepare_import(request, value, path, result):
request.addfinalizer(reset_path)
assert prepare_import(value) == result
assert sys.path[0] == path
assert sys.path[0] == str(path)
@pytest.mark.parametrize(
@ -278,9 +275,8 @@ def test_scriptinfo(test_apps, monkeypatch):
assert obj.load_app() is app
# import app with module's absolute path
cli_app_path = os.path.abspath(
os.path.join(os.path.dirname(__file__), "test_apps", "cliapp", "app.py")
)
cli_app_path = str(test_path / "cliapp" / "app.py")
obj = ScriptInfo(app_import_path=cli_app_path)
app = obj.load_app()
assert app.name == "testapp"
@ -302,19 +298,13 @@ def test_scriptinfo(test_apps, monkeypatch):
pytest.raises(NoAppException, obj.load_app)
# import app from wsgi.py in current directory
monkeypatch.chdir(
os.path.abspath(
os.path.join(os.path.dirname(__file__), "test_apps", "helloworld")
)
)
monkeypatch.chdir(test_path / "helloworld")
obj = ScriptInfo()
app = obj.load_app()
assert app.name == "hello"
# import app from app.py in current directory
monkeypatch.chdir(
os.path.abspath(os.path.join(os.path.dirname(__file__), "test_apps", "cliapp"))
)
monkeypatch.chdir(test_path / "cliapp")
obj = ScriptInfo()
app = obj.load_app()
assert app.name == "testapp"
@ -513,7 +503,7 @@ def test_load_dotenv(monkeypatch):
monkeypatch.setenv("EGGS", "3")
monkeypatch.chdir(test_path)
assert load_dotenv()
assert os.getcwd() == test_path
assert Path.cwd() == test_path
# .flaskenv doesn't overwrite .env
assert os.environ["FOO"] == "env"
# set only in .flaskenv
@ -533,9 +523,8 @@ def test_dotenv_path(monkeypatch):
for item in ("FOO", "BAR", "EGGS"):
monkeypatch._setitem.append((os.environ, item, notset))
cwd = os.getcwd()
load_dotenv(os.path.join(test_path, ".flaskenv"))
assert os.getcwd() == cwd
load_dotenv(test_path / ".flaskenv")
assert Path.cwd() == cwd
assert "FOO" in os.environ