__init__.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. import dataclasses
  2. import decimal
  3. import json as _json
  4. import typing as t
  5. import uuid
  6. from datetime import date
  7. from jinja2.utils import htmlsafe_json_dumps as _jinja_htmlsafe_dumps
  8. from werkzeug.http import http_date
  9. from ..globals import current_app
  10. from ..globals import request
  11. if t.TYPE_CHECKING:
  12. from ..app import Flask
  13. from ..wrappers import Response
  14. class JSONEncoder(_json.JSONEncoder):
  15. """The default JSON encoder. Handles extra types compared to the
  16. built-in :class:`json.JSONEncoder`.
  17. - :class:`datetime.datetime` and :class:`datetime.date` are
  18. serialized to :rfc:`822` strings. This is the same as the HTTP
  19. date format.
  20. - :class:`decimal.Decimal` is serialized to a string.
  21. - :class:`uuid.UUID` is serialized to a string.
  22. - :class:`dataclasses.dataclass` is passed to
  23. :func:`dataclasses.asdict`.
  24. - :class:`~markupsafe.Markup` (or any object with a ``__html__``
  25. method) will call the ``__html__`` method to get a string.
  26. Assign a subclass of this to :attr:`flask.Flask.json_encoder` or
  27. :attr:`flask.Blueprint.json_encoder` to override the default.
  28. """
  29. def default(self, o: t.Any) -> t.Any:
  30. """Convert ``o`` to a JSON serializable type. See
  31. :meth:`json.JSONEncoder.default`. Python does not support
  32. overriding how basic types like ``str`` or ``list`` are
  33. serialized, they are handled before this method.
  34. """
  35. if isinstance(o, date):
  36. return http_date(o)
  37. if isinstance(o, (decimal.Decimal, uuid.UUID)):
  38. return str(o)
  39. if dataclasses and dataclasses.is_dataclass(o):
  40. return dataclasses.asdict(o)
  41. if hasattr(o, "__html__"):
  42. return str(o.__html__())
  43. return super().default(o)
  44. class JSONDecoder(_json.JSONDecoder):
  45. """The default JSON decoder.
  46. This does not change any behavior from the built-in
  47. :class:`json.JSONDecoder`.
  48. Assign a subclass of this to :attr:`flask.Flask.json_decoder` or
  49. :attr:`flask.Blueprint.json_decoder` to override the default.
  50. """
  51. def _dump_arg_defaults(
  52. kwargs: t.Dict[str, t.Any], app: t.Optional["Flask"] = None
  53. ) -> None:
  54. """Inject default arguments for dump functions."""
  55. if app is None:
  56. app = current_app
  57. if app:
  58. cls = app.json_encoder
  59. bp = app.blueprints.get(request.blueprint) if request else None # type: ignore
  60. if bp is not None and bp.json_encoder is not None:
  61. cls = bp.json_encoder
  62. # Only set a custom encoder if it has custom behavior. This is
  63. # faster on PyPy.
  64. if cls is not _json.JSONEncoder:
  65. kwargs.setdefault("cls", cls)
  66. kwargs.setdefault("cls", cls)
  67. kwargs.setdefault("ensure_ascii", app.config["JSON_AS_ASCII"])
  68. kwargs.setdefault("sort_keys", app.config["JSON_SORT_KEYS"])
  69. else:
  70. kwargs.setdefault("sort_keys", True)
  71. kwargs.setdefault("cls", JSONEncoder)
  72. def _load_arg_defaults(
  73. kwargs: t.Dict[str, t.Any], app: t.Optional["Flask"] = None
  74. ) -> None:
  75. """Inject default arguments for load functions."""
  76. if app is None:
  77. app = current_app
  78. if app:
  79. cls = app.json_decoder
  80. bp = app.blueprints.get(request.blueprint) if request else None # type: ignore
  81. if bp is not None and bp.json_decoder is not None:
  82. cls = bp.json_decoder
  83. # Only set a custom decoder if it has custom behavior. This is
  84. # faster on PyPy.
  85. if cls not in {JSONDecoder, _json.JSONDecoder}:
  86. kwargs.setdefault("cls", cls)
  87. def dumps(obj: t.Any, app: t.Optional["Flask"] = None, **kwargs: t.Any) -> str:
  88. """Serialize an object to a string of JSON.
  89. Takes the same arguments as the built-in :func:`json.dumps`, with
  90. some defaults from application configuration.
  91. :param obj: Object to serialize to JSON.
  92. :param app: Use this app's config instead of the active app context
  93. or defaults.
  94. :param kwargs: Extra arguments passed to :func:`json.dumps`.
  95. .. versionchanged:: 2.0.2
  96. :class:`decimal.Decimal` is supported by converting to a string.
  97. .. versionchanged:: 2.0
  98. ``encoding`` is deprecated and will be removed in Flask 2.1.
  99. .. versionchanged:: 1.0.3
  100. ``app`` can be passed directly, rather than requiring an app
  101. context for configuration.
  102. """
  103. _dump_arg_defaults(kwargs, app=app)
  104. return _json.dumps(obj, **kwargs)
  105. def dump(
  106. obj: t.Any, fp: t.IO[str], app: t.Optional["Flask"] = None, **kwargs: t.Any
  107. ) -> None:
  108. """Serialize an object to JSON written to a file object.
  109. Takes the same arguments as the built-in :func:`json.dump`, with
  110. some defaults from application configuration.
  111. :param obj: Object to serialize to JSON.
  112. :param fp: File object to write JSON to.
  113. :param app: Use this app's config instead of the active app context
  114. or defaults.
  115. :param kwargs: Extra arguments passed to :func:`json.dump`.
  116. .. versionchanged:: 2.0
  117. Writing to a binary file, and the ``encoding`` argument, is
  118. deprecated and will be removed in Flask 2.1.
  119. """
  120. _dump_arg_defaults(kwargs, app=app)
  121. _json.dump(obj, fp, **kwargs)
  122. def loads(
  123. s: t.Union[str, bytes],
  124. app: t.Optional["Flask"] = None,
  125. **kwargs: t.Any,
  126. ) -> t.Any:
  127. """Deserialize an object from a string of JSON.
  128. Takes the same arguments as the built-in :func:`json.loads`, with
  129. some defaults from application configuration.
  130. :param s: JSON string to deserialize.
  131. :param app: Use this app's config instead of the active app context
  132. or defaults.
  133. :param kwargs: Extra arguments passed to :func:`json.loads`.
  134. .. versionchanged:: 2.0
  135. ``encoding`` is deprecated and will be removed in Flask 2.1. The
  136. data must be a string or UTF-8 bytes.
  137. .. versionchanged:: 1.0.3
  138. ``app`` can be passed directly, rather than requiring an app
  139. context for configuration.
  140. """
  141. _load_arg_defaults(kwargs, app=app)
  142. return _json.loads(s, **kwargs)
  143. def load(fp: t.IO[str], app: t.Optional["Flask"] = None, **kwargs: t.Any) -> t.Any:
  144. """Deserialize an object from JSON read from a file object.
  145. Takes the same arguments as the built-in :func:`json.load`, with
  146. some defaults from application configuration.
  147. :param fp: File object to read JSON from.
  148. :param app: Use this app's config instead of the active app context
  149. or defaults.
  150. :param kwargs: Extra arguments passed to :func:`json.load`.
  151. .. versionchanged:: 2.0
  152. ``encoding`` is deprecated and will be removed in Flask 2.1. The
  153. file must be text mode, or binary mode with UTF-8 bytes.
  154. """
  155. _load_arg_defaults(kwargs, app=app)
  156. return _json.load(fp, **kwargs)
  157. def htmlsafe_dumps(obj: t.Any, **kwargs: t.Any) -> str:
  158. """Serialize an object to a string of JSON with :func:`dumps`, then
  159. replace HTML-unsafe characters with Unicode escapes and mark the
  160. result safe with :class:`~markupsafe.Markup`.
  161. This is available in templates as the ``|tojson`` filter.
  162. The returned string is safe to render in HTML documents and
  163. ``<script>`` tags. The exception is in HTML attributes that are
  164. double quoted; either use single quotes or the ``|forceescape``
  165. filter.
  166. .. versionchanged:: 2.0
  167. Uses :func:`jinja2.utils.htmlsafe_json_dumps`. The returned
  168. value is marked safe by wrapping in :class:`~markupsafe.Markup`.
  169. .. versionchanged:: 0.10
  170. Single quotes are escaped, making this safe to use in HTML,
  171. ``<script>`` tags, and single-quoted attributes without further
  172. escaping.
  173. """
  174. return _jinja_htmlsafe_dumps(obj, dumps=dumps, **kwargs)
  175. def htmlsafe_dump(obj: t.Any, fp: t.IO[str], **kwargs: t.Any) -> None:
  176. """Serialize an object to JSON written to a file object, replacing
  177. HTML-unsafe characters with Unicode escapes. See
  178. :func:`htmlsafe_dumps` and :func:`dumps`.
  179. """
  180. fp.write(htmlsafe_dumps(obj, **kwargs))
  181. def jsonify(*args: t.Any, **kwargs: t.Any) -> "Response":
  182. """Serialize data to JSON and wrap it in a :class:`~flask.Response`
  183. with the :mimetype:`application/json` mimetype.
  184. Uses :func:`dumps` to serialize the data, but ``args`` and
  185. ``kwargs`` are treated as data rather than arguments to
  186. :func:`json.dumps`.
  187. 1. Single argument: Treated as a single value.
  188. 2. Multiple arguments: Treated as a list of values.
  189. ``jsonify(1, 2, 3)`` is the same as ``jsonify([1, 2, 3])``.
  190. 3. Keyword arguments: Treated as a dict of values.
  191. ``jsonify(data=data, errors=errors)`` is the same as
  192. ``jsonify({"data": data, "errors": errors})``.
  193. 4. Passing both arguments and keyword arguments is not allowed as
  194. it's not clear what should happen.
  195. .. code-block:: python
  196. from flask import jsonify
  197. @app.route("/users/me")
  198. def get_current_user():
  199. return jsonify(
  200. username=g.user.username,
  201. email=g.user.email,
  202. id=g.user.id,
  203. )
  204. Will return a JSON response like this:
  205. .. code-block:: javascript
  206. {
  207. "username": "admin",
  208. "email": "admin@localhost",
  209. "id": 42
  210. }
  211. The default output omits indents and spaces after separators. In
  212. debug mode or if :data:`JSONIFY_PRETTYPRINT_REGULAR` is ``True``,
  213. the output will be formatted to be easier to read.
  214. .. versionchanged:: 2.0.2
  215. :class:`decimal.Decimal` is supported by converting to a string.
  216. .. versionchanged:: 0.11
  217. Added support for serializing top-level arrays. This introduces
  218. a security risk in ancient browsers. See :ref:`security-json`.
  219. .. versionadded:: 0.2
  220. """
  221. indent = None
  222. separators = (",", ":")
  223. if current_app.config["JSONIFY_PRETTYPRINT_REGULAR"] or current_app.debug:
  224. indent = 2
  225. separators = (", ", ": ")
  226. if args and kwargs:
  227. raise TypeError("jsonify() behavior undefined when passed both args and kwargs")
  228. elif len(args) == 1: # single args are passed directly to dumps()
  229. data = args[0]
  230. else:
  231. data = args or kwargs
  232. return current_app.response_class(
  233. f"{dumps(data, indent=indent, separators=separators)}\n",
  234. mimetype=current_app.config["JSONIFY_MIMETYPE"],
  235. )