Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/ijson/backends/yajl2_cffi.py: 94%

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

111 statements  

1''' 

2CFFI-Wrapper for YAJL C library version 2.x. 

3''' 

4 

5from cffi import FFI 

6import functools 

7 

8from ijson import common, backends, utils 

9 

10 

11ffi = FFI() 

12ffi.cdef(""" 

13typedef void * (*yajl_malloc_func)(void *ctx, size_t sz); 

14typedef void (*yajl_free_func)(void *ctx, void * ptr); 

15typedef void * (*yajl_realloc_func)(void *ctx, void * ptr, size_t sz); 

16typedef struct 

17{ 

18 yajl_malloc_func malloc; 

19 yajl_realloc_func realloc; 

20 yajl_free_func free; 

21 void * ctx; 

22} yajl_alloc_funcs; 

23typedef struct yajl_handle_t * yajl_handle; 

24typedef enum { 

25 yajl_status_ok, 

26 yajl_status_client_canceled, 

27 yajl_status_error 

28} yajl_status; 

29typedef enum { 

30 yajl_allow_comments = 0x01, 

31 yajl_dont_validate_strings = 0x02, 

32 yajl_allow_trailing_garbage = 0x04, 

33 yajl_allow_multiple_values = 0x08, 

34 yajl_allow_partial_values = 0x10 

35} yajl_option; 

36typedef struct { 

37 int (* yajl_null)(void * ctx); 

38 int (* yajl_boolean)(void * ctx, int boolVal); 

39 int (* yajl_integer)(void * ctx, long long integerVal); 

40 int (* yajl_double)(void * ctx, double doubleVal); 

41 int (* yajl_number)(void * ctx, const char * numberVal, 

42 size_t numberLen); 

43 int (* yajl_string)(void * ctx, const unsigned char * stringVal, 

44 size_t stringLen); 

45 int (* yajl_start_map)(void * ctx); 

46 int (* yajl_map_key)(void * ctx, const unsigned char * key, 

47 size_t stringLen); 

48 int (* yajl_end_map)(void * ctx); 

49 int (* yajl_start_array)(void * ctx); 

50 int (* yajl_end_array)(void * ctx); 

51} yajl_callbacks; 

52int yajl_version(void); 

53yajl_handle yajl_alloc(const yajl_callbacks *callbacks, yajl_alloc_funcs *afs, void *ctx); 

54int yajl_config(yajl_handle h, yajl_option opt, ...); 

55yajl_status yajl_parse(yajl_handle hand, const unsigned char *jsonText, size_t jsonTextLength); 

56yajl_status yajl_complete_parse(yajl_handle hand); 

57unsigned char* yajl_get_error(yajl_handle hand, int verbose, const unsigned char *jsonText, size_t jsonTextLength); 

58void yajl_free_error(yajl_handle hand, unsigned char * str); 

59void yajl_free(yajl_handle handle); 

60""") 

61 

62 

63yajl = backends.find_yajl_cffi(ffi, 2) 

64 

65YAJL_OK = 0 

66YAJL_CANCELLED = 1 

67YAJL_INSUFFICIENT_DATA = 2 

68YAJL_ERROR = 3 

69 

70# constants defined in yajl_parse.h 

71YAJL_ALLOW_COMMENTS = 1 

72YAJL_MULTIPLE_VALUES = 8 

73 

74 

75def append_event_to_ctx(event): 

76 def wrapper(func): 

77 @functools.wraps(func) 

78 def wrapped(ctx, *args, **kwargs): 

79 value = func(*args, **kwargs) 

80 send = ffi.from_handle(ctx) 

81 send((event, value)) 

82 return 1 

83 return wrapped 

84 return wrapper 

85 

86 

87@ffi.callback('int(void *ctx)') 

88@append_event_to_ctx('null') 

89def null(): 

90 return None 

91 

92 

93@ffi.callback('int(void *ctx, int val)') 

94@append_event_to_ctx('boolean') 

95def boolean(val): 

96 return bool(val) 

97 

98 

99@ffi.callback('int(void *ctx, long long integerVal)') 

100@append_event_to_ctx('number') 

101def integer(val): 

102 return int(val) 

103 

104 

105@ffi.callback('int(void * ctx, double doubleVal)') 

106@append_event_to_ctx('number') 

107def double(val): 

108 return val 

109 

110 

111@ffi.callback('int(void *ctx, const char *numberVal, size_t numberLen)') 

112@append_event_to_ctx('number') 

113def number(val, length): 

114 return common.integer_or_decimal(ffi.string(val, maxlen=length).decode("utf-8")) 

115 

116 

117@ffi.callback('int(void *ctx, const unsigned char *stringVal, size_t stringLen)') 

118@append_event_to_ctx('string') 

119def string(val, length): 

120 return ffi.string(val, maxlen=length).decode('utf-8') 

121 

122 

123@ffi.callback('int(void *ctx)') 

124@append_event_to_ctx('start_map') 

125def start_map(): 

126 return None 

127 

128 

129@ffi.callback('int(void *ctx, const unsigned char *key, size_t stringLen)') 

130@append_event_to_ctx('map_key') 

131def map_key(key, length): 

132 return ffi.string(key, maxlen=length).decode('utf-8') 

133 

134 

135@ffi.callback('int(void *ctx)') 

136@append_event_to_ctx('end_map') 

137def end_map(): 

138 return None 

139 

140 

141@ffi.callback('int(void *ctx)') 

142@append_event_to_ctx('start_array') 

143def start_array(): 

144 return None 

145 

146 

147@ffi.callback('int(void *ctx)') 

148@append_event_to_ctx('end_array') 

149def end_array(): 

150 return None 

151 

152 

153_decimal_callback_data = ( 

154 null, boolean, ffi.NULL, ffi.NULL, number, string, 

155 start_map, map_key, end_map, start_array, end_array 

156) 

157 

158_float_callback_data = ( 

159 null, boolean, integer, double, ffi.NULL, string, 

160 start_map, map_key, end_map, start_array, end_array 

161) 

162 

163 

164def yajl_init(scope, send, allow_comments=False, multiple_values=False, use_float=False): 

165 scope.ctx = ffi.new_handle(send) 

166 if use_float: 

167 scope.callbacks = ffi.new('yajl_callbacks*', _float_callback_data) 

168 else: 

169 scope.callbacks = ffi.new('yajl_callbacks*', _decimal_callback_data) 

170 handle = yajl.yajl_alloc(scope.callbacks, ffi.NULL, scope.ctx) 

171 

172 if allow_comments: 

173 yajl.yajl_config(handle, YAJL_ALLOW_COMMENTS, ffi.cast('int', 1)) 

174 if multiple_values: 

175 yajl.yajl_config(handle, YAJL_MULTIPLE_VALUES, ffi.cast('int', 1)) 

176 

177 return handle 

178 

179 

180def yajl_parse(handle, buffer): 

181 if buffer: 

182 result = yajl.yajl_parse(handle, buffer, len(buffer)) 

183 else: 

184 result = yajl.yajl_complete_parse(handle) 

185 

186 if result != YAJL_OK: 

187 perror = yajl.yajl_get_error(handle, 1, buffer, len(buffer)) 

188 error = ffi.string(perror) 

189 try: 

190 error = error.decode('utf8') 

191 except UnicodeDecodeError: 

192 pass 

193 yajl.yajl_free_error(handle, perror) 

194 exception = common.IncompleteJSONError if result == YAJL_INSUFFICIENT_DATA else common.JSONError 

195 raise exception(error) 

196 

197 

198class Container: 

199 pass 

200 

201 

202@utils.coroutine 

203def basic_parse_basecoro(target, **config): 

204 ''' 

205 Coroutine dispatching unprefixed events. 

206 

207 Parameters: 

208 

209 - allow_comments: tells parser to allow comments in JSON input 

210 - multiple_values: allows the parser to parse multiple JSON objects 

211 ''' 

212 

213 # the scope objects makes sure the C objects allocated in _yajl.init 

214 # are kept alive until this function is done 

215 scope = Container() 

216 

217 handle = yajl_init(scope, target.send, **config) 

218 try: 

219 while True: 

220 try: 

221 buffer = (yield) 

222 except GeneratorExit: 

223 buffer = b'' 

224 yajl_parse(handle, buffer) 

225 if not buffer: 

226 break 

227 finally: 

228 yajl.yajl_free(handle) 

229 

230 

231common.enrich_backend(globals())