mirror of https://github.com/pallets/flask.git
Merge branch '1.0.x'
This commit is contained in:
commit
05a4e15ee4
11
CHANGES.rst
11
CHANGES.rst
|
@ -30,7 +30,7 @@ Unreleased
|
||||||
Version 1.0.3
|
Version 1.0.3
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
Unreleased
|
Released 2019-05-17
|
||||||
|
|
||||||
- :func:`send_file` encodes filenames as ASCII instead of Latin-1
|
- :func:`send_file` encodes filenames as ASCII instead of Latin-1
|
||||||
(ISO-8859-1). This fixes compatibility with Gunicorn, which is
|
(ISO-8859-1). This fixes compatibility with Gunicorn, which is
|
||||||
|
@ -46,10 +46,13 @@ Unreleased
|
||||||
handle ``RoutingException``, which is used internally during
|
handle ``RoutingException``, which is used internally during
|
||||||
routing. This fixes the unexpected behavior that had been introduced
|
routing. This fixes the unexpected behavior that had been introduced
|
||||||
in 1.0. (`#2986`_)
|
in 1.0. (`#2986`_)
|
||||||
|
- Passing the ``json`` argument to ``app.test_client`` does not
|
||||||
|
push/pop an extra app context. (`#2900`_)
|
||||||
|
|
||||||
.. _#2766: https://github.com/pallets/flask/issues/2766
|
.. _#2766: https://github.com/pallets/flask/issues/2766
|
||||||
.. _#2765: https://github.com/pallets/flask/pull/2765
|
.. _#2765: https://github.com/pallets/flask/pull/2765
|
||||||
.. _#2825: https://github.com/pallets/flask/pull/2825
|
.. _#2825: https://github.com/pallets/flask/pull/2825
|
||||||
|
.. _#2900: https://github.com/pallets/flask/issues/2900
|
||||||
.. _#2933: https://github.com/pallets/flask/issues/2933
|
.. _#2933: https://github.com/pallets/flask/issues/2933
|
||||||
.. _#2986: https://github.com/pallets/flask/pull/2986
|
.. _#2986: https://github.com/pallets/flask/pull/2986
|
||||||
|
|
||||||
|
@ -57,7 +60,7 @@ Unreleased
|
||||||
Version 1.0.2
|
Version 1.0.2
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
Released on May 2nd 2018
|
Released 2018-05-02
|
||||||
|
|
||||||
- Fix more backwards compatibility issues with merging slashes between
|
- Fix more backwards compatibility issues with merging slashes between
|
||||||
a blueprint prefix and route. (`#2748`_)
|
a blueprint prefix and route. (`#2748`_)
|
||||||
|
@ -71,7 +74,7 @@ Released on May 2nd 2018
|
||||||
Version 1.0.1
|
Version 1.0.1
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
Released on April 29th 2018
|
Released 2018-04-29
|
||||||
|
|
||||||
- Fix registering partials (with no ``__name__``) as view functions.
|
- Fix registering partials (with no ``__name__``) as view functions.
|
||||||
(`#2730`_)
|
(`#2730`_)
|
||||||
|
@ -97,7 +100,7 @@ Released on April 29th 2018
|
||||||
Version 1.0
|
Version 1.0
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
Released on April 26th 2018
|
Released 2018-04-26
|
||||||
|
|
||||||
- **Python 2.6 and 3.3 are no longer supported.** (`pallets/meta#24`_)
|
- **Python 2.6 and 3.3 are no longer supported.** (`pallets/meta#24`_)
|
||||||
- Bump minimum dependency versions to the latest stable versions:
|
- Bump minimum dependency versions to the latest stable versions:
|
||||||
|
|
|
@ -357,14 +357,15 @@ This however does not make it possible to also modify the session or to
|
||||||
access the session before a request was fired. Starting with Flask 0.8 we
|
access the session before a request was fired. Starting with Flask 0.8 we
|
||||||
provide a so called “session transaction” which simulates the appropriate
|
provide a so called “session transaction” which simulates the appropriate
|
||||||
calls to open a session in the context of the test client and to modify
|
calls to open a session in the context of the test client and to modify
|
||||||
it. At the end of the transaction the session is stored. This works
|
it. At the end of the transaction the session is stored and ready to be
|
||||||
independently of the session backend used::
|
used by the test client. This works independently of the session backend used::
|
||||||
|
|
||||||
with app.test_client() as c:
|
with app.test_client() as c:
|
||||||
with c.session_transaction() as sess:
|
with c.session_transaction() as sess:
|
||||||
sess['a_key'] = 'a value'
|
sess['a_key'] = 'a value'
|
||||||
|
|
||||||
# once this is reached the session was stored
|
# once this is reached the session was stored and ready to be used by the client
|
||||||
|
c.get(...)
|
||||||
|
|
||||||
Note that in this case you have to use the ``sess`` object instead of the
|
Note that in this case you have to use the ``sess`` object instead of the
|
||||||
:data:`flask.session` proxy. The object however itself will provide the
|
:data:`flask.session` proxy. The object however itself will provide the
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
:license: BSD, see LICENSE for more details.
|
:license: BSD, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
from functools import update_wrapper
|
from functools import update_wrapper
|
||||||
from werkzeug.urls import url_join
|
|
||||||
|
|
||||||
from .helpers import _PackageBoundObject, _endpoint_from_view_func
|
from .helpers import _PackageBoundObject, _endpoint_from_view_func
|
||||||
|
|
||||||
|
|
|
@ -650,11 +650,9 @@ def show_server_banner(env, debug, app_import_path, eager_loading):
|
||||||
|
|
||||||
if env == "production":
|
if env == "production":
|
||||||
click.secho(
|
click.secho(
|
||||||
" WARNING: Do not use the development server in a production"
|
' WARNING: This is a development server. '
|
||||||
" environment.",
|
'Do not use it in a production deployment.', fg='red')
|
||||||
fg="red",
|
click.secho(' Use a production WSGI server instead.', dim=True)
|
||||||
)
|
|
||||||
click.secho(" Use a production WSGI server instead.", dim=True)
|
|
||||||
|
|
||||||
if debug is not None:
|
if debug is not None:
|
||||||
click.echo(" * Debug mode: {0}".format("on" if debug else "off"))
|
click.echo(" * Debug mode: {0}".format("on" if debug else "off"))
|
||||||
|
|
|
@ -97,31 +97,35 @@ class JSONDecoder(_json.JSONDecoder):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def _dump_arg_defaults(kwargs):
|
def _dump_arg_defaults(kwargs, app=None):
|
||||||
"""Inject default arguments for dump functions."""
|
"""Inject default arguments for dump functions."""
|
||||||
if current_app:
|
if app is None:
|
||||||
bp = current_app.blueprints.get(request.blueprint) if request else None
|
app = current_app
|
||||||
|
|
||||||
|
if app:
|
||||||
|
bp = app.blueprints.get(request.blueprint) if request else None
|
||||||
kwargs.setdefault(
|
kwargs.setdefault(
|
||||||
"cls",
|
"cls", bp.json_encoder if bp and bp.json_encoder else app.json_encoder
|
||||||
bp.json_encoder if bp and bp.json_encoder else current_app.json_encoder,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if not current_app.config["JSON_AS_ASCII"]:
|
if not app.config["JSON_AS_ASCII"]:
|
||||||
kwargs.setdefault("ensure_ascii", False)
|
kwargs.setdefault("ensure_ascii", False)
|
||||||
|
|
||||||
kwargs.setdefault("sort_keys", current_app.config["JSON_SORT_KEYS"])
|
kwargs.setdefault("sort_keys", app.config["JSON_SORT_KEYS"])
|
||||||
else:
|
else:
|
||||||
kwargs.setdefault("sort_keys", True)
|
kwargs.setdefault("sort_keys", True)
|
||||||
kwargs.setdefault("cls", JSONEncoder)
|
kwargs.setdefault("cls", JSONEncoder)
|
||||||
|
|
||||||
|
|
||||||
def _load_arg_defaults(kwargs):
|
def _load_arg_defaults(kwargs, app=None):
|
||||||
"""Inject default arguments for load functions."""
|
"""Inject default arguments for load functions."""
|
||||||
if current_app:
|
if app is None:
|
||||||
bp = current_app.blueprints.get(request.blueprint) if request else None
|
app = current_app
|
||||||
|
|
||||||
|
if app:
|
||||||
|
bp = app.blueprints.get(request.blueprint) if request else None
|
||||||
kwargs.setdefault(
|
kwargs.setdefault(
|
||||||
"cls",
|
"cls", bp.json_decoder if bp and bp.json_decoder else app.json_decoder
|
||||||
bp.json_decoder if bp and bp.json_decoder else current_app.json_decoder,
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
kwargs.setdefault("cls", JSONDecoder)
|
kwargs.setdefault("cls", JSONDecoder)
|
||||||
|
@ -170,17 +174,28 @@ def detect_encoding(data):
|
||||||
return "utf-8"
|
return "utf-8"
|
||||||
|
|
||||||
|
|
||||||
def dumps(obj, **kwargs):
|
def dumps(obj, app=None, **kwargs):
|
||||||
"""Serialize ``obj`` to a JSON formatted ``str`` by using the application's
|
"""Serialize ``obj`` to a JSON-formatted string. If there is an
|
||||||
configured encoder (:attr:`~flask.Flask.json_encoder`) if there is an
|
app context pushed, use the current app's configured encoder
|
||||||
application on the stack.
|
(:attr:`~flask.Flask.json_encoder`), or fall back to the default
|
||||||
|
:class:`JSONEncoder`.
|
||||||
|
|
||||||
This function can return ``unicode`` strings or ascii-only bytestrings by
|
Takes the same arguments as the built-in :func:`json.dumps`, and
|
||||||
default which coerce into unicode strings automatically. That behavior by
|
does some extra configuration based on the application. If the
|
||||||
default is controlled by the ``JSON_AS_ASCII`` configuration variable
|
simplejson package is installed, it is preferred.
|
||||||
and can be overridden by the simplejson ``ensure_ascii`` parameter.
|
|
||||||
|
:param obj: Object to serialize to JSON.
|
||||||
|
:param app: App instance to use to configure the JSON encoder.
|
||||||
|
Uses ``current_app`` if not given, and falls back to the default
|
||||||
|
encoder when not in an app context.
|
||||||
|
:param kwargs: Extra arguments passed to :func:`json.dumps`.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.0.3
|
||||||
|
|
||||||
|
``app`` can be passed directly, rather than requiring an app
|
||||||
|
context for configuration.
|
||||||
"""
|
"""
|
||||||
_dump_arg_defaults(kwargs)
|
_dump_arg_defaults(kwargs, app=app)
|
||||||
encoding = kwargs.pop("encoding", None)
|
encoding = kwargs.pop("encoding", None)
|
||||||
rv = _json.dumps(obj, **kwargs)
|
rv = _json.dumps(obj, **kwargs)
|
||||||
if encoding is not None and isinstance(rv, text_type):
|
if encoding is not None and isinstance(rv, text_type):
|
||||||
|
@ -188,21 +203,37 @@ def dumps(obj, **kwargs):
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
|
|
||||||
def dump(obj, fp, **kwargs):
|
def dump(obj, fp, app=None, **kwargs):
|
||||||
"""Like :func:`dumps` but writes into a file object."""
|
"""Like :func:`dumps` but writes into a file object."""
|
||||||
_dump_arg_defaults(kwargs)
|
_dump_arg_defaults(kwargs, app=app)
|
||||||
encoding = kwargs.pop("encoding", None)
|
encoding = kwargs.pop("encoding", None)
|
||||||
if encoding is not None:
|
if encoding is not None:
|
||||||
fp = _wrap_writer_for_text(fp, encoding)
|
fp = _wrap_writer_for_text(fp, encoding)
|
||||||
_json.dump(obj, fp, **kwargs)
|
_json.dump(obj, fp, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def loads(s, **kwargs):
|
def loads(s, app=None, **kwargs):
|
||||||
"""Unserialize a JSON object from a string ``s`` by using the application's
|
"""Deserialize an object from a JSON-formatted string ``s``. If
|
||||||
configured decoder (:attr:`~flask.Flask.json_decoder`) if there is an
|
there is an app context pushed, use the current app's configured
|
||||||
application on the stack.
|
decoder (:attr:`~flask.Flask.json_decoder`), or fall back to the
|
||||||
|
default :class:`JSONDecoder`.
|
||||||
|
|
||||||
|
Takes the same arguments as the built-in :func:`json.loads`, and
|
||||||
|
does some extra configuration based on the application. If the
|
||||||
|
simplejson package is installed, it is preferred.
|
||||||
|
|
||||||
|
:param s: JSON string to deserialize.
|
||||||
|
:param app: App instance to use to configure the JSON decoder.
|
||||||
|
Uses ``current_app`` if not given, and falls back to the default
|
||||||
|
encoder when not in an app context.
|
||||||
|
:param kwargs: Extra arguments passed to :func:`json.dumps`.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.0.3
|
||||||
|
|
||||||
|
``app`` can be passed directly, rather than requiring an app
|
||||||
|
context for configuration.
|
||||||
"""
|
"""
|
||||||
_load_arg_defaults(kwargs)
|
_load_arg_defaults(kwargs, app=app)
|
||||||
if isinstance(s, bytes):
|
if isinstance(s, bytes):
|
||||||
encoding = kwargs.pop("encoding", None)
|
encoding = kwargs.pop("encoding", None)
|
||||||
if encoding is None:
|
if encoding is None:
|
||||||
|
@ -211,10 +242,9 @@ def loads(s, **kwargs):
|
||||||
return _json.loads(s, **kwargs)
|
return _json.loads(s, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def load(fp, **kwargs):
|
def load(fp, app=None, **kwargs):
|
||||||
"""Like :func:`loads` but reads from a file object.
|
"""Like :func:`loads` but reads from a file object."""
|
||||||
"""
|
_load_arg_defaults(kwargs, app=app)
|
||||||
_load_arg_defaults(kwargs)
|
|
||||||
if not PY2:
|
if not PY2:
|
||||||
fp = _wrap_reader_for_text(fp, kwargs.pop("encoding", None) or "utf-8")
|
fp = _wrap_reader_for_text(fp, kwargs.pop("encoding", None) or "utf-8")
|
||||||
return _json.load(fp, **kwargs)
|
return _json.load(fp, **kwargs)
|
||||||
|
|
|
@ -71,12 +71,10 @@ def make_test_environ_builder(
|
||||||
sep = b"?" if isinstance(url.query, bytes) else "?"
|
sep = b"?" if isinstance(url.query, bytes) else "?"
|
||||||
path += sep + url.query
|
path += sep + url.query
|
||||||
|
|
||||||
|
# TODO use EnvironBuilder.json_dumps once we require Werkzeug 0.15
|
||||||
if "json" in kwargs:
|
if "json" in kwargs:
|
||||||
assert "data" not in kwargs, "Client cannot provide both 'json' and 'data'."
|
assert "data" not in kwargs, "Client cannot provide both 'json' and 'data'."
|
||||||
|
kwargs["data"] = json_dumps(kwargs.pop("json"), app=app)
|
||||||
# push a context so flask.json can use app's json attributes
|
|
||||||
with app.app_context():
|
|
||||||
kwargs["data"] = json_dumps(kwargs.pop("json"))
|
|
||||||
|
|
||||||
if "content_type" not in kwargs:
|
if "content_type" not in kwargs:
|
||||||
kwargs["content_type"] = "application/json"
|
kwargs["content_type"] = "application/json"
|
||||||
|
|
|
@ -14,11 +14,17 @@ import pytest
|
||||||
import flask
|
import flask
|
||||||
import werkzeug
|
import werkzeug
|
||||||
|
|
||||||
|
from flask import appcontext_popped
|
||||||
from flask._compat import text_type
|
from flask._compat import text_type
|
||||||
from flask.cli import ScriptInfo
|
from flask.cli import ScriptInfo
|
||||||
from flask.json import jsonify
|
from flask.json import jsonify
|
||||||
from flask.testing import make_test_environ_builder, FlaskCliRunner
|
from flask.testing import make_test_environ_builder, FlaskCliRunner
|
||||||
|
|
||||||
|
try:
|
||||||
|
import blinker
|
||||||
|
except ImportError:
|
||||||
|
blinker = None
|
||||||
|
|
||||||
|
|
||||||
def test_environ_defaults_from_config(app, client):
|
def test_environ_defaults_from_config(app, client):
|
||||||
app.config["SERVER_NAME"] = "example.com:1234"
|
app.config["SERVER_NAME"] = "example.com:1234"
|
||||||
|
@ -306,6 +312,27 @@ def test_json_request_and_response(app, client):
|
||||||
assert rv.get_json() == json_data
|
assert rv.get_json() == json_data
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(blinker is None, reason="blinker is not installed")
|
||||||
|
def test_client_json_no_app_context(app, client):
|
||||||
|
@app.route("/hello", methods=["POST"])
|
||||||
|
def hello():
|
||||||
|
return "Hello, {}!".format(flask.request.json["name"])
|
||||||
|
|
||||||
|
class Namespace(object):
|
||||||
|
count = 0
|
||||||
|
|
||||||
|
def add(self, app):
|
||||||
|
self.count += 1
|
||||||
|
|
||||||
|
ns = Namespace()
|
||||||
|
|
||||||
|
with appcontext_popped.connected_to(ns.add, app):
|
||||||
|
rv = client.post("/hello", json={"name": "Flask"})
|
||||||
|
|
||||||
|
assert rv.get_data(as_text=True) == "Hello, Flask!"
|
||||||
|
assert ns.count == 1
|
||||||
|
|
||||||
|
|
||||||
def test_subdomain():
|
def test_subdomain():
|
||||||
app = flask.Flask(__name__, subdomain_matching=True)
|
app = flask.Flask(__name__, subdomain_matching=True)
|
||||||
app.config["SERVER_NAME"] = "example.com"
|
app.config["SERVER_NAME"] = "example.com"
|
||||||
|
|
Loading…
Reference in New Issue