diff options
author | robot-piglet <robot-piglet@yandex-team.com> | 2025-01-16 19:09:30 +0300 |
---|---|---|
committer | robot-piglet <robot-piglet@yandex-team.com> | 2025-01-16 19:38:51 +0300 |
commit | 7a23ad1fa2a5561a3575177d7240d8a1aa499718 (patch) | |
tree | b4932bad31f595149e7a42e88cf729919995d735 /contrib/python/Flask/py3/flask/json/provider.py | |
parent | fbb15f5ab8a61fc7c50500e2757af0b47174d825 (diff) | |
download | ydb-7a23ad1fa2a5561a3575177d7240d8a1aa499718.tar.gz |
Intermediate changes
commit_hash:ae9e37c897fc6d514389f7089184df33bf781005
Diffstat (limited to 'contrib/python/Flask/py3/flask/json/provider.py')
-rw-r--r-- | contrib/python/Flask/py3/flask/json/provider.py | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/contrib/python/Flask/py3/flask/json/provider.py b/contrib/python/Flask/py3/flask/json/provider.py new file mode 100644 index 00000000000..cb6aae809be --- /dev/null +++ b/contrib/python/Flask/py3/flask/json/provider.py @@ -0,0 +1,310 @@ +from __future__ import annotations + +import dataclasses +import decimal +import json +import typing as t +import uuid +import weakref +from datetime import date + +from werkzeug.http import http_date + +from ..globals import request + +if t.TYPE_CHECKING: # pragma: no cover + from ..app import Flask + from ..wrappers import Response + + +class JSONProvider: + """A standard set of JSON operations for an application. Subclasses + of this can be used to customize JSON behavior or use different + JSON libraries. + + To implement a provider for a specific library, subclass this base + class and implement at least :meth:`dumps` and :meth:`loads`. All + other methods have default implementations. + + To use a different provider, either subclass ``Flask`` and set + :attr:`~flask.Flask.json_provider_class` to a provider class, or set + :attr:`app.json <flask.Flask.json>` to an instance of the class. + + :param app: An application instance. This will be stored as a + :class:`weakref.proxy` on the :attr:`_app` attribute. + + .. versionadded:: 2.2 + """ + + def __init__(self, app: Flask) -> None: + self._app = weakref.proxy(app) + + def dumps(self, obj: t.Any, **kwargs: t.Any) -> str: + """Serialize data as JSON. + + :param obj: The data to serialize. + :param kwargs: May be passed to the underlying JSON library. + """ + raise NotImplementedError + + def dump(self, obj: t.Any, fp: t.IO[str], **kwargs: t.Any) -> None: + """Serialize data as JSON and write to a file. + + :param obj: The data to serialize. + :param fp: A file opened for writing text. Should use the UTF-8 + encoding to be valid JSON. + :param kwargs: May be passed to the underlying JSON library. + """ + fp.write(self.dumps(obj, **kwargs)) + + def loads(self, s: str | bytes, **kwargs: t.Any) -> t.Any: + """Deserialize data as JSON. + + :param s: Text or UTF-8 bytes. + :param kwargs: May be passed to the underlying JSON library. + """ + raise NotImplementedError + + def load(self, fp: t.IO[t.AnyStr], **kwargs: t.Any) -> t.Any: + """Deserialize data as JSON read from a file. + + :param fp: A file opened for reading text or UTF-8 bytes. + :param kwargs: May be passed to the underlying JSON library. + """ + return self.loads(fp.read(), **kwargs) + + def _prepare_response_obj( + self, args: t.Tuple[t.Any, ...], kwargs: t.Dict[str, t.Any] + ) -> t.Any: + if args and kwargs: + raise TypeError("app.json.response() takes either args or kwargs, not both") + + if not args and not kwargs: + return None + + if len(args) == 1: + return args[0] + + return args or kwargs + + def response(self, *args: t.Any, **kwargs: t.Any) -> Response: + """Serialize the given arguments as JSON, and return a + :class:`~flask.Response` object with the ``application/json`` + mimetype. + + The :func:`~flask.json.jsonify` function calls this method for + the current application. + + Either positional or keyword arguments can be given, not both. + If no arguments are given, ``None`` is serialized. + + :param args: A single value to serialize, or multiple values to + treat as a list to serialize. + :param kwargs: Treat as a dict to serialize. + """ + obj = self._prepare_response_obj(args, kwargs) + return self._app.response_class(self.dumps(obj), mimetype="application/json") + + +def _default(o: t.Any) -> t.Any: + if isinstance(o, date): + return http_date(o) + + if isinstance(o, (decimal.Decimal, uuid.UUID)): + return str(o) + + if dataclasses and dataclasses.is_dataclass(o): + return dataclasses.asdict(o) + + if hasattr(o, "__html__"): + return str(o.__html__()) + + raise TypeError(f"Object of type {type(o).__name__} is not JSON serializable") + + +class DefaultJSONProvider(JSONProvider): + """Provide JSON operations using Python's built-in :mod:`json` + library. Serializes the following additional data types: + + - :class:`datetime.datetime` and :class:`datetime.date` are + serialized to :rfc:`822` strings. This is the same as the HTTP + date format. + - :class:`uuid.UUID` is serialized to a string. + - :class:`dataclasses.dataclass` is passed to + :func:`dataclasses.asdict`. + - :class:`~markupsafe.Markup` (or any object with a ``__html__`` + method) will call the ``__html__`` method to get a string. + """ + + default: t.Callable[[t.Any], t.Any] = staticmethod( + _default + ) # type: ignore[assignment] + """Apply this function to any object that :meth:`json.dumps` does + not know how to serialize. It should return a valid JSON type or + raise a ``TypeError``. + """ + + ensure_ascii = True + """Replace non-ASCII characters with escape sequences. This may be + more compatible with some clients, but can be disabled for better + performance and size. + """ + + sort_keys = True + """Sort the keys in any serialized dicts. This may be useful for + some caching situations, but can be disabled for better performance. + When enabled, keys must all be strings, they are not converted + before sorting. + """ + + compact: bool | None = None + """If ``True``, or ``None`` out of debug mode, the :meth:`response` + output will not add indentation, newlines, or spaces. If ``False``, + or ``None`` in debug mode, it will use a non-compact representation. + """ + + mimetype = "application/json" + """The mimetype set in :meth:`response`.""" + + def dumps(self, obj: t.Any, **kwargs: t.Any) -> str: + """Serialize data as JSON to a string. + + Keyword arguments are passed to :func:`json.dumps`. Sets some + parameter defaults from the :attr:`default`, + :attr:`ensure_ascii`, and :attr:`sort_keys` attributes. + + :param obj: The data to serialize. + :param kwargs: Passed to :func:`json.dumps`. + """ + cls = self._app._json_encoder + bp = self._app.blueprints.get(request.blueprint) if request else None + + if bp is not None and bp._json_encoder is not None: + cls = bp._json_encoder + + if cls is not None: + import warnings + + warnings.warn( + "Setting 'json_encoder' on the app or a blueprint is" + " deprecated and will be removed in Flask 2.3." + " Customize 'app.json' instead.", + DeprecationWarning, + ) + kwargs.setdefault("cls", cls) + + if "default" not in cls.__dict__: + kwargs.setdefault("default", self.default) + else: + kwargs.setdefault("default", self.default) + + ensure_ascii = self._app.config["JSON_AS_ASCII"] + sort_keys = self._app.config["JSON_SORT_KEYS"] + + if ensure_ascii is not None: + import warnings + + warnings.warn( + "The 'JSON_AS_ASCII' config key is deprecated and will" + " be removed in Flask 2.3. Set 'app.json.ensure_ascii'" + " instead.", + DeprecationWarning, + ) + else: + ensure_ascii = self.ensure_ascii + + if sort_keys is not None: + import warnings + + warnings.warn( + "The 'JSON_SORT_KEYS' config key is deprecated and will" + " be removed in Flask 2.3. Set 'app.json.sort_keys'" + " instead.", + DeprecationWarning, + ) + else: + sort_keys = self.sort_keys + + kwargs.setdefault("ensure_ascii", ensure_ascii) + kwargs.setdefault("sort_keys", sort_keys) + return json.dumps(obj, **kwargs) + + def loads(self, s: str | bytes, **kwargs: t.Any) -> t.Any: + """Deserialize data as JSON from a string or bytes. + + :param s: Text or UTF-8 bytes. + :param kwargs: Passed to :func:`json.loads`. + """ + cls = self._app._json_decoder + bp = self._app.blueprints.get(request.blueprint) if request else None + + if bp is not None and bp._json_decoder is not None: + cls = bp._json_decoder + + if cls is not None: + import warnings + + warnings.warn( + "Setting 'json_decoder' on the app or a blueprint is" + " deprecated and will be removed in Flask 2.3." + " Customize 'app.json' instead.", + DeprecationWarning, + ) + kwargs.setdefault("cls", cls) + + return json.loads(s, **kwargs) + + def response(self, *args: t.Any, **kwargs: t.Any) -> Response: + """Serialize the given arguments as JSON, and return a + :class:`~flask.Response` object with it. The response mimetype + will be "application/json" and can be changed with + :attr:`mimetype`. + + If :attr:`compact` is ``False`` or debug mode is enabled, the + output will be formatted to be easier to read. + + Either positional or keyword arguments can be given, not both. + If no arguments are given, ``None`` is serialized. + + :param args: A single value to serialize, or multiple values to + treat as a list to serialize. + :param kwargs: Treat as a dict to serialize. + """ + obj = self._prepare_response_obj(args, kwargs) + dump_args: t.Dict[str, t.Any] = {} + pretty = self._app.config["JSONIFY_PRETTYPRINT_REGULAR"] + mimetype = self._app.config["JSONIFY_MIMETYPE"] + + if pretty is not None: + import warnings + + warnings.warn( + "The 'JSONIFY_PRETTYPRINT_REGULAR' config key is" + " deprecated and will be removed in Flask 2.3. Set" + " 'app.json.compact' instead.", + DeprecationWarning, + ) + compact: bool | None = not pretty + else: + compact = self.compact + + if (compact is None and self._app.debug) or compact is False: + dump_args.setdefault("indent", 2) + else: + dump_args.setdefault("separators", (",", ":")) + + if mimetype is not None: + import warnings + + warnings.warn( + "The 'JSONIFY_MIMETYPE' config key is deprecated and" + " will be removed in Flask 2.3. Set 'app.json.mimetype'" + " instead.", + DeprecationWarning, + ) + else: + mimetype = self.mimetype + + return self._app.response_class( + f"{self.dumps(obj, **dump_args)}\n", mimetype=mimetype + ) |