Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/nbclient/jsonutil.py: 40%
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
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
1"""Utilities to manipulate JSON objects."""
3# NOTE: this is a copy of ipykernel/jsonutils.py (+blackified)
5# Copyright (c) IPython Development Team.
6# Distributed under the terms of the Modified BSD License.
7from __future__ import annotations
9import math
10import numbers
11import re
12import types
13from binascii import b2a_base64
14from datetime import datetime
15from typing import Any
17# -----------------------------------------------------------------------------
18# Globals and constants
19# -----------------------------------------------------------------------------
21# timestamp formats
22ISO8601 = "%Y-%m-%dT%H:%M:%S.%f"
23ISO8601_PAT = re.compile(
24 r"^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(\.\d{1,6})?Z?([\+\-]\d{2}:?\d{2})?$"
25)
27# holy crap, strptime is not threadsafe.
28# Calling it once at import seems to help.
29datetime.strptime("2000-01-01", "%Y-%m-%d")
31# -----------------------------------------------------------------------------
32# Classes and functions
33# -----------------------------------------------------------------------------
36# constants for identifying png/jpeg data
37PNG = b"\x89PNG\r\n\x1a\n"
38# front of PNG base64-encoded
39PNG64 = b"iVBORw0KG"
40JPEG = b"\xff\xd8"
41# front of JPEG base64-encoded
42JPEG64 = b"/9"
43# constants for identifying gif data
44GIF_64 = b"R0lGODdh"
45GIF89_64 = b"R0lGODlh"
46# front of PDF base64-encoded
47PDF64 = b"JVBER"
50def encode_images(format_dict: dict[str, str]) -> dict[str, str]:
51 """b64-encodes images in a displaypub format dict
53 Perhaps this should be handled in json_clean itself?
55 Parameters
56 ----------
58 format_dict : dict
59 A dictionary of display data keyed by mime-type
61 Returns
62 -------
64 format_dict : dict
65 A copy of the same dictionary,
66 but binary image data ('image/png', 'image/jpeg' or 'application/pdf')
67 is base64-encoded.
69 """
70 return format_dict
73def json_clean(obj: Any) -> Any:
74 """Clean an object to ensure it's safe to encode in JSON.
76 Atomic, immutable objects are returned unmodified. Sets and tuples are
77 converted to lists, lists are copied and dicts are also copied.
79 Note: dicts whose keys could cause collisions upon encoding (such as a dict
80 with both the number 1 and the string '1' as keys) will cause a ValueError
81 to be raised.
83 Parameters
84 ----------
85 obj : any python object
87 Returns
88 -------
89 out : object
91 A version of the input which will not cause an encoding error when
92 encoded as JSON. Note that this function does not *encode* its inputs,
93 it simply sanitizes it so that there will be no encoding errors later.
95 """
96 # types that are 'atomic' and ok in json as-is.
97 atomic_ok = (str, type(None))
99 # containers that we need to convert into lists
100 container_to_list = (tuple, set, types.GeneratorType)
102 # Since bools are a subtype of Integrals, which are a subtype of Reals,
103 # we have to check them in that order.
105 if isinstance(obj, bool):
106 return obj
108 if isinstance(obj, numbers.Integral):
109 # cast int to int, in case subclasses override __str__ (e.g. boost enum, #4598)
110 return int(obj)
112 if isinstance(obj, numbers.Real):
113 # cast out-of-range floats to their reprs
114 if math.isnan(obj) or math.isinf(obj):
115 return repr(obj)
116 return float(obj)
118 if isinstance(obj, atomic_ok):
119 return obj
121 if isinstance(obj, bytes):
122 return b2a_base64(obj).decode("ascii")
124 if isinstance(obj, container_to_list) or (
125 hasattr(obj, "__iter__") and hasattr(obj, "__next__")
126 ):
127 obj = list(obj)
129 if isinstance(obj, list):
130 return [json_clean(x) for x in obj]
132 if isinstance(obj, dict):
133 # First, validate that the dict won't lose data in conversion due to
134 # key collisions after stringification. This can happen with keys like
135 # True and 'true' or 1 and '1', which collide in JSON.
136 nkeys = len(obj)
137 nkeys_collapsed = len(set(map(str, obj)))
138 if nkeys != nkeys_collapsed:
139 raise ValueError(
140 "dict cannot be safely converted to JSON: "
141 "key collision would lead to dropped values"
142 )
143 # If all OK, proceed by making the new dict that will be json-safe
144 out = {}
145 for k, v in iter(obj.items()):
146 out[str(k)] = json_clean(v)
147 return out
148 if isinstance(obj, datetime):
149 return obj.strftime(ISO8601)
151 # we don't understand it, it's probably an unserializable object
152 raise ValueError("Can't clean for JSON: %r" % obj)