Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/flask/json/provider.py: 28%
101 statements
« prev ^ index » next coverage.py v7.0.1, created at 2022-12-25 06:11 +0000
« prev ^ index » next coverage.py v7.0.1, created at 2022-12-25 06:11 +0000
1from __future__ import annotations
3import dataclasses
4import decimal
5import json
6import typing as t
7import uuid
8import weakref
9from datetime import date
11from werkzeug.http import http_date
13from ..globals import request
15if t.TYPE_CHECKING: # pragma: no cover
16 from ..app import Flask
17 from ..wrappers import Response
20class JSONProvider:
21 """A standard set of JSON operations for an application. Subclasses
22 of this can be used to customize JSON behavior or use different
23 JSON libraries.
25 To implement a provider for a specific library, subclass this base
26 class and implement at least :meth:`dumps` and :meth:`loads`. All
27 other methods have default implementations.
29 To use a different provider, either subclass ``Flask`` and set
30 :attr:`~flask.Flask.json_provider_class` to a provider class, or set
31 :attr:`app.json <flask.Flask.json>` to an instance of the class.
33 :param app: An application instance. This will be stored as a
34 :class:`weakref.proxy` on the :attr:`_app` attribute.
36 .. versionadded:: 2.2
37 """
39 def __init__(self, app: Flask) -> None:
40 self._app = weakref.proxy(app)
42 def dumps(self, obj: t.Any, **kwargs: t.Any) -> str:
43 """Serialize data as JSON.
45 :param obj: The data to serialize.
46 :param kwargs: May be passed to the underlying JSON library.
47 """
48 raise NotImplementedError
50 def dump(self, obj: t.Any, fp: t.IO[str], **kwargs: t.Any) -> None:
51 """Serialize data as JSON and write to a file.
53 :param obj: The data to serialize.
54 :param fp: A file opened for writing text. Should use the UTF-8
55 encoding to be valid JSON.
56 :param kwargs: May be passed to the underlying JSON library.
57 """
58 fp.write(self.dumps(obj, **kwargs))
60 def loads(self, s: str | bytes, **kwargs: t.Any) -> t.Any:
61 """Deserialize data as JSON.
63 :param s: Text or UTF-8 bytes.
64 :param kwargs: May be passed to the underlying JSON library.
65 """
66 raise NotImplementedError
68 def load(self, fp: t.IO[t.AnyStr], **kwargs: t.Any) -> t.Any:
69 """Deserialize data as JSON read from a file.
71 :param fp: A file opened for reading text or UTF-8 bytes.
72 :param kwargs: May be passed to the underlying JSON library.
73 """
74 return self.loads(fp.read(), **kwargs)
76 def _prepare_response_obj(
77 self, args: t.Tuple[t.Any, ...], kwargs: t.Dict[str, t.Any]
78 ) -> t.Any:
79 if args and kwargs:
80 raise TypeError("app.json.response() takes either args or kwargs, not both")
82 if not args and not kwargs:
83 return None
85 if len(args) == 1:
86 return args[0]
88 return args or kwargs
90 def response(self, *args: t.Any, **kwargs: t.Any) -> Response:
91 """Serialize the given arguments as JSON, and return a
92 :class:`~flask.Response` object with the ``application/json``
93 mimetype.
95 The :func:`~flask.json.jsonify` function calls this method for
96 the current application.
98 Either positional or keyword arguments can be given, not both.
99 If no arguments are given, ``None`` is serialized.
101 :param args: A single value to serialize, or multiple values to
102 treat as a list to serialize.
103 :param kwargs: Treat as a dict to serialize.
104 """
105 obj = self._prepare_response_obj(args, kwargs)
106 return self._app.response_class(self.dumps(obj), mimetype="application/json")
109def _default(o: t.Any) -> t.Any:
110 if isinstance(o, date):
111 return http_date(o)
113 if isinstance(o, (decimal.Decimal, uuid.UUID)):
114 return str(o)
116 if dataclasses and dataclasses.is_dataclass(o):
117 return dataclasses.asdict(o)
119 if hasattr(o, "__html__"):
120 return str(o.__html__())
122 raise TypeError(f"Object of type {type(o).__name__} is not JSON serializable")
125class DefaultJSONProvider(JSONProvider):
126 """Provide JSON operations using Python's built-in :mod:`json`
127 library. Serializes the following additional data types:
129 - :class:`datetime.datetime` and :class:`datetime.date` are
130 serialized to :rfc:`822` strings. This is the same as the HTTP
131 date format.
132 - :class:`uuid.UUID` is serialized to a string.
133 - :class:`dataclasses.dataclass` is passed to
134 :func:`dataclasses.asdict`.
135 - :class:`~markupsafe.Markup` (or any object with a ``__html__``
136 method) will call the ``__html__`` method to get a string.
137 """
139 default: t.Callable[[t.Any], t.Any] = staticmethod(
140 _default
141 ) # type: ignore[assignment]
142 """Apply this function to any object that :meth:`json.dumps` does
143 not know how to serialize. It should return a valid JSON type or
144 raise a ``TypeError``.
145 """
147 ensure_ascii = True
148 """Replace non-ASCII characters with escape sequences. This may be
149 more compatible with some clients, but can be disabled for better
150 performance and size.
151 """
153 sort_keys = True
154 """Sort the keys in any serialized dicts. This may be useful for
155 some caching situations, but can be disabled for better performance.
156 When enabled, keys must all be strings, they are not converted
157 before sorting.
158 """
160 compact: bool | None = None
161 """If ``True``, or ``None`` out of debug mode, the :meth:`response`
162 output will not add indentation, newlines, or spaces. If ``False``,
163 or ``None`` in debug mode, it will use a non-compact representation.
164 """
166 mimetype = "application/json"
167 """The mimetype set in :meth:`response`."""
169 def dumps(self, obj: t.Any, **kwargs: t.Any) -> str:
170 """Serialize data as JSON to a string.
172 Keyword arguments are passed to :func:`json.dumps`. Sets some
173 parameter defaults from the :attr:`default`,
174 :attr:`ensure_ascii`, and :attr:`sort_keys` attributes.
176 :param obj: The data to serialize.
177 :param kwargs: Passed to :func:`json.dumps`.
178 """
179 cls = self._app._json_encoder
180 bp = self._app.blueprints.get(request.blueprint) if request else None
182 if bp is not None and bp._json_encoder is not None:
183 cls = bp._json_encoder
185 if cls is not None:
186 import warnings
188 warnings.warn(
189 "Setting 'json_encoder' on the app or a blueprint is"
190 " deprecated and will be removed in Flask 2.3."
191 " Customize 'app.json' instead.",
192 DeprecationWarning,
193 )
194 kwargs.setdefault("cls", cls)
196 if "default" not in cls.__dict__:
197 kwargs.setdefault("default", self.default)
198 else:
199 kwargs.setdefault("default", self.default)
201 ensure_ascii = self._app.config["JSON_AS_ASCII"]
202 sort_keys = self._app.config["JSON_SORT_KEYS"]
204 if ensure_ascii is not None:
205 import warnings
207 warnings.warn(
208 "The 'JSON_AS_ASCII' config key is deprecated and will"
209 " be removed in Flask 2.3. Set 'app.json.ensure_ascii'"
210 " instead.",
211 DeprecationWarning,
212 )
213 else:
214 ensure_ascii = self.ensure_ascii
216 if sort_keys is not None:
217 import warnings
219 warnings.warn(
220 "The 'JSON_SORT_KEYS' config key is deprecated and will"
221 " be removed in Flask 2.3. Set 'app.json.sort_keys'"
222 " instead.",
223 DeprecationWarning,
224 )
225 else:
226 sort_keys = self.sort_keys
228 kwargs.setdefault("ensure_ascii", ensure_ascii)
229 kwargs.setdefault("sort_keys", sort_keys)
230 return json.dumps(obj, **kwargs)
232 def loads(self, s: str | bytes, **kwargs: t.Any) -> t.Any:
233 """Deserialize data as JSON from a string or bytes.
235 :param s: Text or UTF-8 bytes.
236 :param kwargs: Passed to :func:`json.loads`.
237 """
238 cls = self._app._json_decoder
239 bp = self._app.blueprints.get(request.blueprint) if request else None
241 if bp is not None and bp._json_decoder is not None:
242 cls = bp._json_decoder
244 if cls is not None:
245 import warnings
247 warnings.warn(
248 "Setting 'json_decoder' on the app or a blueprint is"
249 " deprecated and will be removed in Flask 2.3."
250 " Customize 'app.json' instead.",
251 DeprecationWarning,
252 )
253 kwargs.setdefault("cls", cls)
255 return json.loads(s, **kwargs)
257 def response(self, *args: t.Any, **kwargs: t.Any) -> Response:
258 """Serialize the given arguments as JSON, and return a
259 :class:`~flask.Response` object with it. The response mimetype
260 will be "application/json" and can be changed with
261 :attr:`mimetype`.
263 If :attr:`compact` is ``False`` or debug mode is enabled, the
264 output will be formatted to be easier to read.
266 Either positional or keyword arguments can be given, not both.
267 If no arguments are given, ``None`` is serialized.
269 :param args: A single value to serialize, or multiple values to
270 treat as a list to serialize.
271 :param kwargs: Treat as a dict to serialize.
272 """
273 obj = self._prepare_response_obj(args, kwargs)
274 dump_args: t.Dict[str, t.Any] = {}
275 pretty = self._app.config["JSONIFY_PRETTYPRINT_REGULAR"]
276 mimetype = self._app.config["JSONIFY_MIMETYPE"]
278 if pretty is not None:
279 import warnings
281 warnings.warn(
282 "The 'JSONIFY_PRETTYPRINT_REGULAR' config key is"
283 " deprecated and will be removed in Flask 2.3. Set"
284 " 'app.json.compact' instead.",
285 DeprecationWarning,
286 )
287 compact: bool | None = not pretty
288 else:
289 compact = self.compact
291 if (compact is None and self._app.debug) or compact is False:
292 dump_args.setdefault("indent", 2)
293 else:
294 dump_args.setdefault("separators", (",", ":"))
296 if mimetype is not None:
297 import warnings
299 warnings.warn(
300 "The 'JSONIFY_MIMETYPE' config key is deprecated and"
301 " will be removed in Flask 2.3. Set 'app.json.mimetype'"
302 " instead.",
303 DeprecationWarning,
304 )
305 else:
306 mimetype = self.mimetype
308 return self._app.response_class(
309 f"{self.dumps(obj, **dump_args)}\n", mimetype=mimetype
310 )