Merge pull request #3562 from pallets/remove-simplejson

Remove simplejson and deprecate encoding options
This commit is contained in:
David Lord 2020-04-07 13:40:39 -07:00 committed by GitHub
commit e69b49bd3d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 210 additions and 284 deletions

View file

@ -6,6 +6,9 @@ Version 2.0.0
Unreleased Unreleased
- Drop support for Python 2 and 3.5. - Drop support for Python 2 and 3.5.
- JSON support no longer uses simplejson. To use another JSON module,
override ``app.json_encoder`` and ``json_decoder``. :issue:`3555`
- The ``encoding`` option to JSON functions is deprecated. :pr:`3562`
- Add :meth:`sessions.SessionInterface.get_cookie_name` to allow - Add :meth:`sessions.SessionInterface.get_cookie_name` to allow
setting the session cookie name dynamically. :pr:`3369` setting the session cookie name dynamically. :pr:`3369`
- Add :meth:`Config.from_file` to load config using arbitrary file - Add :meth:`Config.from_file` to load config using arbitrary file

View file

@ -279,58 +279,34 @@ Message Flashing
.. autofunction:: get_flashed_messages .. autofunction:: get_flashed_messages
JSON Support 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 handling JSON. It will
is provided by both the standard library as well as extension, Flask will use the current blueprint's or application's JSON encoder and decoder
try simplejson first and then fall back to the stdlib json module. On top for easier customization. By default it handles some extra data types:
of that it will delegate access to the current application's JSON encoders
and decoders for easier customization.
So for starters instead of doing:: - :class:`datetime.datetime` and :class:`datetime.date` are serialized
to :rfc:`822` strings. This is the same as the HTTP date format.
- :class:`uuid.UUID` is serialized to a string.
- :class:`dataclasses.dataclass` is passed to
:func:`dataclasses.asdict`.
- :class:`~markupsafe.Markup` (or any object with a ``__html__``
method) will call the ``__html__`` method to get a string.
try: :func:`~htmlsafe_dumps` is also available as the ``|tojson`` template
import simplejson as json filter. The filter marks the output with ``|safe`` so it can be used
except ImportError: inside ``script`` tags.
import json
You can instead just do this::
from flask import json
For usage examples, read the :mod:`json` documentation in the standard
library. The following extensions are by default applied to the stdlib's
JSON module:
1. ``datetime`` objects are serialized as :rfc:`822` strings.
2. Any object with an ``__html__`` method (like :class:`~flask.Markup`)
will have that method called and then the return value is serialized
as string.
The :func:`~htmlsafe_dumps` function of this json module is also available
as a filter called ``|tojson`` in Jinja2. Note that in versions of Flask prior
to Flask 0.10, you must disable escaping with ``|safe`` if you intend to use
``|tojson`` output inside ``script`` tags. In Flask 0.10 and above, this
happens automatically (but it's harmless to include ``|safe`` anyway).
.. sourcecode:: html+jinja .. sourcecode:: html+jinja
<script type=text/javascript> <script type=text/javascript>
doSomethingWith({{ user.username|tojson|safe }}); renderChart({{ axis_data|tojson }});
</script> </script>
.. admonition:: Auto-Sort JSON Keys
The configuration variable :data:`JSON_SORT_KEYS` can be set to
``False`` to stop Flask from auto-sorting keys. By default sorting
is enabled and outside of the app context sorting is turned on.
Notice that disabling key sorting can cause issues when using
content based HTTP caches and Python's hash randomization feature.
.. autofunction:: jsonify .. autofunction:: jsonify
.. autofunction:: dumps .. autofunction:: dumps
@ -349,6 +325,7 @@ happens automatically (but it's harmless to include ``|safe`` anyway).
.. automodule:: flask.json.tag .. automodule:: flask.json.tag
Template Rendering Template Rendering
------------------ ------------------

View file

@ -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/

View file

@ -1,11 +1,11 @@
import codecs
import io import io
import json as _json
import uuid import uuid
import warnings
from datetime import date from datetime import date
from datetime import datetime from datetime import datetime
from itsdangerous import json as _json from markupsafe import Markup
from jinja2 import Markup
from werkzeug.http import http_date from werkzeug.http import http_date
from ..globals import current_app from ..globals import current_app
@ -17,66 +17,29 @@ 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__ = [
"dump",
"dumps",
"load",
"loads",
"htmlsafe_dump",
"htmlsafe_dumps",
"JSONDecoder",
"JSONEncoder",
"jsonify",
]
def _wrap_reader_for_text(fp, encoding):
if isinstance(fp.read(0), bytes):
fp = io.TextIOWrapper(io.BufferedReader(fp), encoding)
return fp
def _wrap_writer_for_text(fp, encoding):
try:
fp.write("")
except TypeError:
fp = io.TextIOWrapper(fp, encoding)
return fp
class JSONEncoder(_json.JSONEncoder): class JSONEncoder(_json.JSONEncoder):
"""The default Flask JSON encoder. This one extends the default """The default JSON encoder. Handles extra types compared to the
encoder by also supporting ``datetime``, ``UUID``, ``dataclasses``, built-in :class:`json.JSONEncoder`.
and ``Markup`` objects.
``datetime`` objects are serialized as RFC 822 datetime strings. - :class:`datetime.datetime` and :class:`datetime.date` are
This is the same as the HTTP date format. serialized to :rfc:`822` strings. This is the same as the HTTP
date format.
- :class:`uuid.UUID` is serialized to a string.
- :class:`dataclasses.dataclass` is passed to
:func:`dataclasses.asdict`.
- :class:`~markupsafe.Markup` (or any object with a ``__html__``
method) will call the ``__html__`` method to get a string.
In order to support more data types, override the :meth:`default` Assign a subclass of this to :attr:`flask.Flask.json_encoder` or
method. :attr:`flask.Blueprint.json_encoder` to override the default.
""" """
def default(self, o): def default(self, o):
"""Implement this method in a subclass such that it returns a """Convert ``o`` to a JSON serializable type. See
serializable object for ``o``, or calls the base implementation (to :meth:`json.JSONEncoder.default`. Python does not support
raise a :exc:`TypeError`). overriding how basic types like ``str`` or ``list`` are
serialized, they are handled before this method.
For example, to support arbitrary iterators, you could implement
default like this::
def default(self, o):
try:
iterable = iter(o)
except TypeError:
pass
else:
return list(iterable)
return JSONEncoder.default(self, o)
""" """
if isinstance(o, datetime): if isinstance(o, datetime):
return http_date(o.utctimetuple()) return http_date(o.utctimetuple())
@ -88,14 +51,17 @@ class JSONEncoder(_json.JSONEncoder):
return dataclasses.asdict(o) return dataclasses.asdict(o)
if hasattr(o, "__html__"): if hasattr(o, "__html__"):
return str(o.__html__()) return str(o.__html__())
return _json.JSONEncoder.default(self, o) return super().default(self, o)
class JSONDecoder(_json.JSONDecoder): class JSONDecoder(_json.JSONDecoder):
"""The default JSON decoder. This one does not change the behavior from """The default JSON decoder.
the default simplejson decoder. Consult the :mod:`json` documentation
for more information. This decoder is not only used for the load This does not change any behavior from the built-in
functions of this module but also :attr:`~flask.Request`. :class:`json.JSONDecoder`.
Assign a subclass of this to :attr:`flask.Flask.json_decoder` or
:attr:`flask.Blueprint.json_decoder` to override the default.
""" """
@ -106,13 +72,9 @@ def _dump_arg_defaults(kwargs, app=None):
if app: if app:
bp = app.blueprints.get(request.blueprint) if request else None bp = app.blueprints.get(request.blueprint) if request else None
kwargs.setdefault( cls = bp.json_encoder if bp and bp.json_encoder else app.json_encoder
"cls", bp.json_encoder if bp and bp.json_encoder else app.json_encoder kwargs.setdefault("cls", cls)
) kwargs.setdefault("ensure_ascii", app.config["JSON_AS_ASCII"])
if not app.config["JSON_AS_ASCII"]:
kwargs.setdefault("ensure_ascii", False)
kwargs.setdefault("sort_keys", 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)
@ -126,223 +88,235 @@ def _load_arg_defaults(kwargs, app=None):
if app: if app:
bp = app.blueprints.get(request.blueprint) if request else None bp = app.blueprints.get(request.blueprint) if request else None
kwargs.setdefault( cls = bp.json_decoder if bp and bp.json_decoder else app.json_decoder
"cls", bp.json_decoder if bp and bp.json_decoder else app.json_decoder kwargs.setdefault("cls", cls)
)
else: else:
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 an object to a string of JSON.
app context pushed, use the current app's configured encoder
(:attr:`~flask.Flask.json_encoder`), or fall back to the default
: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`, with
does some extra configuration based on the application. If the some defaults from application configuration.
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: Use this app's config instead of the active app context
Uses ``current_app`` if not given, and falls back to the default or defaults.
encoder when not in an app context. :param kwargs: Extra arguments passed to func:`json.dumps`.
:param kwargs: Extra arguments passed to :func:`json.dumps`.
.. versionchanged:: 2.0
``encoding`` is deprecated and will be removed in 2.1.
.. versionchanged:: 1.0.3 .. versionchanged:: 1.0.3
``app`` can be passed directly, rather than requiring an app ``app`` can be passed directly, rather than requiring an app
context for configuration. context for configuration.
""" """
_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):
rv = rv.encode(encoding) if encoding is not None:
warnings.warn(
"'encoding' is deprecated and will be removed in 2.1.",
DeprecationWarning,
stacklevel=2,
)
if isinstance(rv, str):
return rv.encode(encoding)
return rv return rv
def dump(obj, fp, app=None, **kwargs): def dump(obj, fp, app=None, **kwargs):
"""Like :func:`dumps` but writes into a file object.""" """Serialize an object to JSON written to a file object.
Takes the same arguments as the built-in :func:`json.dump`, with
some defaults from application configuration.
:param obj: Object to serialize to JSON.
:param fp: File object to write JSON to.
:param app: Use this app's config instead of the active app context
or defaults.
:param kwargs: Extra arguments passed to func:`json.dump`.
.. versionchanged:: 2.0
Writing to a binary file, and the ``encoding`` argument, is
deprecated and will be removed in 2.1.
"""
_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: show_warning = encoding is not None
fp = _wrap_writer_for_text(fp, encoding)
try:
fp.write("")
except TypeError:
show_warning = True
fp = io.TextIOWrapper(fp, encoding or "utf-8")
if show_warning:
warnings.warn(
"Writing to a binary file, and the 'encoding' argument, is"
" deprecated and will be removed in 2.1.",
DeprecationWarning,
stacklevel=2,
)
_json.dump(obj, fp, **kwargs) _json.dump(obj, fp, **kwargs)
def loads(s, app=None, **kwargs): def loads(s, app=None, **kwargs):
"""Deserialize an object from a JSON-formatted string ``s``. If """Deserialize an object from a string of JSON.
there is an app context pushed, use the current app's configured
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 Takes the same arguments as the built-in :func:`json.loads`, with
does some extra configuration based on the application. If the some defaults from application configuration.
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: Use this app's config instead of the active app context
Uses ``current_app`` if not given, and falls back to the default or defaults.
encoder when not in an app context. :param kwargs: Extra arguments passed to func:`json.dump`.
:param kwargs: Extra arguments passed to :func:`json.dumps`.
.. versionchanged:: 2.0
``encoding`` is deprecated and will be removed in 2.1. The data
must be a string or UTF-8 bytes.
.. versionchanged:: 1.0.3 .. versionchanged:: 1.0.3
``app`` can be passed directly, rather than requiring an app ``app`` can be passed directly, rather than requiring an app
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:
encoding = detect_encoding(s) warnings.warn(
s = s.decode(encoding) "'encoding' is deprecated and will be removed in 2.1. The"
" data must be a string or UTF-8 bytes.",
DeprecationWarning,
stacklevel=2,
)
if isinstance(s, bytes):
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.""" """Deserialize an object from JSON read from a file object.
Takes the same arguments as the built-in :func:`json.load`, with
some defaults from application configuration.
:param fp: File object to read JSON from.
:param app: Use this app's config instead of the active app context
or defaults.
:param kwargs: Extra arguments passed to func:`json.load`.
.. versionchanged:: 2.0
``encoding`` is deprecated and will be removed in 2.1. The file
must be text mode, or binary mode with UTF-8 bytes.
"""
_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)
if encoding is not None:
warnings.warn(
"'encoding' is deprecated and will be removed in 2.1. The"
" file must be text mode, or binary mode with UTF-8 bytes.",
DeprecationWarning,
stacklevel=2,
)
if isinstance(fp.read(0), bytes):
fp = io.TextIOWrapper(fp, encoding)
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>`` """Serialize an object to a string of JSON, replacing HTML-unsafe
tags. It accepts the same arguments and returns a JSON string. Note that characters with Unicode escapes. Otherwise behaves the same as
this is available in templates through the ``|tojson`` filter which will :func:`dumps`.
also mark the result as safe. Due to how this function escapes certain
characters this is safe even if used outside of ``<script>`` tags.
The following characters are escaped in strings: This is available in templates as the ``|tojson`` filter, which will
also mark the result with ``|safe``.
- ``<`` The returned string is safe to render in HTML documents and
- ``>`` ``<script>`` tags. The exception is in HTML attributes that are
- ``&`` double quoted; either use single quotes or the ``|forceescape``
- ``'`` filter.
This makes it safe to embed such strings in any place in HTML with the
notable exception of double quoted attributes. In that case single
quote your attributes or HTML escape it in addition.
.. versionchanged:: 0.10 .. versionchanged:: 0.10
This function's return value is now always safe for HTML usage, even Single quotes are escaped, making this safe to use in HTML,
if outside of script tags or if used in XHTML. This rule does not ``<script>`` tags, and single-quoted attributes without further
hold true when using this function in HTML attributes that are double escaping.
quoted. Always single quote attributes if you use the ``|tojson``
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):
"""Like :func:`htmlsafe_dumps` but writes into a file object.""" """Serialize an object to JSON written to a file object, replacing
fp.write(str(htmlsafe_dumps(obj, **kwargs))) HTML-unsafe characters with Unicode escapes. See
:func:`htmlsafe_dumps` and :func:`dumps`.
"""
fp.write(htmlsafe_dumps(obj, **kwargs))
def jsonify(*args, **kwargs): def jsonify(*args, **kwargs):
"""This function wraps :func:`dumps` to add a few enhancements that make """Serialize data to JSON and wrap it in a :class:`~flask.Response`
life easier. It turns the JSON output into a :class:`~flask.Response` with the :mimetype:`application/json` mimetype.
object with the :mimetype:`application/json` mimetype. For convenience, it
also converts multiple arguments into an array or multiple keyword arguments
into a dict. This means that both ``jsonify(1,2,3)`` and
``jsonify([1,2,3])`` serialize to ``[1,2,3]``.
For clarity, the JSON serialization behavior has the following differences Uses :func:`dumps` to serialize the data, but ``args`` and
from :func:`dumps`: ``kwargs`` are treated as data rather than arguments to
:func:`json.dumps`.
1. Single argument: Passed straight through to :func:`dumps`. 1. Single argument: Treated as a single value.
2. Multiple arguments: Converted to an array before being passed to 2. Multiple arguments: Treated as a list of values.
:func:`dumps`. ``jsonify(1, 2, 3)`` is the same as ``jsonify([1, 2, 3])``.
3. Multiple keyword arguments: Converted to a dict before being passed to 3. Keyword arguments: Treated as a dict of values.
:func:`dumps`. ``jsonify(data=data, errors=errors)`` is the same as
4. Both args and kwargs: Behavior undefined and will throw an exception. ``jsonify({"data": data, "errors": errors})``.
4. Passing both arguments and keyword arguments is not allowed as
it's not clear what should happen.
Example usage:: .. code-block:: python
from flask import jsonify from flask import jsonify
@app.route('/_get_current_user') @app.route("/users/me")
def get_current_user(): def get_current_user():
return jsonify(username=g.user.username, return jsonify(
email=g.user.email, username=g.user.username,
id=g.user.id) email=g.user.email,
id=g.user.id,
)
This will send a JSON response like this to the browser:: Will return a JSON response like this:
.. code-block:: javascript
{ {
"username": "admin", "username": "admin",
"email": "admin@localhost", "email": "admin@localhost",
"id": 42 "id": 42
} }
The default output omits indents and spaces after separators. In
debug mode or if :data:`JSONIFY_PRETTYPRINT_REGULAR` is ``True``,
the output will be formatted to be easier to read.
.. versionchanged:: 0.11 .. versionchanged:: 0.11
Added support for serializing top-level arrays. This introduces Added support for serializing top-level arrays. This introduces
a security risk in ancient browsers. See :ref:`security-json` a security risk in ancient browsers. See :ref:`security-json`.
for details.
This function's response will be pretty printed if the
``JSONIFY_PRETTYPRINT_REGULAR`` config parameter is set to True or the
Flask app is running in debug mode. Compressed (not pretty) formatting
currently means no indents and no spaces after separators.
.. versionadded:: 0.2 .. versionadded:: 0.2
""" """
indent = None indent = None
separators = (",", ":") separators = (",", ":")

View file

@ -45,7 +45,7 @@ from base64 import b64encode
from datetime import datetime from datetime import datetime
from uuid import UUID from uuid import UUID
from jinja2 import Markup from markupsafe import Markup
from werkzeug.http import http_date from werkzeug.http import http_date
from werkzeug.http import parse_date from werkzeug.http import parse_date
@ -167,9 +167,9 @@ class TagBytes(JSONTag):
class TagMarkup(JSONTag): class TagMarkup(JSONTag):
"""Serialize anything matching the :class:`~flask.Markup` API by """Serialize anything matching the :class:`~markupsafe.Markup` API by
having a ``__html__`` method to the result of that method. Always having a ``__html__`` method to the result of that method. Always
deserializes to an instance of :class:`~flask.Markup`.""" deserializes to an instance of :class:`~markupsafe.Markup`."""
__slots__ = () __slots__ = ()
key = " m" key = " m"
@ -222,7 +222,7 @@ class TaggedJSONSerializer:
* :class:`dict` * :class:`dict`
* :class:`tuple` * :class:`tuple`
* :class:`bytes` * :class:`bytes`
* :class:`~flask.Markup` * :class:`~markupsafe.Markup`
* :class:`~uuid.UUID` * :class:`~uuid.UUID`
* :class:`~datetime.datetime` * :class:`~datetime.datetime`
""" """

View file

@ -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
@ -253,7 +232,6 @@ class TestJSON:
render = flask.render_template_string render = flask.render_template_string
rv = flask.json.htmlsafe_dumps("</script>") rv = flask.json.htmlsafe_dumps("</script>")
assert rv == '"\\u003c/script\\u003e"' assert rv == '"\\u003c/script\\u003e"'
assert type(rv) is str
rv = render('{{ "</script>"|tojson }}') rv = render('{{ "</script>"|tojson }}')
assert rv == '"\\u003c/script\\u003e"' assert rv == '"\\u003c/script\\u003e"'
rv = render('{{ "<\0/script>"|tojson }}') rv = render('{{ "<\0/script>"|tojson }}')

View file

@ -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]