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

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

61 statements  

1from __future__ import annotations 

2 

3import dataclasses 

4import decimal 

5import json 

6import typing as t 

7import uuid 

8import weakref 

9from datetime import date 

10 

11from werkzeug.http import http_date 

12 

13if t.TYPE_CHECKING: # pragma: no cover 

14 from werkzeug.sansio.response import Response 

15 

16 from ..sansio.app import App 

17 

18 

19class JSONProvider: 

20 """A standard set of JSON operations for an application. Subclasses 

21 of this can be used to customize JSON behavior or use different 

22 JSON libraries. 

23 

24 To implement a provider for a specific library, subclass this base 

25 class and implement at least :meth:`dumps` and :meth:`loads`. All 

26 other methods have default implementations. 

27 

28 To use a different provider, either subclass ``Flask`` and set 

29 :attr:`~flask.Flask.json_provider_class` to a provider class, or set 

30 :attr:`app.json <flask.Flask.json>` to an instance of the class. 

31 

32 :param app: An application instance. This will be stored as a 

33 :class:`weakref.proxy` on the :attr:`_app` attribute. 

34 

35 .. versionadded:: 2.2 

36 """ 

37 

38 def __init__(self, app: App) -> None: 

39 self._app: App = weakref.proxy(app) 

40 

41 def dumps(self, obj: t.Any, **kwargs: t.Any) -> str: 

42 """Serialize data as JSON. 

43 

44 :param obj: The data to serialize. 

45 :param kwargs: May be passed to the underlying JSON library. 

46 """ 

47 raise NotImplementedError 

48 

49 def dump(self, obj: t.Any, fp: t.IO[str], **kwargs: t.Any) -> None: 

50 """Serialize data as JSON and write to a file. 

51 

52 :param obj: The data to serialize. 

53 :param fp: A file opened for writing text. Should use the UTF-8 

54 encoding to be valid JSON. 

55 :param kwargs: May be passed to the underlying JSON library. 

56 """ 

57 fp.write(self.dumps(obj, **kwargs)) 

58 

59 def loads(self, s: str | bytes, **kwargs: t.Any) -> t.Any: 

60 """Deserialize data as JSON. 

61 

62 :param s: Text or UTF-8 bytes. 

63 :param kwargs: May be passed to the underlying JSON library. 

64 """ 

65 raise NotImplementedError 

66 

67 def load(self, fp: t.IO[t.AnyStr], **kwargs: t.Any) -> t.Any: 

68 """Deserialize data as JSON read from a file. 

69 

70 :param fp: A file opened for reading text or UTF-8 bytes. 

71 :param kwargs: May be passed to the underlying JSON library. 

72 """ 

73 return self.loads(fp.read(), **kwargs) 

74 

75 def _prepare_response_obj( 

76 self, args: tuple[t.Any, ...], kwargs: dict[str, t.Any] 

77 ) -> t.Any: 

78 if args and kwargs: 

79 raise TypeError("app.json.response() takes either args or kwargs, not both") 

80 

81 if not args and not kwargs: 

82 return None 

83 

84 if len(args) == 1: 

85 return args[0] 

86 

87 return args or kwargs 

88 

89 def response(self, *args: t.Any, **kwargs: t.Any) -> Response: 

90 """Serialize the given arguments as JSON, and return a 

91 :class:`~flask.Response` object with the ``application/json`` 

92 mimetype. 

93 

94 The :func:`~flask.json.jsonify` function calls this method for 

95 the current application. 

96 

97 Either positional or keyword arguments can be given, not both. 

98 If no arguments are given, ``None`` is serialized. 

99 

100 :param args: A single value to serialize, or multiple values to 

101 treat as a list to serialize. 

102 :param kwargs: Treat as a dict to serialize. 

103 """ 

104 obj = self._prepare_response_obj(args, kwargs) 

105 return self._app.response_class(self.dumps(obj), mimetype="application/json") 

106 

107 

108def _default(o: t.Any) -> t.Any: 

109 if isinstance(o, date): 

110 return http_date(o) 

111 

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

113 return str(o) 

114 

115 if dataclasses and dataclasses.is_dataclass(o): 

116 return dataclasses.asdict(o) 

117 

118 if hasattr(o, "__html__"): 

119 return str(o.__html__()) 

120 

121 raise TypeError(f"Object of type {type(o).__name__} is not JSON serializable") 

122 

123 

124class DefaultJSONProvider(JSONProvider): 

125 """Provide JSON operations using Python's built-in :mod:`json` 

126 library. Serializes the following additional data types: 

127 

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

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

130 date format. 

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

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

133 :func:`dataclasses.asdict`. 

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

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

136 """ 

137 

138 default: t.Callable[[t.Any], t.Any] = staticmethod(_default) # type: ignore[assignment] 

139 """Apply this function to any object that :meth:`json.dumps` does 

140 not know how to serialize. It should return a valid JSON type or 

141 raise a ``TypeError``. 

142 """ 

143 

144 ensure_ascii = True 

145 """Replace non-ASCII characters with escape sequences. This may be 

146 more compatible with some clients, but can be disabled for better 

147 performance and size. 

148 """ 

149 

150 sort_keys = True 

151 """Sort the keys in any serialized dicts. This may be useful for 

152 some caching situations, but can be disabled for better performance. 

153 When enabled, keys must all be strings, they are not converted 

154 before sorting. 

155 """ 

156 

157 compact: bool | None = None 

158 """If ``True``, or ``None`` out of debug mode, the :meth:`response` 

159 output will not add indentation, newlines, or spaces. If ``False``, 

160 or ``None`` in debug mode, it will use a non-compact representation. 

161 """ 

162 

163 mimetype = "application/json" 

164 """The mimetype set in :meth:`response`.""" 

165 

166 def dumps(self, obj: t.Any, **kwargs: t.Any) -> str: 

167 """Serialize data as JSON to a string. 

168 

169 Keyword arguments are passed to :func:`json.dumps`. Sets some 

170 parameter defaults from the :attr:`default`, 

171 :attr:`ensure_ascii`, and :attr:`sort_keys` attributes. 

172 

173 :param obj: The data to serialize. 

174 :param kwargs: Passed to :func:`json.dumps`. 

175 """ 

176 kwargs.setdefault("default", self.default) 

177 kwargs.setdefault("ensure_ascii", self.ensure_ascii) 

178 kwargs.setdefault("sort_keys", self.sort_keys) 

179 return json.dumps(obj, **kwargs) 

180 

181 def loads(self, s: str | bytes, **kwargs: t.Any) -> t.Any: 

182 """Deserialize data as JSON from a string or bytes. 

183 

184 :param s: Text or UTF-8 bytes. 

185 :param kwargs: Passed to :func:`json.loads`. 

186 """ 

187 return json.loads(s, **kwargs) 

188 

189 def response(self, *args: t.Any, **kwargs: t.Any) -> Response: 

190 """Serialize the given arguments as JSON, and return a 

191 :class:`~flask.Response` object with it. The response mimetype 

192 will be "application/json" and can be changed with 

193 :attr:`mimetype`. 

194 

195 If :attr:`compact` is ``False`` or debug mode is enabled, the 

196 output will be formatted to be easier to read. 

197 

198 Either positional or keyword arguments can be given, not both. 

199 If no arguments are given, ``None`` is serialized. 

200 

201 :param args: A single value to serialize, or multiple values to 

202 treat as a list to serialize. 

203 :param kwargs: Treat as a dict to serialize. 

204 """ 

205 obj = self._prepare_response_obj(args, kwargs) 

206 dump_args: dict[str, t.Any] = {} 

207 

208 if (self.compact is None and self._app.debug) or self.compact is False: 

209 dump_args.setdefault("indent", 2) 

210 else: 

211 dump_args.setdefault("separators", (",", ":")) 

212 

213 return self._app.response_class( 

214 f"{self.dumps(obj, **dump_args)}\n", mimetype=self.mimetype 

215 )