Source code for pylav.compat.json

import contextlib
import json
from collections.abc import Callable
from json import JSONDecodeError as JSONDecodeError
from json import JSONDecoder as JSONDecoder
from json import JSONEncoder as JSONEncoder
from types import ModuleType
from typing import IO, Any, AnyStr, overload

try:
    import orjson as _orjson

except ImportError:
    _orjson = None

try:
    import ujson as _ujson
except ImportError:
    _ujson = None


__all__ = [
    "dump",
    "dumps",
    "load",
    "loads",
    "JSONDecoder",
    "JSONDecodeError",
    "JSONEncoder",
    "get_origin",
]

# .dumps() signatures
if _orjson:

    @overload
    def dumps(
        obj: Any,
        *,
        orjson_default: Callable[[Any], Any] | None = None,
        orjson_option: int | None = None,
    ) -> str:
        """Serialize ``obj`` to a JSON formatted ``str``."""
        ...

    __dumps_origin = _orjson

elif _ujson:

    @overload
    def dumps(
        obj: Any,
        *,
        ensure_ascii: bool = True,
        ujson_encode_html_chars: bool = False,
        ujson_escape_forward_slashes: bool = True,
        sort_keys: bool = False,
        indent: int | None = 0,
    ) -> str:
        """Serialize ``obj`` to a JSON formatted ``str``."""
        ...

    __dumps_origin = _ujson

else:

    @overload
    def dumps(
        obj: Any,
        *,
        skipkeys: bool = False,
        ensure_ascii: bool = True,
        check_circular: bool = True,
        allow_nan: bool = True,
        cls: type[JSONEncoder] | None = None,
        indent: None | int | str = None,
        separators: tuple[str, str] | None = None,
        default: Callable[[Any], Any] | None = None,
        sort_keys: bool = False,
        **kwds: Any,
    ) -> str:
        """Serialize ``obj`` to a JSON formatted ``str``."""
        ...

    __dumps_origin = json


# .loads() signatures
if _orjson:

    @overload
    def loads(obj: bytes | bytearray | memoryview | str) -> Any:
        """Deserialize ``obj`` (a ``str``, ``bytes`` or ``bytearray`` instance"""
        ...

    __loads_origin = _orjson

elif _ujson:

    @overload
    def loads(obj: AnyStr, *, ujson_precise_float: bool | None = None) -> Any:
        """Deserialize ``obj`` (a ``str``, ``bytes`` or ``bytearray`` instance"""
        ...

    __loads_origin = _ujson

else:

    @overload
    def loads(
        obj: AnyStr,
        *,
        cls: type[JSONDecoder] | None = None,
        object_hook: Callable[[dict[Any, Any]], Any] | None = None,
        parse_float: Callable[[str], Any] | None = None,
        parse_int: Callable[[str], Any] | None = None,
        parse_constant: Callable[[str], Any] | None = None,
        object_pairs_hook: Callable[[list[tuple[Any, Any]]], Any] | None = None,
        **kwargs: Any,
    ) -> Any:
        """Deserialize ``obj`` (a ``str``, ``bytes`` or ``bytearray`` instance"""
        ...

    __loads_origin = json


# .dump() signatures
if _orjson:

    @overload
    def dump(
        obj: Any,
        fp: IO[str],
    ) -> None:
        """Serialize ``obj`` as a JSON formatted stream to ``fp`` (a ``.write()``-supporting file-like object)."""
        ...

    __dump_origin = _orjson

elif _ujson:

    @overload
    def dump(
        obj: Any,
        fp: IO[str],
        *,
        ensure_ascii: bool = True,
        sort_keys: bool = False,
        indent: int = 0,
        ujson_encode_html_chars: bool = False,
        ujson_escape_forward_slashes: bool = True,
    ) -> None:
        """Serialize ``obj`` as a JSON formatted stream to ``fp`` (a ``.write()``-supporting file-like object)."""
        ...

    __dump_origin = _ujson

else:

    @overload
    def dump(
        obj: Any,
        fp: IO[str],
        *,
        skipkeys: bool = False,
        ensure_ascii: bool = True,
        check_circular: bool = True,
        allow_nan: bool = True,
        cls: type[JSONEncoder] | None = None,
        indent: None | int | str = None,
        separators: tuple[str, str] | None = None,
        default: Callable[[Any], Any] | None = None,
        sort_keys: bool = False,
        **kwargs: Any,
    ) -> None:
        """Serialize ``obj`` as a JSON formatted stream to ``fp`` (a ``.write()``-supporting file-like object)."""
        ...

    __dump_origin = json


# .load() signatures

if _orjson:

    @overload
    def load(fp: IO[AnyStr]) -> Any:
        """Deserialize ``fp`` (a ``.read()``-supporting file-like object containing a JSON document) to a Python object."""
        ...

    __load_origin = _orjson

elif _ujson:

    @overload
    def load(
        fp: IO[AnyStr],
        *,
        precise_float: bool = ...,
    ) -> Any:
        """Deserialize ``fp`` (a ``.read()``-supporting file-like object containing a JSON document) to a Python object."""
        ...

    __load_origin = _ujson

else:

    @overload
    def load(
        fp: IO[AnyStr],
        *,
        cls: type[JSONDecoder] | None = ...,
        object_hook: Callable[[Any], Any] | None = ...,
        parse_float: Callable[[str], float] | None = ...,
        parse_int: Callable[[str], int] | None = ...,
        parse_constant: Callable[[str], Any] | None = ...,
        object_pairs_hook: Callable[[list[tuple[str, Any]]], Any] | None = ...,
        **kwargs: Any,
    ) -> Any:
        """Deserialize ``fp`` (a ``.read()``-supporting file-like object containing a JSON document) to a Python object."""
        ...

    __load_origin = json


[docs] def dumps( obj: Any, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, orjson_default=None, orjson_option=None, ujson_encode_html_chars=False, ujson_escape_forward_slashes=True, **kwargs, ) -> str: """ Serialize ``obj`` to a JSON formatted ``str``. Parameters ---------- obj : Any The object to serialize. skipkeys : bool, optional If ``True`` (default: ``False``), keys that are not basic types (``str``, ``int``, ``float``, ``bool``, ``None``) will be skipped instead of raising a ``TypeError``. ensure_ascii: bool, optional If ``True`` (default: ``True``), the output is guaranteed to have all incoming non-ASCII characters escaped. If ``False``, these characters will be output as-is. check_circular: bool, optional If check_circular is ``False`` (default: ``True``), then the circular reference check for container types will be skipped and a circular reference will result in an ``RecursionError`` (or worse). allow_nan: bool, optional If ``False`` (default: ``True``), then it will be a ValueError to serialize out of range float values (`nan`, `inf`, `-inf`) in strict compliance of the JSON specification. If ``True``, their JavaScript equivalents (`NaN`, `Infinity`, `-Infinity`) will be used. cls : type, optional If specified, must be a subclass of ``json.JSONEncoder``. An instance is used to encode the object. indent : int or str, optional If specified, then JSON array elements and object members will be pretty-printed with a newline followed by that many spaces. An indent level of 0, negative, or ``None`` will only insert newlines. ``None`` is the most compact representation. Using a negative indent indents that many spaces after the ``newline`` character. If it is a string (such as ``'\t'``), that string is used to indent each level. Default: ``None``. separators : tuple, optional If specified, then it should be an (item_separator, key_separator) tuple. The default is ``(', ', ': ')`` if *indent* is ``None`` and ``(',', ': ')`` otherwise. To get the most compact JSON representation, you should specify ``(',', ':')`` to eliminate whitespace. default: Callable, optional If specified, then it should be a function that gets called for objects that can't otherwise be serialized. It should return a JSON encodable version of the object or raise a ``TypeError``. If not specified, ``TypeError`` is raised. sort_keys: bool, optional If ``True`` (default: ``False``), then the output of dictionaries will be sorted by key. orjson_default: Callable, optional If specified, then it should be a function that gets called for objects that can't otherwise be serialized. It should return a JSON encodable version of the object or raise a ``TypeError``. If not specified, ``TypeError`` is raised. orjson_option: int, optional If specified, then it should be an integer that is passed to ``orjson.dumps`` as the ``option`` parameter. If not specified, ``None`` is used. ujson_encode_html_chars: bool, optional If ``True`` (default: ``False``), then the output will have the characters ``<``, ``>``, ``&`` encoded as ``\u003c``, ``\u003e``, ``\u0026``. ujson_escape_forward_slashes: If ``True`` (default: ``True``), then the output will have the forward slash character ``/`` encoded as ``\\/``. kwargs: Any, optional Additional keyword arguments are passed to ``json.dumps``. Returns ------- str The JSON string representation of ``obj``. """ if _orjson: with contextlib.suppress(_orjson.JSONEncodeError): return _orjson.dumps(obj, default=orjson_default, option=orjson_option).decode() if _ujson: return _ujson.dumps( obj, ensure_ascii=ensure_ascii, sort_keys=sort_keys, indent=indent or 0, encode_html_chars=ujson_encode_html_chars, escape_forward_slashes=ujson_escape_forward_slashes, ) return json.dumps( obj, skipkeys=skipkeys, ensure_ascii=ensure_ascii, check_circular=check_circular, allow_nan=allow_nan, cls=cls, indent=indent, separators=separators, default=default, sort_keys=sort_keys, **kwargs, )
[docs] def loads( obj: AnyStr | bytes | bytearray | memoryview | str, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, ujson_precise_float=None, **kwargs, ) -> Any: """Deserialize ``obj`` (a ``str``, ``bytes`` or ``bytearray`` instance containing a JSON document) to a Python object. Parameters ---------- obj: AnyStr | bytes | bytearray | memoryview | str The JSON string to deserialize. cls: type, optional If specified, must be a subclass of ``json.JSONDecoder``. An instance is used to decode the object. object_hook: Callable, optional If specified, then it should be a function that will be called with the result of any object literal decoded (a ``dict``). The return value of ``object_hook`` will be used instead of the ``dict``. This feature can be used to implement custom decoders (e.g. JSON-RPC class hinting). parse_float: Callable, optional If specified, then it should be a function to be called with the string of every JSON float to be decoded. By default this is equivalent to ``float(num_str)``. This can be used to use another datatype or parser for JSON floats (e.g. ``decimal.Decimal``). parse_int: Callable, optional If specified, then it should be a function to be called with the string of every JSON int to be decoded. By default this is equivalent to ``int(num_str)``. This can be used to use another datatype or parser for JSON integers (e.g. ``float``). parse_constant: Callable, optional If specified, then it should be a function to be called with one of the following strings: ``'-Infinity'``, ``'Infinity'``, ``'NaN'``. This can be used to raise an exception if invalid JSON numbers are encountered. object_pairs_hook: Callable, optional If specified, then it should be a function that will be called with the result of any object literal decoded with an ordered list of pairs. The return value of ``object_pairs_hook`` will be used instead of the ``dict``. This feature can be used to implement custom decoders. If ``object_hook`` is also defined, the ``object_pairs_hook`` takes priority. ujson_precise_float: bool, optional If ``True`` (default: ``False``), then kwargs: Any, optional Additional keyword arguments are passed to ``json.loads``. Returns ------- Any The deserialized object. Raises ------ json.JSONDecodeError If the input is not valid JSON. orjson.JSONDecodeError(json.JSONDecodeError, ValueError) If the input is not valid JSON and orjson is used. ValueError If the string is not correctly formed and ujson is used. """ if _orjson: with contextlib.suppress(_orjson.JSONDecodeError): return _orjson.loads(obj) if _ujson: with contextlib.suppress(_ujson.JSONDecodeError): return _ujson.loads(obj, precise_float=ujson_precise_float) return json.loads( obj, cls=cls, object_hook=object_hook, parse_float=parse_float, parse_int=parse_int, parse_constant=parse_constant, object_pairs_hook=object_pairs_hook, **kwargs, )
[docs] def dump( obj: Any, fp: IO[AnyStr], *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, ujson_encode_html_chars=False, ujson_escape_forward_slashes=True, **kwargs, ) -> None: """Serialize ``obj`` as a JSON formatted stream to ``fp`` (a ``.write()``-supporting file-like object).""" if _orjson: with contextlib.suppress(_orjson.JSONEncodeError): fp.write(_orjson.dumps(obj).decode()) return if _ujson: return _ujson.dump( obj, fp, ensure_ascii=ensure_ascii, sort_keys=sort_keys, indent=indent or 0, encode_html_chars=ujson_encode_html_chars, escape_forward_slashes=ujson_escape_forward_slashes, ) return json.dump( obj, fp, skipkeys=skipkeys, ensure_ascii=ensure_ascii, check_circular=check_circular, allow_nan=allow_nan, cls=cls, indent=indent, separators=separators, default=default, sort_keys=sort_keys, **kwargs, )
[docs] def load( fp: IO[AnyStr], *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, precise_float=None, **kwargs, ) -> Any: """Deserialize ``fp`` (a ``.read()``-supporting file-like object containing a JSON document) to a Python object.""" if _orjson: with contextlib.suppress(_orjson.JSONDecodeError): return _orjson.loads(fp.read()) if _ujson: with contextlib.suppress(_ujson.JSONDecodeError): return _ujson.load(fp, precise_float=precise_float) return json.load( fp, cls=cls, object_hook=object_hook, parse_float=parse_float, parse_int=parse_int, parse_constant=parse_constant, object_pairs_hook=object_pairs_hook, **kwargs, )
[docs] def get_origin() -> dict[str, ModuleType]: """Return a dict of json modules being used.""" return { "dumps": __dumps_origin, "loads": __loads_origin, "dump": __dump_origin, "load": __load_origin, }