Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/flask/json/provider.py: 69%
61 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-09 07:17 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-09 07:17 +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
13if t.TYPE_CHECKING: # pragma: no cover
14 from ..sansio.app import App
15 from ..wrappers import Response
18class JSONProvider:
19 """A standard set of JSON operations for an application. Subclasses
20 of this can be used to customize JSON behavior or use different
21 JSON libraries.
23 To implement a provider for a specific library, subclass this base
24 class and implement at least :meth:`dumps` and :meth:`loads`. All
25 other methods have default implementations.
27 To use a different provider, either subclass ``Flask`` and set
28 :attr:`~flask.Flask.json_provider_class` to a provider class, or set
29 :attr:`app.json <flask.Flask.json>` to an instance of the class.
31 :param app: An application instance. This will be stored as a
32 :class:`weakref.proxy` on the :attr:`_app` attribute.
34 .. versionadded:: 2.2
35 """
37 def __init__(self, app: App) -> None:
38 self._app = weakref.proxy(app)
40 def dumps(self, obj: t.Any, **kwargs: t.Any) -> str:
41 """Serialize data as JSON.
43 :param obj: The data to serialize.
44 :param kwargs: May be passed to the underlying JSON library.
45 """
46 raise NotImplementedError
48 def dump(self, obj: t.Any, fp: t.IO[str], **kwargs: t.Any) -> None:
49 """Serialize data as JSON and write to a file.
51 :param obj: The data to serialize.
52 :param fp: A file opened for writing text. Should use the UTF-8
53 encoding to be valid JSON.
54 :param kwargs: May be passed to the underlying JSON library.
55 """
56 fp.write(self.dumps(obj, **kwargs))
58 def loads(self, s: str | bytes, **kwargs: t.Any) -> t.Any:
59 """Deserialize data as JSON.
61 :param s: Text or UTF-8 bytes.
62 :param kwargs: May be passed to the underlying JSON library.
63 """
64 raise NotImplementedError
66 def load(self, fp: t.IO[t.AnyStr], **kwargs: t.Any) -> t.Any:
67 """Deserialize data as JSON read from a file.
69 :param fp: A file opened for reading text or UTF-8 bytes.
70 :param kwargs: May be passed to the underlying JSON library.
71 """
72 return self.loads(fp.read(), **kwargs)
74 def _prepare_response_obj(
75 self, args: tuple[t.Any, ...], kwargs: dict[str, t.Any]
76 ) -> t.Any:
77 if args and kwargs:
78 raise TypeError("app.json.response() takes either args or kwargs, not both")
80 if not args and not kwargs:
81 return None
83 if len(args) == 1:
84 return args[0]
86 return args or kwargs
88 def response(self, *args: t.Any, **kwargs: t.Any) -> Response:
89 """Serialize the given arguments as JSON, and return a
90 :class:`~flask.Response` object with the ``application/json``
91 mimetype.
93 The :func:`~flask.json.jsonify` function calls this method for
94 the current application.
96 Either positional or keyword arguments can be given, not both.
97 If no arguments are given, ``None`` is serialized.
99 :param args: A single value to serialize, or multiple values to
100 treat as a list to serialize.
101 :param kwargs: Treat as a dict to serialize.
102 """
103 obj = self._prepare_response_obj(args, kwargs)
104 return self._app.response_class(self.dumps(obj), mimetype="application/json")
107def _default(o: t.Any) -> t.Any:
108 if isinstance(o, date):
109 return http_date(o)
111 if isinstance(o, (decimal.Decimal, uuid.UUID)):
112 return str(o)
114 if dataclasses and dataclasses.is_dataclass(o):
115 return dataclasses.asdict(o)
117 if hasattr(o, "__html__"):
118 return str(o.__html__())
120 raise TypeError(f"Object of type {type(o).__name__} is not JSON serializable")
123class DefaultJSONProvider(JSONProvider):
124 """Provide JSON operations using Python's built-in :mod:`json`
125 library. Serializes the following additional data types:
127 - :class:`datetime.datetime` and :class:`datetime.date` are
128 serialized to :rfc:`822` strings. This is the same as the HTTP
129 date format.
130 - :class:`uuid.UUID` is serialized to a string.
131 - :class:`dataclasses.dataclass` is passed to
132 :func:`dataclasses.asdict`.
133 - :class:`~markupsafe.Markup` (or any object with a ``__html__``
134 method) will call the ``__html__`` method to get a string.
135 """
137 default: t.Callable[[t.Any], t.Any] = staticmethod(
138 _default
139 ) # type: ignore[assignment]
140 """Apply this function to any object that :meth:`json.dumps` does
141 not know how to serialize. It should return a valid JSON type or
142 raise a ``TypeError``.
143 """
145 ensure_ascii = True
146 """Replace non-ASCII characters with escape sequences. This may be
147 more compatible with some clients, but can be disabled for better
148 performance and size.
149 """
151 sort_keys = True
152 """Sort the keys in any serialized dicts. This may be useful for
153 some caching situations, but can be disabled for better performance.
154 When enabled, keys must all be strings, they are not converted
155 before sorting.
156 """
158 compact: bool | None = None
159 """If ``True``, or ``None`` out of debug mode, the :meth:`response`
160 output will not add indentation, newlines, or spaces. If ``False``,
161 or ``None`` in debug mode, it will use a non-compact representation.
162 """
164 mimetype = "application/json"
165 """The mimetype set in :meth:`response`."""
167 def dumps(self, obj: t.Any, **kwargs: t.Any) -> str:
168 """Serialize data as JSON to a string.
170 Keyword arguments are passed to :func:`json.dumps`. Sets some
171 parameter defaults from the :attr:`default`,
172 :attr:`ensure_ascii`, and :attr:`sort_keys` attributes.
174 :param obj: The data to serialize.
175 :param kwargs: Passed to :func:`json.dumps`.
176 """
177 kwargs.setdefault("default", self.default)
178 kwargs.setdefault("ensure_ascii", self.ensure_ascii)
179 kwargs.setdefault("sort_keys", self.sort_keys)
180 return json.dumps(obj, **kwargs)
182 def loads(self, s: str | bytes, **kwargs: t.Any) -> t.Any:
183 """Deserialize data as JSON from a string or bytes.
185 :param s: Text or UTF-8 bytes.
186 :param kwargs: Passed to :func:`json.loads`.
187 """
188 return json.loads(s, **kwargs)
190 def response(self, *args: t.Any, **kwargs: t.Any) -> Response:
191 """Serialize the given arguments as JSON, and return a
192 :class:`~flask.Response` object with it. The response mimetype
193 will be "application/json" and can be changed with
194 :attr:`mimetype`.
196 If :attr:`compact` is ``False`` or debug mode is enabled, the
197 output will be formatted to be easier to read.
199 Either positional or keyword arguments can be given, not both.
200 If no arguments are given, ``None`` is serialized.
202 :param args: A single value to serialize, or multiple values to
203 treat as a list to serialize.
204 :param kwargs: Treat as a dict to serialize.
205 """
206 obj = self._prepare_response_obj(args, kwargs)
207 dump_args: dict[str, t.Any] = {}
209 if (self.compact is None and self._app.debug) or self.compact is False:
210 dump_args.setdefault("indent", 2)
211 else:
212 dump_args.setdefault("separators", (",", ":"))
214 return self._app.response_class(
215 f"{self.dumps(obj, **dump_args)}\n", mimetype=self.mimetype
216 )