forked from orbit-oss/flask
Merge branch '2.0.x'
This commit is contained in:
commit
83f7efa047
11 changed files with 76 additions and 44 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue