2 Wrapper for YAJL C library version 1.x.
5 from ctypes import Structure, c_uint, c_ubyte, c_int, c_long, c_double, \
6 c_void_p, c_char_p, CFUNCTYPE, POINTER, byref, string_at, cast , \
8 from decimal import Decimal
10 from ijson import common, backends
11 from ijson.compat import b2s
14 yajl = backends.find_yajl(1)
16 yajl.yajl_alloc.restype = POINTER(c_char)
17 yajl.yajl_get_error.restype = POINTER(c_char)
19 C_EMPTY = CFUNCTYPE(c_int, c_void_p)
20 C_INT = CFUNCTYPE(c_int, c_void_p, c_int)
21 C_LONG = CFUNCTYPE(c_int, c_void_p, c_long)
22 C_DOUBLE = CFUNCTYPE(c_int, c_void_p, c_double)
23 C_STR = CFUNCTYPE(c_int, c_void_p, POINTER(c_ubyte), c_uint)
28 Helper function casting a string that represents any Javascript number
29 into appropriate Python value: either int or Decimal.
37 # Mapping of JSON parser events to callback C types and value converters.
38 # Used to define the Callbacks structure and actual callback functions
39 # inside the parse function.
40 ('null', C_EMPTY, lambda: None),
41 ('boolean', C_INT, lambda v: bool(v)),
42 # "integer" and "double" aren't actually yielded by yajl since "number"
43 # takes precedence if defined
44 ('integer', C_LONG, lambda v, l: int(string_at(v, l))),
45 ('double', C_DOUBLE, lambda v, l: float(string_at(v, l))),
46 ('number', C_STR, lambda v, l: number(b2s(string_at(v, l)))),
47 ('string', C_STR, lambda v, l: string_at(v, l).decode('utf-8')),
48 ('start_map', C_EMPTY, lambda: None),
49 ('map_key', C_STR, lambda v, l: b2s(string_at(v, l))),
50 ('end_map', C_EMPTY, lambda: None),
51 ('start_array', C_EMPTY, lambda: None),
52 ('end_array', C_EMPTY, lambda: None),
55 class Callbacks(Structure):
56 _fields_ = [(name, type) for name, type, func in _callback_data]
58 class Config(Structure):
60 ("allowComments", c_uint),
66 YAJL_INSUFFICIENT_DATA = 2
70 def basic_parse(f, allow_comments=False, check_utf8=False, buf_size=64 * 1024):
72 Iterator yielding unprefixed events.
76 - f: a readable file-like object with JSON input
77 - allow_comments: tells parser to allow comments in JSON input
78 - check_utf8: if True, parser will cause an error if input is invalid utf-8
79 - buf_size: a size of an input buffer
83 def callback(event, func_type, func):
84 def c_callback(context, *args):
85 events.append((event, func(*args)))
87 return func_type(c_callback)
89 callbacks = Callbacks(*[callback(*data) for data in _callback_data])
90 config = Config(allow_comments, check_utf8)
91 handle = yajl.yajl_alloc(byref(callbacks), byref(config), None, None)
94 buffer = f.read(buf_size)
96 result = yajl.yajl_parse(handle, buffer, len(buffer))
98 result = yajl.yajl_parse_complete(handle)
99 if result == YAJL_ERROR:
100 perror = yajl.yajl_get_error(handle, 1, buffer, len(buffer))
101 error = cast(perror, c_char_p).value
102 yajl.yajl_free_error(handle, perror)
103 raise common.JSONError(error)
104 if not buffer and not events:
105 if result == YAJL_INSUFFICIENT_DATA:
106 raise common.IncompleteJSONError()
113 yajl.yajl_free(handle)
115 def parse(file, **kwargs):
117 Backend-specific wrapper for ijson.common.parse.
119 return common.parse(basic_parse(file, **kwargs))
121 def items(file, prefix):
123 Backend-specific wrapper for ijson.common.items.
125 return common.items(parse(file), prefix)