deprecate JSON encoding options

make consistent with built-in json module
This commit is contained in:
David Lord 2020-04-07 12:32:27 -07:00
parent c43edfc7c0
commit 8b5f760b72
No known key found for this signature in database
GPG key ID: 7A1C87E3F5BC42A8
2 changed files with 54 additions and 49 deletions

View file

@ -1,10 +1,11 @@
import io import io
import json as _json 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 jinja2 import Markup from markupsafe 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,33 +18,6 @@ except ImportError:
dataclasses = None dataclasses = None
__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 Flask JSON encoder. This one extends the default
encoder by also supporting ``datetime``, ``UUID``, ``dataclasses``, encoder by also supporting ``datetime``, ``UUID``, ``dataclasses``,
@ -83,7 +57,7 @@ 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):
@ -101,13 +75,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)
@ -121,9 +91,8 @@ 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)
@ -152,8 +121,15 @@ def dumps(obj, app=None, **kwargs):
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:
rv = rv.encode(encoding) 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
@ -162,9 +138,21 @@ 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)
show_warning = encoding is not None
if encoding is not None: try:
fp = _wrap_writer_for_text(fp, encoding) 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)
@ -192,8 +180,16 @@ def loads(s, app=None, **kwargs):
_load_arg_defaults(kwargs, app=app) _load_arg_defaults(kwargs, app=app)
encoding = kwargs.pop("encoding", None) encoding = kwargs.pop("encoding", None)
if encoding is not None and isinstance(s, bytes): if encoding is not None:
s = s.decode(encoding) warnings.warn(
"'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)
@ -202,7 +198,18 @@ 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)
encoding = kwargs.pop("encoding", None) encoding = kwargs.pop("encoding", None)
fp = _wrap_reader_for_text(fp, encoding or "utf-8")
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)
@ -241,7 +248,7 @@ def htmlsafe_dumps(obj, **kwargs):
def htmlsafe_dump(obj, fp, **kwargs): def htmlsafe_dump(obj, fp, **kwargs):
"""Like :func:`htmlsafe_dumps` but writes into a file object.""" """Like :func:`htmlsafe_dumps` but writes into a file object."""
fp.write(str(htmlsafe_dumps(obj, **kwargs))) fp.write(htmlsafe_dumps(obj, **kwargs))
def jsonify(*args, **kwargs): def jsonify(*args, **kwargs):
@ -293,7 +300,6 @@ def jsonify(*args, **kwargs):
.. versionadded:: 0.2 .. versionadded:: 0.2
""" """
indent = None indent = None
separators = (",", ":") separators = (",", ":")

View file

@ -232,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 }}')