diff --git a/CHANGES.rst b/CHANGES.rst index 2b021d8815..2cd3144b3e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,9 @@ Version 2.2.1 Unreleased +- Setting or accessing ``json_encoder`` or ``json_decoder`` raises a + deprecation warning. :issue:`4732` + Version 2.2.0 ------------- @@ -50,18 +53,18 @@ Released 2022-08-01 provider can be set to use a different JSON library. ``flask.jsonify`` will call ``app.json.response``, other functions in ``flask.json`` will call corresponding functions in - ``app.json``. :pr:`4688` + ``app.json``. :pr:`4692` - JSON configuration is moved to attributes on the default ``app.json`` provider. ``JSON_AS_ASCII``, ``JSON_SORT_KEYS``, ``JSONIFY_MIMETYPE``, and ``JSONIFY_PRETTYPRINT_REGULAR`` are - deprecated. :pr:`4688` + deprecated. :pr:`4692` - Setting custom ``json_encoder`` and ``json_decoder`` classes on the app or a blueprint, and the corresponding ``json.JSONEncoder`` and ``JSONDecoder`` classes, are deprecated. JSON behavior can now be - overridden using the ``app.json`` provider interface. :pr:`4688` + overridden using the ``app.json`` provider interface. :pr:`4692` - ``json.htmlsafe_dumps`` and ``json.htmlsafe_dump`` are deprecated, - the function is built-in to Jinja now. :pr:`4688` + the function is built-in to Jinja now. :pr:`4692` - Refactor ``register_error_handler`` to consolidate error checking. Rewrite some error messages to be more consistent. :issue:`4559` - Use Blueprint decorators and functions intended for setup after diff --git a/src/flask/app.py b/src/flask/app.py index 2da0a9fa2c..965865f2aa 100644 --- a/src/flask/app.py +++ b/src/flask/app.py @@ -1,5 +1,6 @@ import functools import inspect +import json import logging import os import sys @@ -379,25 +380,86 @@ def use_x_sendfile(self, value: bool) -> None: ) self.config["USE_X_SENDFILE"] = value - #: The JSON encoder class to use. Defaults to - #: :class:`~flask.json.JSONEncoder`. - #: - #: .. deprecated:: 2.2 - #: Will be removed in Flask 2.3. Customize - #: :attr:`json_provider_class` instead. - #: - #: .. versionadded:: 0.10 - json_encoder: None = None + _json_encoder: t.Union[t.Type[json.JSONEncoder], None] = None + _json_decoder: t.Union[t.Type[json.JSONDecoder], None] = None - #: The JSON decoder class to use. Defaults to - #: :class:`~flask.json.JSONDecoder`. - #: - #: .. deprecated:: 2.2 - #: Will be removed in Flask 2.3. Customize - #: :attr:`json_provider_class` instead. - #: - #: .. versionadded:: 0.10 - json_decoder: None = None + @property # type: ignore[override] + def json_encoder(self) -> t.Type[json.JSONEncoder]: # type: ignore[override] + """The JSON encoder class to use. Defaults to + :class:`~flask.json.JSONEncoder`. + + .. deprecated:: 2.2 + Will be removed in Flask 2.3. Customize + :attr:`json_provider_class` instead. + + .. versionadded:: 0.10 + """ + import warnings + + warnings.warn( + "'app.json_encoder' is deprecated and will be removed in Flask 2.3." + " Customize 'app.json_provider_class' or 'app.json' instead.", + DeprecationWarning, + stacklevel=2, + ) + + if self._json_encoder is None: + from . import json + + return json.JSONEncoder + + return self._json_encoder + + @json_encoder.setter + def json_encoder(self, value: t.Type[json.JSONEncoder]) -> None: + import warnings + + warnings.warn( + "'app.json_encoder' is deprecated and will be removed in Flask 2.3." + " Customize 'app.json_provider_class' or 'app.json' instead.", + DeprecationWarning, + stacklevel=2, + ) + self._json_encoder = value + + @property # type: ignore[override] + def json_decoder(self) -> t.Type[json.JSONDecoder]: # type: ignore[override] + """The JSON decoder class to use. Defaults to + :class:`~flask.json.JSONDecoder`. + + .. deprecated:: 2.2 + Will be removed in Flask 2.3. Customize + :attr:`json_provider_class` instead. + + .. versionadded:: 0.10 + """ + import warnings + + warnings.warn( + "'app.json_decoder' is deprecated and will be removed in Flask 2.3." + " Customize 'app.json_provider_class' or 'app.json' instead.", + DeprecationWarning, + stacklevel=2, + ) + + if self._json_decoder is None: + from . import json + + return json.JSONDecoder + + return self._json_decoder + + @json_decoder.setter + def json_decoder(self, value: t.Type[json.JSONDecoder]) -> None: + import warnings + + warnings.warn( + "'app.json_decoder' is deprecated and will be removed in Flask 2.3." + " Customize 'app.json_provider_class' or 'app.json' instead.", + DeprecationWarning, + stacklevel=2, + ) + self._json_decoder = value json_provider_class: t.Type[JSONProvider] = DefaultJSONProvider """A subclass of :class:`~flask.json.provider.JSONProvider`. An diff --git a/src/flask/blueprints.py b/src/flask/blueprints.py index 178851364b..104f8acf0d 100644 --- a/src/flask/blueprints.py +++ b/src/flask/blueprints.py @@ -1,3 +1,4 @@ +import json import os import typing as t from collections import defaultdict @@ -172,18 +173,76 @@ class Blueprint(Scaffold): _got_registered_once = False - #: Blueprint local JSON encoder class to use. Set to ``None`` to use - #: the app's :class:`~flask.Flask.json_encoder`. - #: - #: .. deprecated:: 2.2 - #: Will be removed in Flask 2.3. - json_encoder: None = None - #: Blueprint local JSON decoder class to use. Set to ``None`` to use - #: the app's :class:`~flask.Flask.json_decoder`. - #: - #: .. deprecated:: 2.2 - #: Will be removed in Flask 2.3. - json_decoder: None = None + _json_encoder: t.Union[t.Type[json.JSONEncoder], None] = None + _json_decoder: t.Union[t.Type[json.JSONDecoder], None] = None + + @property # type: ignore[override] + def json_encoder( # type: ignore[override] + self, + ) -> t.Union[t.Type[json.JSONEncoder], None]: + """Blueprint-local JSON encoder class to use. Set to ``None`` to use the app's. + + .. deprecated:: 2.2 + Will be removed in Flask 2.3. Customize + :attr:`json_provider_class` instead. + + .. versionadded:: 0.10 + """ + import warnings + + warnings.warn( + "'bp.json_encoder' is deprecated and will be removed in Flask 2.3." + " Customize 'app.json_provider_class' or 'app.json' instead.", + DeprecationWarning, + stacklevel=2, + ) + return self._json_encoder + + @json_encoder.setter + def json_encoder(self, value: t.Union[t.Type[json.JSONEncoder], None]) -> None: + import warnings + + warnings.warn( + "'bp.json_encoder' is deprecated and will be removed in Flask 2.3." + " Customize 'app.json_provider_class' or 'app.json' instead.", + DeprecationWarning, + stacklevel=2, + ) + self._json_encoder = value + + @property # type: ignore[override] + def json_decoder( # type: ignore[override] + self, + ) -> t.Union[t.Type[json.JSONDecoder], None]: + """Blueprint-local JSON decoder class to use. Set to ``None`` to use the app's. + + .. deprecated:: 2.2 + Will be removed in Flask 2.3. Customize + :attr:`json_provider_class` instead. + + .. versionadded:: 0.10 + """ + import warnings + + warnings.warn( + "'bp.json_decoder' is deprecated and will be removed in Flask 2.3." + " Customize 'app.json_provider_class' or 'app.json' instead.", + DeprecationWarning, + stacklevel=2, + ) + return self._json_decoder + + @json_decoder.setter + def json_decoder(self, value: t.Union[t.Type[json.JSONDecoder], None]) -> None: + import warnings + + warnings.warn( + "'bp.json_decoder' is deprecated and will be removed in Flask 2.3." + " Customize 'app.json_provider_class' or 'app.json' instead.", + DeprecationWarning, + stacklevel=2, + ) + self._json_decoder = value def __init__( self, diff --git a/src/flask/json/provider.py b/src/flask/json/provider.py index 025d9df57b..cb6aae809b 100644 --- a/src/flask/json/provider.py +++ b/src/flask/json/provider.py @@ -176,11 +176,11 @@ def dumps(self, obj: t.Any, **kwargs: t.Any) -> str: :param obj: The data to serialize. :param kwargs: Passed to :func:`json.dumps`. """ - cls = self._app.json_encoder + cls = self._app._json_encoder bp = self._app.blueprints.get(request.blueprint) if request else None - if bp is not None and bp.json_encoder is not None: - cls = bp.json_encoder + if bp is not None and bp._json_encoder is not None: + cls = bp._json_encoder if cls is not None: import warnings @@ -235,11 +235,11 @@ def loads(self, s: str | bytes, **kwargs: t.Any) -> t.Any: :param s: Text or UTF-8 bytes. :param kwargs: Passed to :func:`json.loads`. """ - cls = self._app.json_decoder + cls = self._app._json_decoder bp = self._app.blueprints.get(request.blueprint) if request else None - if bp is not None and bp.json_decoder is not None: - cls = bp.json_decoder + if bp is not None and bp._json_decoder is not None: + cls = bp._json_decoder if cls is not None: import warnings diff --git a/src/flask/scaffold.py b/src/flask/scaffold.py index 4d3356b880..1530a11ec8 100644 --- a/src/flask/scaffold.py +++ b/src/flask/scaffold.py @@ -1,4 +1,5 @@ import importlib.util +import json import os import pathlib import pkgutil @@ -78,14 +79,14 @@ class Scaffold: #: #: .. deprecated:: 2.2 #: Will be removed in Flask 2.3. - json_encoder: None = None + json_encoder: t.Union[t.Type[json.JSONEncoder], None] = None #: JSON decoder class used by :func:`flask.json.loads`. If a #: blueprint sets this, it will be used instead of the app's value. #: #: .. deprecated:: 2.2 #: Will be removed in Flask 2.3. - json_decoder: None = None + json_decoder: t.Union[t.Type[json.JSONDecoder], None] = None def __init__( self,