forked from orbit-oss/flask
remove simplejson
- remove encoding detection backport, json.loads supports it directly - use str.translate instead of multiple str.replace
This commit is contained in:
parent
024f0d384c
commit
c43edfc7c0
6 changed files with 27 additions and 113 deletions
|
|
@ -19,6 +19,9 @@ Unreleased
|
||||||
200 OK and an empty file. :issue:`3358`
|
200 OK and an empty file. :issue:`3358`
|
||||||
- When using ad-hoc certificates, check for the cryptography library
|
- When using ad-hoc certificates, check for the cryptography library
|
||||||
instead of PyOpenSSL. :pr:`3492`
|
instead of PyOpenSSL. :pr:`3492`
|
||||||
|
- JSON support no longer uses simplejson if it's installed. To use
|
||||||
|
another JSON module, override ``app.json_encoder`` and
|
||||||
|
``json_decoder``. :issue:`3555`
|
||||||
|
|
||||||
|
|
||||||
Version 1.1.2
|
Version 1.1.2
|
||||||
|
|
|
||||||
19
docs/api.rst
19
docs/api.rst
|
|
@ -284,22 +284,9 @@ JSON Support
|
||||||
|
|
||||||
.. module:: flask.json
|
.. module:: flask.json
|
||||||
|
|
||||||
Flask uses ``simplejson`` for the JSON implementation. Since simplejson
|
Flask uses the built-in :mod:`json` module for the JSON implementation.
|
||||||
is provided by both the standard library as well as extension, Flask will
|
It will delegate access to the current application's JSON encoders and
|
||||||
try simplejson first and then fall back to the stdlib json module. On top
|
decoders for easier customization.
|
||||||
of that it will delegate access to the current application's JSON encoders
|
|
||||||
and decoders for easier customization.
|
|
||||||
|
|
||||||
So for starters instead of doing::
|
|
||||||
|
|
||||||
try:
|
|
||||||
import simplejson as json
|
|
||||||
except ImportError:
|
|
||||||
import json
|
|
||||||
|
|
||||||
You can instead just do this::
|
|
||||||
|
|
||||||
from flask import json
|
|
||||||
|
|
||||||
For usage examples, read the :mod:`json` documentation in the standard
|
For usage examples, read the :mod:`json` documentation in the standard
|
||||||
library. The following extensions are by default applied to the stdlib's
|
library. The following extensions are by default applied to the stdlib's
|
||||||
|
|
|
||||||
|
|
@ -39,16 +39,12 @@ These distributions will not be installed automatically. Flask will detect and
|
||||||
use them if you install them.
|
use them if you install them.
|
||||||
|
|
||||||
* `Blinker`_ provides support for :doc:`signals`.
|
* `Blinker`_ provides support for :doc:`signals`.
|
||||||
* `SimpleJSON`_ is a fast JSON implementation that is compatible with
|
|
||||||
Python's ``json`` module. It is preferred for JSON operations if it is
|
|
||||||
installed.
|
|
||||||
* `python-dotenv`_ enables support for :ref:`dotenv` when running ``flask``
|
* `python-dotenv`_ enables support for :ref:`dotenv` when running ``flask``
|
||||||
commands.
|
commands.
|
||||||
* `Watchdog`_ provides a faster, more efficient reloader for the development
|
* `Watchdog`_ provides a faster, more efficient reloader for the development
|
||||||
server.
|
server.
|
||||||
|
|
||||||
.. _Blinker: https://pythonhosted.org/blinker/
|
.. _Blinker: https://pythonhosted.org/blinker/
|
||||||
.. _SimpleJSON: https://simplejson.readthedocs.io/
|
|
||||||
.. _python-dotenv: https://github.com/theskumar/python-dotenv#readme
|
.. _python-dotenv: https://github.com/theskumar/python-dotenv#readme
|
||||||
.. _watchdog: https://pythonhosted.org/watchdog/
|
.. _watchdog: https://pythonhosted.org/watchdog/
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
import codecs
|
|
||||||
import io
|
import io
|
||||||
|
import json as _json
|
||||||
import uuid
|
import uuid
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from itsdangerous import json as _json
|
|
||||||
from jinja2 import Markup
|
from jinja2 import Markup
|
||||||
from werkzeug.http import http_date
|
from werkzeug.http import http_date
|
||||||
|
|
||||||
|
|
@ -17,10 +16,6 @@ except ImportError:
|
||||||
# Python < 3.7
|
# Python < 3.7
|
||||||
dataclasses = None
|
dataclasses = None
|
||||||
|
|
||||||
# Figure out if simplejson escapes slashes. This behavior was changed
|
|
||||||
# from one version to another without reason.
|
|
||||||
_slash_escape = "\\/" not in _json.dumps("/")
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"dump",
|
"dump",
|
||||||
|
|
@ -93,7 +88,7 @@ class JSONEncoder(_json.JSONEncoder):
|
||||||
|
|
||||||
class JSONDecoder(_json.JSONDecoder):
|
class JSONDecoder(_json.JSONDecoder):
|
||||||
"""The default JSON decoder. This one does not change the behavior from
|
"""The default JSON decoder. This one does not change the behavior from
|
||||||
the default simplejson decoder. Consult the :mod:`json` documentation
|
the default decoder. Consult the :mod:`json` documentation
|
||||||
for more information. This decoder is not only used for the load
|
for more information. This decoder is not only used for the load
|
||||||
functions of this module but also :attr:`~flask.Request`.
|
functions of this module but also :attr:`~flask.Request`.
|
||||||
"""
|
"""
|
||||||
|
|
@ -133,49 +128,6 @@ def _load_arg_defaults(kwargs, app=None):
|
||||||
kwargs.setdefault("cls", JSONDecoder)
|
kwargs.setdefault("cls", JSONDecoder)
|
||||||
|
|
||||||
|
|
||||||
def detect_encoding(data):
|
|
||||||
"""Detect which UTF codec was used to encode the given bytes.
|
|
||||||
|
|
||||||
The latest JSON standard (:rfc:`8259`) suggests that only UTF-8 is
|
|
||||||
accepted. Older documents allowed 8, 16, or 32. 16 and 32 can be big
|
|
||||||
or little endian. Some editors or libraries may prepend a BOM.
|
|
||||||
|
|
||||||
:param data: Bytes in unknown UTF encoding.
|
|
||||||
:return: UTF encoding name
|
|
||||||
"""
|
|
||||||
head = data[:4]
|
|
||||||
|
|
||||||
if head[:3] == codecs.BOM_UTF8:
|
|
||||||
return "utf-8-sig"
|
|
||||||
|
|
||||||
if b"\x00" not in head:
|
|
||||||
return "utf-8"
|
|
||||||
|
|
||||||
if head in (codecs.BOM_UTF32_BE, codecs.BOM_UTF32_LE):
|
|
||||||
return "utf-32"
|
|
||||||
|
|
||||||
if head[:2] in (codecs.BOM_UTF16_BE, codecs.BOM_UTF16_LE):
|
|
||||||
return "utf-16"
|
|
||||||
|
|
||||||
if len(head) == 4:
|
|
||||||
if head[:3] == b"\x00\x00\x00":
|
|
||||||
return "utf-32-be"
|
|
||||||
|
|
||||||
if head[::2] == b"\x00\x00":
|
|
||||||
return "utf-16-be"
|
|
||||||
|
|
||||||
if head[1:] == b"\x00\x00\x00":
|
|
||||||
return "utf-32-le"
|
|
||||||
|
|
||||||
if head[1::2] == b"\x00\x00":
|
|
||||||
return "utf-16-le"
|
|
||||||
|
|
||||||
if len(head) == 2:
|
|
||||||
return "utf-16-be" if head.startswith(b"\x00") else "utf-16-le"
|
|
||||||
|
|
||||||
return "utf-8"
|
|
||||||
|
|
||||||
|
|
||||||
def dumps(obj, app=None, **kwargs):
|
def dumps(obj, app=None, **kwargs):
|
||||||
"""Serialize ``obj`` to a JSON-formatted string. If there is an
|
"""Serialize ``obj`` to a JSON-formatted string. If there is an
|
||||||
app context pushed, use the current app's configured encoder
|
app context pushed, use the current app's configured encoder
|
||||||
|
|
@ -183,8 +135,7 @@ def dumps(obj, app=None, **kwargs):
|
||||||
:class:`JSONEncoder`.
|
:class:`JSONEncoder`.
|
||||||
|
|
||||||
Takes the same arguments as the built-in :func:`json.dumps`, and
|
Takes the same arguments as the built-in :func:`json.dumps`, and
|
||||||
does some extra configuration based on the application. If the
|
does some extra configuration based on the application.
|
||||||
simplejson package is installed, it is preferred.
|
|
||||||
|
|
||||||
:param obj: Object to serialize to JSON.
|
:param obj: Object to serialize to JSON.
|
||||||
:param app: App instance to use to configure the JSON encoder.
|
:param app: App instance to use to configure the JSON encoder.
|
||||||
|
|
@ -200,8 +151,10 @@ def dumps(obj, app=None, **kwargs):
|
||||||
_dump_arg_defaults(kwargs, app=app)
|
_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, str):
|
if encoding is not None and isinstance(rv, str):
|
||||||
rv = rv.encode(encoding)
|
rv = rv.encode(encoding)
|
||||||
|
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -209,8 +162,10 @@ 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, app=app)
|
_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)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -221,8 +176,7 @@ def loads(s, app=None, **kwargs):
|
||||||
default :class:`JSONDecoder`.
|
default :class:`JSONDecoder`.
|
||||||
|
|
||||||
Takes the same arguments as the built-in :func:`json.loads`, and
|
Takes the same arguments as the built-in :func:`json.loads`, and
|
||||||
does some extra configuration based on the application. If the
|
does some extra configuration based on the application.
|
||||||
simplejson package is installed, it is preferred.
|
|
||||||
|
|
||||||
:param s: JSON string to deserialize.
|
:param s: JSON string to deserialize.
|
||||||
:param app: App instance to use to configure the JSON decoder.
|
:param app: App instance to use to configure the JSON decoder.
|
||||||
|
|
@ -236,21 +190,27 @@ def loads(s, app=None, **kwargs):
|
||||||
context for configuration.
|
context for configuration.
|
||||||
"""
|
"""
|
||||||
_load_arg_defaults(kwargs, app=app)
|
_load_arg_defaults(kwargs, app=app)
|
||||||
if isinstance(s, bytes):
|
encoding = kwargs.pop("encoding", None)
|
||||||
encoding = kwargs.pop("encoding", None)
|
|
||||||
if encoding is None:
|
if encoding is not None and isinstance(s, bytes):
|
||||||
encoding = detect_encoding(s)
|
|
||||||
s = s.decode(encoding)
|
s = s.decode(encoding)
|
||||||
|
|
||||||
return _json.loads(s, **kwargs)
|
return _json.loads(s, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def load(fp, app=None, **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, app=app)
|
||||||
fp = _wrap_reader_for_text(fp, kwargs.pop("encoding", None) or "utf-8")
|
encoding = kwargs.pop("encoding", None)
|
||||||
|
fp = _wrap_reader_for_text(fp, encoding or "utf-8")
|
||||||
return _json.load(fp, **kwargs)
|
return _json.load(fp, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
_htmlsafe_map = str.maketrans(
|
||||||
|
{"<": "\\u003c", ">": "\\u003e", "&": "\\u0026", "'": "\\u0027"}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def htmlsafe_dumps(obj, **kwargs):
|
def htmlsafe_dumps(obj, **kwargs):
|
||||||
"""Works exactly like :func:`dumps` but is safe for use in ``<script>``
|
"""Works exactly like :func:`dumps` but is safe for use in ``<script>``
|
||||||
tags. It accepts the same arguments and returns a JSON string. Note that
|
tags. It accepts the same arguments and returns a JSON string. Note that
|
||||||
|
|
@ -276,16 +236,7 @@ def htmlsafe_dumps(obj, **kwargs):
|
||||||
quoted. Always single quote attributes if you use the ``|tojson``
|
quoted. Always single quote attributes if you use the ``|tojson``
|
||||||
filter. Alternatively use ``|tojson|forceescape``.
|
filter. Alternatively use ``|tojson|forceescape``.
|
||||||
"""
|
"""
|
||||||
rv = (
|
return dumps(obj, **kwargs).translate(_htmlsafe_map)
|
||||||
dumps(obj, **kwargs)
|
|
||||||
.replace("<", "\\u003c")
|
|
||||||
.replace(">", "\\u003e")
|
|
||||||
.replace("&", "\\u0026")
|
|
||||||
.replace("'", "\\u0027")
|
|
||||||
)
|
|
||||||
if not _slash_escape:
|
|
||||||
rv = rv.replace("\\/", "/")
|
|
||||||
return rv
|
|
||||||
|
|
||||||
|
|
||||||
def htmlsafe_dump(obj, fp, **kwargs):
|
def htmlsafe_dump(obj, fp, **kwargs):
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ from werkzeug.http import parse_cache_control_header
|
||||||
from werkzeug.http import parse_options_header
|
from werkzeug.http import parse_options_header
|
||||||
|
|
||||||
import flask
|
import flask
|
||||||
from flask import json
|
|
||||||
from flask.helpers import get_debug_flag
|
from flask.helpers import get_debug_flag
|
||||||
from flask.helpers import get_env
|
from flask.helpers import get_env
|
||||||
|
|
||||||
|
|
@ -64,26 +63,6 @@ class FixedOffset(datetime.tzinfo):
|
||||||
|
|
||||||
|
|
||||||
class TestJSON:
|
class TestJSON:
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"value", (1, "t", True, False, None, [], [1, 2, 3], {}, {"foo": "🐍"})
|
|
||||||
)
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"encoding",
|
|
||||||
(
|
|
||||||
"utf-8",
|
|
||||||
"utf-8-sig",
|
|
||||||
"utf-16-le",
|
|
||||||
"utf-16-be",
|
|
||||||
"utf-16",
|
|
||||||
"utf-32-le",
|
|
||||||
"utf-32-be",
|
|
||||||
"utf-32",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
def test_detect_encoding(self, value, encoding):
|
|
||||||
data = json.dumps(value).encode(encoding)
|
|
||||||
assert json.loads(data) == value
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("debug", (True, False))
|
@pytest.mark.parametrize("debug", (True, False))
|
||||||
def test_bad_request_debug_message(self, app, client, debug):
|
def test_bad_request_debug_message(self, app, client, debug):
|
||||||
app.config["DEBUG"] = debug
|
app.config["DEBUG"] = debug
|
||||||
|
|
|
||||||
4
tox.ini
4
tox.ini
|
|
@ -1,7 +1,7 @@
|
||||||
[tox]
|
[tox]
|
||||||
envlist =
|
envlist =
|
||||||
py{38,37,36,py3}
|
py{38,37,36,py3}
|
||||||
py38-{simplejson,devel,lowest}
|
py38-{devel,lowest}
|
||||||
style
|
style
|
||||||
docs
|
docs
|
||||||
skip_missing_interpreters = true
|
skip_missing_interpreters = true
|
||||||
|
|
@ -24,8 +24,6 @@ deps =
|
||||||
devel: https://github.com/pallets/itsdangerous/archive/master.tar.gz
|
devel: https://github.com/pallets/itsdangerous/archive/master.tar.gz
|
||||||
devel: https://github.com/pallets/click/archive/master.tar.gz
|
devel: https://github.com/pallets/click/archive/master.tar.gz
|
||||||
|
|
||||||
simplejson: simplejson
|
|
||||||
|
|
||||||
commands =
|
commands =
|
||||||
pip install -q -e examples/tutorial[test]
|
pip install -q -e examples/tutorial[test]
|
||||||
pip install -q -e examples/javascript[test]
|
pip install -q -e examples/javascript[test]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue