]> jfr.im git - dlqueue.git/blob - venv/lib/python3.11/site-packages/flask/json/provider.py
init: venv aand flask
[dlqueue.git] / venv / lib / python3.11 / site-packages / flask / json / provider.py
1 from __future__ import annotations
2
3 import dataclasses
4 import decimal
5 import json
6 import typing as t
7 import uuid
8 import weakref
9 from datetime import date
10
11 from werkzeug.http import http_date
12
13 if t.TYPE_CHECKING: # pragma: no cover
14 from ..sansio.app import App
15 from ..wrappers import Response
16
17
18 class 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.
22
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.
26
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.
30
31 :param app: An application instance. This will be stored as a
32 :class:`weakref.proxy` on the :attr:`_app` attribute.
33
34 .. versionadded:: 2.2
35 """
36
37 def __init__(self, app: App) -> None:
38 self._app = weakref.proxy(app)
39
40 def dumps(self, obj: t.Any, **kwargs: t.Any) -> str:
41 """Serialize data as JSON.
42
43 :param obj: The data to serialize.
44 :param kwargs: May be passed to the underlying JSON library.
45 """
46 raise NotImplementedError
47
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.
50
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))
57
58 def loads(self, s: str | bytes, **kwargs: t.Any) -> t.Any:
59 """Deserialize data as JSON.
60
61 :param s: Text or UTF-8 bytes.
62 :param kwargs: May be passed to the underlying JSON library.
63 """
64 raise NotImplementedError
65
66 def load(self, fp: t.IO[t.AnyStr], **kwargs: t.Any) -> t.Any:
67 """Deserialize data as JSON read from a file.
68
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)
73
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")
79
80 if not args and not kwargs:
81 return None
82
83 if len(args) == 1:
84 return args[0]
85
86 return args or kwargs
87
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.
92
93 The :func:`~flask.json.jsonify` function calls this method for
94 the current application.
95
96 Either positional or keyword arguments can be given, not both.
97 If no arguments are given, ``None`` is serialized.
98
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")
105
106
107 def _default(o: t.Any) -> t.Any:
108 if isinstance(o, date):
109 return http_date(o)
110
111 if isinstance(o, (decimal.Decimal, uuid.UUID)):
112 return str(o)
113
114 if dataclasses and dataclasses.is_dataclass(o):
115 return dataclasses.asdict(o)
116
117 if hasattr(o, "__html__"):
118 return str(o.__html__())
119
120 raise TypeError(f"Object of type {type(o).__name__} is not JSON serializable")
121
122
123 class DefaultJSONProvider(JSONProvider):
124 """Provide JSON operations using Python's built-in :mod:`json`
125 library. Serializes the following additional data types:
126
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 """
136
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 """
144
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 """
150
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 """
157
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 """
163
164 mimetype = "application/json"
165 """The mimetype set in :meth:`response`."""
166
167 def dumps(self, obj: t.Any, **kwargs: t.Any) -> str:
168 """Serialize data as JSON to a string.
169
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.
173
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)
181
182 def loads(self, s: str | bytes, **kwargs: t.Any) -> t.Any:
183 """Deserialize data as JSON from a string or bytes.
184
185 :param s: Text or UTF-8 bytes.
186 :param kwargs: Passed to :func:`json.loads`.
187 """
188 return json.loads(s, **kwargs)
189
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`.
195
196 If :attr:`compact` is ``False`` or debug mode is enabled, the
197 output will be formatted to be easier to read.
198
199 Either positional or keyword arguments can be given, not both.
200 If no arguments are given, ``None`` is serialized.
201
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] = {}
208
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", (",", ":"))
213
214 return self._app.response_class(
215 f"{self.dumps(obj, **dump_args)}\n", mimetype=self.mimetype
216 )