Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/nbclient/jsonutil.py: 38%

50 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-07-01 06:54 +0000

1"""Utilities to manipulate JSON objects.""" 

2 

3# NOTE: this is a copy of ipykernel/jsonutils.py (+blackified) 

4 

5# Copyright (c) IPython Development Team. 

6# Distributed under the terms of the Modified BSD License. 

7 

8import math 

9import numbers 

10import re 

11import types 

12from binascii import b2a_base64 

13from datetime import datetime 

14from typing import Dict 

15 

16# ----------------------------------------------------------------------------- 

17# Globals and constants 

18# ----------------------------------------------------------------------------- 

19 

20# timestamp formats 

21ISO8601 = "%Y-%m-%dT%H:%M:%S.%f" 

22ISO8601_PAT = re.compile( 

23 r"^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(\.\d{1,6})?Z?([\+\-]\d{2}:?\d{2})?$" 

24) 

25 

26# holy crap, strptime is not threadsafe. 

27# Calling it once at import seems to help. 

28datetime.strptime("1", "%d") 

29 

30# ----------------------------------------------------------------------------- 

31# Classes and functions 

32# ----------------------------------------------------------------------------- 

33 

34 

35# constants for identifying png/jpeg data 

36PNG = b'\x89PNG\r\n\x1a\n' 

37# front of PNG base64-encoded 

38PNG64 = b'iVBORw0KG' 

39JPEG = b'\xff\xd8' 

40# front of JPEG base64-encoded 

41JPEG64 = b'/9' 

42# constants for identifying gif data 

43GIF_64 = b'R0lGODdh' 

44GIF89_64 = b'R0lGODlh' 

45# front of PDF base64-encoded 

46PDF64 = b'JVBER' 

47 

48 

49def encode_images(format_dict: Dict) -> Dict[str, str]: 

50 """b64-encodes images in a displaypub format dict 

51 

52 Perhaps this should be handled in json_clean itself? 

53 

54 Parameters 

55 ---------- 

56 

57 format_dict : dict 

58 A dictionary of display data keyed by mime-type 

59 

60 Returns 

61 ------- 

62 

63 format_dict : dict 

64 A copy of the same dictionary, 

65 but binary image data ('image/png', 'image/jpeg' or 'application/pdf') 

66 is base64-encoded. 

67 

68 """ 

69 return format_dict 

70 

71 

72def json_clean(obj): 

73 """Clean an object to ensure it's safe to encode in JSON. 

74 

75 Atomic, immutable objects are returned unmodified. Sets and tuples are 

76 converted to lists, lists are copied and dicts are also copied. 

77 

78 Note: dicts whose keys could cause collisions upon encoding (such as a dict 

79 with both the number 1 and the string '1' as keys) will cause a ValueError 

80 to be raised. 

81 

82 Parameters 

83 ---------- 

84 obj : any python object 

85 

86 Returns 

87 ------- 

88 out : object 

89 

90 A version of the input which will not cause an encoding error when 

91 encoded as JSON. Note that this function does not *encode* its inputs, 

92 it simply sanitizes it so that there will be no encoding errors later. 

93 

94 """ 

95 # types that are 'atomic' and ok in json as-is. 

96 atomic_ok = (str, type(None)) 

97 

98 # containers that we need to convert into lists 

99 container_to_list = (tuple, set, types.GeneratorType) 

100 

101 # Since bools are a subtype of Integrals, which are a subtype of Reals, 

102 # we have to check them in that order. 

103 

104 if isinstance(obj, bool): 

105 return obj 

106 

107 if isinstance(obj, numbers.Integral): 

108 # cast int to int, in case subclasses override __str__ (e.g. boost enum, #4598) 

109 return int(obj) 

110 

111 if isinstance(obj, numbers.Real): 

112 # cast out-of-range floats to their reprs 

113 if math.isnan(obj) or math.isinf(obj): 

114 return repr(obj) 

115 return float(obj) 

116 

117 if isinstance(obj, atomic_ok): 

118 return obj 

119 

120 if isinstance(obj, bytes): 

121 return b2a_base64(obj).decode('ascii') 

122 

123 if isinstance(obj, container_to_list) or ( 

124 hasattr(obj, '__iter__') and hasattr(obj, '__next__') 

125 ): 

126 obj = list(obj) 

127 

128 if isinstance(obj, list): 

129 return [json_clean(x) for x in obj] 

130 

131 if isinstance(obj, dict): 

132 # First, validate that the dict won't lose data in conversion due to 

133 # key collisions after stringification. This can happen with keys like 

134 # True and 'true' or 1 and '1', which collide in JSON. 

135 nkeys = len(obj) 

136 nkeys_collapsed = len(set(map(str, obj))) 

137 if nkeys != nkeys_collapsed: 

138 raise ValueError( 

139 'dict cannot be safely converted to JSON: ' 

140 'key collision would lead to dropped values' 

141 ) 

142 # If all OK, proceed by making the new dict that will be json-safe 

143 out = {} 

144 for k, v in iter(obj.items()): 

145 out[str(k)] = json_clean(v) 

146 return out 

147 if isinstance(obj, datetime): 

148 return obj.strftime(ISO8601) 

149 

150 # we don't understand it, it's probably an unserializable object 

151 raise ValueError("Can't clean for JSON: %r" % obj)