Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/ijson-3.2.0.post0-py3.8-linux-x86_64.egg/ijson/backends/yajl2_cffi.py: 94%

110 statements  

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

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 

9from ijson.compat import b2s 

10 

11 

12ffi = FFI() 

13ffi.cdef(""" 

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

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

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

17typedef struct 

18{ 

19 yajl_malloc_func malloc; 

20 yajl_realloc_func realloc; 

21 yajl_free_func free; 

22 void * ctx; 

23} yajl_alloc_funcs; 

24typedef struct yajl_handle_t * yajl_handle; 

25typedef enum { 

26 yajl_status_ok, 

27 yajl_status_client_canceled, 

28 yajl_status_error 

29} yajl_status; 

30typedef enum { 

31 yajl_allow_comments = 0x01, 

32 yajl_dont_validate_strings = 0x02, 

33 yajl_allow_trailing_garbage = 0x04, 

34 yajl_allow_multiple_values = 0x08, 

35 yajl_allow_partial_values = 0x10 

36} yajl_option; 

37typedef struct { 

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

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

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

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

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

43 size_t numberLen); 

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

45 size_t stringLen); 

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

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

48 size_t stringLen); 

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

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

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

52} yajl_callbacks; 

53int yajl_version(void); 

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

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

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

57yajl_status yajl_complete_parse(yajl_handle hand); 

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

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

60void yajl_free(yajl_handle handle); 

61""") 

62 

63 

64yajl = backends.find_yajl_cffi(ffi, 2) 

65 

66YAJL_OK = 0 

67YAJL_CANCELLED = 1 

68YAJL_INSUFFICIENT_DATA = 2 

69YAJL_ERROR = 3 

70 

71# constants defined in yajl_parse.h 

72YAJL_ALLOW_COMMENTS = 1 

73YAJL_MULTIPLE_VALUES = 8 

74 

75 

76def append_event_to_ctx(event): 

77 def wrapper(func): 

78 @functools.wraps(func) 

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

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

81 send = ffi.from_handle(ctx) 

82 send((event, value)) 

83 return 1 

84 return wrapped 

85 return wrapper 

86 

87 

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

89@append_event_to_ctx('null') 

90def null(): 

91 return None 

92 

93 

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

95@append_event_to_ctx('boolean') 

96def boolean(val): 

97 return bool(val) 

98 

99 

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

101@append_event_to_ctx('number') 

102def integer(val): 

103 return int(val) 

104 

105 

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

107@append_event_to_ctx('number') 

108def double(val): 

109 return val 

110 

111 

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

113@append_event_to_ctx('number') 

114def number(val, length): 

115 return common.integer_or_decimal(b2s(ffi.string(val, maxlen=length))) 

116 

117 

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

119@append_event_to_ctx('string') 

120def string(val, length): 

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

122 

123 

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

125@append_event_to_ctx('start_map') 

126def start_map(): 

127 return None 

128 

129 

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

131@append_event_to_ctx('map_key') 

132def map_key(key, length): 

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

134 

135 

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

137@append_event_to_ctx('end_map') 

138def end_map(): 

139 return None 

140 

141 

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

143@append_event_to_ctx('start_array') 

144def start_array(): 

145 return None 

146 

147 

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

149@append_event_to_ctx('end_array') 

150def end_array(): 

151 return None 

152 

153 

154_decimal_callback_data = ( 

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

156 start_map, map_key, end_map, start_array, end_array 

157) 

158 

159_float_callback_data = ( 

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

161 start_map, map_key, end_map, start_array, end_array 

162) 

163 

164 

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

166 scope.ctx = ffi.new_handle(send) 

167 if use_float: 

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

169 else: 

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

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

172 

173 if allow_comments: 

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

175 if multiple_values: 

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

177 

178 return handle 

179 

180 

181def yajl_parse(handle, buffer): 

182 if buffer: 

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

184 else: 

185 result = yajl.yajl_complete_parse(handle) 

186 

187 if result != YAJL_OK: 

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

189 error = ffi.string(perror) 

190 try: 

191 error = error.decode('utf8') 

192 except UnicodeDecodeError: 

193 pass 

194 yajl.yajl_free_error(handle, perror) 

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

196 raise exception(error) 

197 

198 

199class Container(object): 

200 pass 

201 

202 

203@utils.coroutine 

204def basic_parse_basecoro(target, **config): 

205 ''' 

206 Coroutine dispatching unprefixed events. 

207 

208 Parameters: 

209 

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

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

212 ''' 

213 

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

215 # are kept alive until this function is done 

216 scope = Container() 

217 

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

219 try: 

220 while True: 

221 try: 

222 buffer = (yield) 

223 except GeneratorExit: 

224 buffer = b'' 

225 yajl_parse(handle, buffer) 

226 if not buffer: 

227 break 

228 finally: 

229 yajl.yajl_free(handle) 

230 

231 

232common.enrich_backend(globals())