Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/flask/json/__init__.py: 29%

78 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-03-26 06:03 +0000

1import dataclasses 

2import decimal 

3import json as _json 

4import typing as t 

5import uuid 

6from datetime import date 

7 

8from jinja2.utils import htmlsafe_json_dumps as _jinja_htmlsafe_dumps 

9from werkzeug.http import http_date 

10 

11from ..globals import current_app 

12from ..globals import request 

13 

14if t.TYPE_CHECKING: 

15 from ..app import Flask 

16 from ..wrappers import Response 

17 

18 

19class JSONEncoder(_json.JSONEncoder): 

20 """The default JSON encoder. Handles extra types compared to the 

21 built-in :class:`json.JSONEncoder`. 

22 

23 - :class:`datetime.datetime` and :class:`datetime.date` are 

24 serialized to :rfc:`822` strings. This is the same as the HTTP 

25 date format. 

26 - :class:`decimal.Decimal` is serialized to a string. 

27 - :class:`uuid.UUID` is serialized to a string. 

28 - :class:`dataclasses.dataclass` is passed to 

29 :func:`dataclasses.asdict`. 

30 - :class:`~markupsafe.Markup` (or any object with a ``__html__`` 

31 method) will call the ``__html__`` method to get a string. 

32 

33 Assign a subclass of this to :attr:`flask.Flask.json_encoder` or 

34 :attr:`flask.Blueprint.json_encoder` to override the default. 

35 """ 

36 

37 def default(self, o: t.Any) -> t.Any: 

38 """Convert ``o`` to a JSON serializable type. See 

39 :meth:`json.JSONEncoder.default`. Python does not support 

40 overriding how basic types like ``str`` or ``list`` are 

41 serialized, they are handled before this method. 

42 """ 

43 if isinstance(o, date): 

44 return http_date(o) 

45 if isinstance(o, (decimal.Decimal, uuid.UUID)): 

46 return str(o) 

47 if dataclasses and dataclasses.is_dataclass(o): 

48 return dataclasses.asdict(o) 

49 if hasattr(o, "__html__"): 

50 return str(o.__html__()) 

51 return super().default(o) 

52 

53 

54class JSONDecoder(_json.JSONDecoder): 

55 """The default JSON decoder. 

56 

57 This does not change any behavior from the built-in 

58 :class:`json.JSONDecoder`. 

59 

60 Assign a subclass of this to :attr:`flask.Flask.json_decoder` or 

61 :attr:`flask.Blueprint.json_decoder` to override the default. 

62 """ 

63 

64 

65def _dump_arg_defaults( 

66 kwargs: t.Dict[str, t.Any], app: t.Optional["Flask"] = None 

67) -> None: 

68 """Inject default arguments for dump functions.""" 

69 if app is None: 

70 app = current_app 

71 

72 if app: 

73 cls = app.json_encoder 

74 bp = app.blueprints.get(request.blueprint) if request else None # type: ignore 

75 if bp is not None and bp.json_encoder is not None: 

76 cls = bp.json_encoder 

77 

78 # Only set a custom encoder if it has custom behavior. This is 

79 # faster on PyPy. 

80 if cls is not _json.JSONEncoder: 

81 kwargs.setdefault("cls", cls) 

82 

83 kwargs.setdefault("cls", cls) 

84 kwargs.setdefault("ensure_ascii", app.config["JSON_AS_ASCII"]) 

85 kwargs.setdefault("sort_keys", app.config["JSON_SORT_KEYS"]) 

86 else: 

87 kwargs.setdefault("sort_keys", True) 

88 kwargs.setdefault("cls", JSONEncoder) 

89 

90 

91def _load_arg_defaults( 

92 kwargs: t.Dict[str, t.Any], app: t.Optional["Flask"] = None 

93) -> None: 

94 """Inject default arguments for load functions.""" 

95 if app is None: 

96 app = current_app 

97 

98 if app: 

99 cls = app.json_decoder 

100 bp = app.blueprints.get(request.blueprint) if request else None # type: ignore 

101 if bp is not None and bp.json_decoder is not None: 

102 cls = bp.json_decoder 

103 

104 # Only set a custom decoder if it has custom behavior. This is 

105 # faster on PyPy. 

106 if cls not in {JSONDecoder, _json.JSONDecoder}: 

107 kwargs.setdefault("cls", cls) 

108 

109 

110def dumps(obj: t.Any, app: t.Optional["Flask"] = None, **kwargs: t.Any) -> str: 

111 """Serialize an object to a string of JSON. 

112 

113 Takes the same arguments as the built-in :func:`json.dumps`, with 

114 some defaults from application configuration. 

115 

116 :param obj: Object to serialize to JSON. 

117 :param app: Use this app's config instead of the active app context 

118 or defaults. 

119 :param kwargs: Extra arguments passed to :func:`json.dumps`. 

120 

121 .. versionchanged:: 2.0.2 

122 :class:`decimal.Decimal` is supported by converting to a string. 

123 

124 .. versionchanged:: 2.0 

125 ``encoding`` is deprecated and will be removed in Flask 2.1. 

126 

127 .. versionchanged:: 1.0.3 

128 ``app`` can be passed directly, rather than requiring an app 

129 context for configuration. 

130 """ 

131 _dump_arg_defaults(kwargs, app=app) 

132 return _json.dumps(obj, **kwargs) 

133 

134 

135def dump( 

136 obj: t.Any, fp: t.IO[str], app: t.Optional["Flask"] = None, **kwargs: t.Any 

137) -> None: 

138 """Serialize an object to JSON written to a file object. 

139 

140 Takes the same arguments as the built-in :func:`json.dump`, with 

141 some defaults from application configuration. 

142 

143 :param obj: Object to serialize to JSON. 

144 :param fp: File object to write JSON to. 

145 :param app: Use this app's config instead of the active app context 

146 or defaults. 

147 :param kwargs: Extra arguments passed to :func:`json.dump`. 

148 

149 .. versionchanged:: 2.0 

150 Writing to a binary file, and the ``encoding`` argument, is 

151 deprecated and will be removed in Flask 2.1. 

152 """ 

153 _dump_arg_defaults(kwargs, app=app) 

154 _json.dump(obj, fp, **kwargs) 

155 

156 

157def loads( 

158 s: t.Union[str, bytes], 

159 app: t.Optional["Flask"] = None, 

160 **kwargs: t.Any, 

161) -> t.Any: 

162 """Deserialize an object from a string of JSON. 

163 

164 Takes the same arguments as the built-in :func:`json.loads`, with 

165 some defaults from application configuration. 

166 

167 :param s: JSON string to deserialize. 

168 :param app: Use this app's config instead of the active app context 

169 or defaults. 

170 :param kwargs: Extra arguments passed to :func:`json.loads`. 

171 

172 .. versionchanged:: 2.0 

173 ``encoding`` is deprecated and will be removed in Flask 2.1. The 

174 data must be a string or UTF-8 bytes. 

175 

176 .. versionchanged:: 1.0.3 

177 ``app`` can be passed directly, rather than requiring an app 

178 context for configuration. 

179 """ 

180 _load_arg_defaults(kwargs, app=app) 

181 return _json.loads(s, **kwargs) 

182 

183 

184def load(fp: t.IO[str], app: t.Optional["Flask"] = None, **kwargs: t.Any) -> t.Any: 

185 """Deserialize an object from JSON read from a file object. 

186 

187 Takes the same arguments as the built-in :func:`json.load`, with 

188 some defaults from application configuration. 

189 

190 :param fp: File object to read JSON from. 

191 :param app: Use this app's config instead of the active app context 

192 or defaults. 

193 :param kwargs: Extra arguments passed to :func:`json.load`. 

194 

195 .. versionchanged:: 2.0 

196 ``encoding`` is deprecated and will be removed in Flask 2.1. The 

197 file must be text mode, or binary mode with UTF-8 bytes. 

198 """ 

199 _load_arg_defaults(kwargs, app=app) 

200 return _json.load(fp, **kwargs) 

201 

202 

203def htmlsafe_dumps(obj: t.Any, **kwargs: t.Any) -> str: 

204 """Serialize an object to a string of JSON with :func:`dumps`, then 

205 replace HTML-unsafe characters with Unicode escapes and mark the 

206 result safe with :class:`~markupsafe.Markup`. 

207 

208 This is available in templates as the ``|tojson`` filter. 

209 

210 The returned string is safe to render in HTML documents and 

211 ``<script>`` tags. The exception is in HTML attributes that are 

212 double quoted; either use single quotes or the ``|forceescape`` 

213 filter. 

214 

215 .. versionchanged:: 2.0 

216 Uses :func:`jinja2.utils.htmlsafe_json_dumps`. The returned 

217 value is marked safe by wrapping in :class:`~markupsafe.Markup`. 

218 

219 .. versionchanged:: 0.10 

220 Single quotes are escaped, making this safe to use in HTML, 

221 ``<script>`` tags, and single-quoted attributes without further 

222 escaping. 

223 """ 

224 return _jinja_htmlsafe_dumps(obj, dumps=dumps, **kwargs) 

225 

226 

227def htmlsafe_dump(obj: t.Any, fp: t.IO[str], **kwargs: t.Any) -> None: 

228 """Serialize an object to JSON written to a file object, replacing 

229 HTML-unsafe characters with Unicode escapes. See 

230 :func:`htmlsafe_dumps` and :func:`dumps`. 

231 """ 

232 fp.write(htmlsafe_dumps(obj, **kwargs)) 

233 

234 

235def jsonify(*args: t.Any, **kwargs: t.Any) -> "Response": 

236 """Serialize data to JSON and wrap it in a :class:`~flask.Response` 

237 with the :mimetype:`application/json` mimetype. 

238 

239 Uses :func:`dumps` to serialize the data, but ``args`` and 

240 ``kwargs`` are treated as data rather than arguments to 

241 :func:`json.dumps`. 

242 

243 1. Single argument: Treated as a single value. 

244 2. Multiple arguments: Treated as a list of values. 

245 ``jsonify(1, 2, 3)`` is the same as ``jsonify([1, 2, 3])``. 

246 3. Keyword arguments: Treated as a dict of values. 

247 ``jsonify(data=data, errors=errors)`` is the same as 

248 ``jsonify({"data": data, "errors": errors})``. 

249 4. Passing both arguments and keyword arguments is not allowed as 

250 it's not clear what should happen. 

251 

252 .. code-block:: python 

253 

254 from flask import jsonify 

255 

256 @app.route("/users/me") 

257 def get_current_user(): 

258 return jsonify( 

259 username=g.user.username, 

260 email=g.user.email, 

261 id=g.user.id, 

262 ) 

263 

264 Will return a JSON response like this: 

265 

266 .. code-block:: javascript 

267 

268 { 

269 "username": "admin", 

270 "email": "admin@localhost", 

271 "id": 42 

272 } 

273 

274 The default output omits indents and spaces after separators. In 

275 debug mode or if :data:`JSONIFY_PRETTYPRINT_REGULAR` is ``True``, 

276 the output will be formatted to be easier to read. 

277 

278 .. versionchanged:: 2.0.2 

279 :class:`decimal.Decimal` is supported by converting to a string. 

280 

281 .. versionchanged:: 0.11 

282 Added support for serializing top-level arrays. This introduces 

283 a security risk in ancient browsers. See :ref:`security-json`. 

284 

285 .. versionadded:: 0.2 

286 """ 

287 indent = None 

288 separators = (",", ":") 

289 

290 if current_app.config["JSONIFY_PRETTYPRINT_REGULAR"] or current_app.debug: 

291 indent = 2 

292 separators = (", ", ": ") 

293 

294 if args and kwargs: 

295 raise TypeError("jsonify() behavior undefined when passed both args and kwargs") 

296 elif len(args) == 1: # single args are passed directly to dumps() 

297 data = args[0] 

298 else: 

299 data = args or kwargs 

300 

301 return current_app.response_class( 

302 f"{dumps(data, indent=indent, separators=separators)}\n", 

303 mimetype=current_app.config["JSONIFY_MIMETYPE"], 

304 )