remove simplejson

- remove encoding detection backport, json.loads supports it directly
- use str.translate instead of multiple str.replace
This commit is contained in:
David Lord 2020-04-07 07:00:15 -07:00
parent 024f0d384c
commit c43edfc7c0
No known key found for this signature in database
GPG key ID: 7A1C87E3F5BC42A8
6 changed files with 27 additions and 113 deletions

View file

@ -19,6 +19,9 @@ Unreleased
200 OK and an empty file. :issue:`3358`
- When using ad-hoc certificates, check for the cryptography library
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

View file

@ -284,22 +284,9 @@ JSON Support
.. module:: flask.json
Flask uses ``simplejson`` for the JSON implementation. Since simplejson
is provided by both the standard library as well as extension, Flask will
try simplejson first and then fall back to the stdlib json module. On top
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
Flask uses the built-in :mod:`json` module for the JSON implementation.
It will delegate access to the current application's JSON encoders and
decoders for easier customization.
For usage examples, read the :mod:`json` documentation in the standard
library. The following extensions are by default applied to the stdlib's

View file

@ -39,16 +39,12 @@ These distributions will not be installed automatically. Flask will detect and
use them if you install them.
* `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``
commands.
* `Watchdog`_ provides a faster, more efficient reloader for the development
server.
.. _Blinker: https://pythonhosted.org/blinker/
.. _SimpleJSON: https://simplejson.readthedocs.io/
.. _python-dotenv: https://github.com/theskumar/python-dotenv#readme
.. _watchdog: https://pythonhosted.org/watchdog/

View file

@ -1,10 +1,9 @@
import codecs
import io
import json as _json
import uuid
from datetime import date
from datetime import datetime
from itsdangerous import json as _json
from jinja2 import Markup
from werkzeug.http import http_date
@ -17,10 +16,6 @@ except ImportError:
# Python < 3.7
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__ = [
"dump",
@ -93,7 +88,7 @@ class JSONEncoder(_json.JSONEncoder):
class JSONDecoder(_json.JSONDecoder):
"""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
functions of this module but also :attr:`~flask.Request`.
"""
@ -133,49 +128,6 @@ def _load_arg_defaults(kwargs, app=None):
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):
"""Serialize ``obj`` to a JSON-formatted string. If there is an
app context pushed, use the current app's configured encoder
@ -183,8 +135,7 @@ def dumps(obj, app=None, **kwargs):
:class:`JSONEncoder`.
Takes the same arguments as the built-in :func:`json.dumps`, and
does some extra configuration based on the application. If the
simplejson package is installed, it is preferred.
does some extra configuration based on the application.
:param obj: Object to serialize to JSON.
: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)
encoding = kwargs.pop("encoding", None)
rv = _json.dumps(obj, **kwargs)
if encoding is not None and isinstance(rv, str):
rv = rv.encode(encoding)
return rv
@ -209,8 +162,10 @@ def dump(obj, fp, app=None, **kwargs):
"""Like :func:`dumps` but writes into a file object."""
_dump_arg_defaults(kwargs, app=app)
encoding = kwargs.pop("encoding", None)
if encoding is not None:
fp = _wrap_writer_for_text(fp, encoding)
_json.dump(obj, fp, **kwargs)
@ -221,8 +176,7 @@ def loads(s, app=None, **kwargs):
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.
does some extra configuration based on the application.
:param s: JSON string to deserialize.
:param app: App instance to use to configure the JSON decoder.
@ -236,21 +190,27 @@ def loads(s, app=None, **kwargs):
context for configuration.
"""
_load_arg_defaults(kwargs, app=app)
if isinstance(s, bytes):
encoding = kwargs.pop("encoding", None)
if encoding is None:
encoding = detect_encoding(s)
encoding = kwargs.pop("encoding", None)
if encoding is not None and isinstance(s, bytes):
s = s.decode(encoding)
return _json.loads(s, **kwargs)
def load(fp, app=None, **kwargs):
"""Like :func:`loads` but reads from a file object."""
_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)
_htmlsafe_map = str.maketrans(
{"<": "\\u003c", ">": "\\u003e", "&": "\\u0026", "'": "\\u0027"}
)
def htmlsafe_dumps(obj, **kwargs):
"""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
@ -276,16 +236,7 @@ def htmlsafe_dumps(obj, **kwargs):
quoted. Always single quote attributes if you use the ``|tojson``
filter. Alternatively use ``|tojson|forceescape``.
"""
rv = (
dumps(obj, **kwargs)
.replace("<", "\\u003c")
.replace(">", "\\u003e")
.replace("&", "\\u0026")
.replace("'", "\\u0027")
)
if not _slash_escape:
rv = rv.replace("\\/", "/")
return rv
return dumps(obj, **kwargs).translate(_htmlsafe_map)
def htmlsafe_dump(obj, fp, **kwargs):

View file

@ -13,7 +13,6 @@ from werkzeug.http import parse_cache_control_header
from werkzeug.http import parse_options_header
import flask
from flask import json
from flask.helpers import get_debug_flag
from flask.helpers import get_env
@ -64,26 +63,6 @@ class FixedOffset(datetime.tzinfo):
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))
def test_bad_request_debug_message(self, app, client, debug):
app.config["DEBUG"] = debug

View file

@ -1,7 +1,7 @@
[tox]
envlist =
py{38,37,36,py3}
py38-{simplejson,devel,lowest}
py38-{devel,lowest}
style
docs
skip_missing_interpreters = true
@ -24,8 +24,6 @@ deps =
devel: https://github.com/pallets/itsdangerous/archive/master.tar.gz
devel: https://github.com/pallets/click/archive/master.tar.gz
simplejson: simplejson
commands =
pip install -q -e examples/tutorial[test]
pip install -q -e examples/javascript[test]