import of github code used for the hackathon
[github-barcamp-201407] / ijson-1.1 / ijson / common.py
1 '''
2 Backend independent higher level interfaces, common exceptions.
3 '''
4
5 class JSONError(Exception):
6     '''
7     Base exception for all parsing errors.
8     '''
9     pass
10
11 class IncompleteJSONError(JSONError):
12     '''
13     Raised when the parser expects data and it's not available. May be
14     caused by malformed syntax or a broken source stream.
15     '''
16     def __init__(self):
17         super(IncompleteJSONError, self).__init__('Incomplete or empty JSON data')
18
19 def parse(basic_events):
20     '''
21     An iterator returning parsing events with the information about their location
22     with the JSON object tree. Events are tuples ``(prefix, type, value)``.
23
24     Available types and values are:
25
26     ('null', None)
27     ('boolean', <True or False>)
28     ('number', <int or Decimal>)
29     ('string', <unicode>)
30     ('map_key', <str>)
31     ('start_map', None)
32     ('end_map', None)
33     ('start_array', None)
34     ('end_array', None)
35
36     Prefixes represent the path to the nested elements from the root of the JSON
37     document. For example, given this document::
38
39         {
40           "array": [1, 2],
41           "map": {
42             "key": "value"
43           }
44         }
45
46     the parser would yield events:
47
48       ('', 'start_map', None)
49       ('', 'map_key', 'array')
50       ('array', 'start_array', None)
51       ('array.item', 'number', 1)
52       ('array.item', 'number', 2)
53       ('array', 'end_array', None)
54       ('', 'map_key', 'map')
55       ('map', 'start_map', None)
56       ('map', 'map_key', 'key')
57       ('map.key', 'string', u'value')
58       ('map', 'end_map', None)
59       ('', 'end_map', None)
60
61     '''
62     path = []
63     for event, value in basic_events:
64         if event == 'map_key':
65             prefix = '.'.join(path[:-1])
66             path[-1] = value
67         elif event == 'start_map':
68             prefix = '.'.join(path)
69             path.append(None)
70         elif event == 'end_map':
71             path.pop()
72             prefix = '.'.join(path)
73         elif event == 'start_array':
74             prefix = '.'.join(path)
75             path.append('item')
76         elif event == 'end_array':
77             path.pop()
78             prefix = '.'.join(path)
79         else: # any scalar value
80             prefix = '.'.join(path)
81
82         yield prefix, event, value
83
84
85 class ObjectBuilder(object):
86     '''
87     Incrementally builds an object from JSON parser events. Events are passed
88     into the `event` function that accepts two parameters: event type and
89     value. The object being built is available at any time from the `value`
90     attribute.
91
92     Example::
93
94         from StringIO import StringIO
95         from ijson.parse import basic_parse
96         from ijson.utils import ObjectBuilder
97
98         builder = ObjectBuilder()
99         f = StringIO('{"key": "value"})
100         for event, value in basic_parse(f):
101             builder.event(event, value)
102         print builder.value
103
104     '''
105     def __init__(self):
106         def initial_set(value):
107             self.value = value
108         self.containers = [initial_set]
109
110     def event(self, event, value):
111         if event == 'map_key':
112             self.key = value
113         elif event == 'start_map':
114             map = {}
115             self.containers[-1](map)
116             def setter(value):
117                 map[self.key] = value
118             self.containers.append(setter)
119         elif event == 'start_array':
120             array = []
121             self.containers[-1](array)
122             self.containers.append(array.append)
123         elif event == 'end_array' or event == 'end_map':
124             self.containers.pop()
125         else:
126             self.containers[-1](value)
127
128 def items(prefixed_events, prefix):
129     '''
130     An iterator returning native Python objects constructed from the events
131     under a given prefix.
132     '''
133     prefixed_events = iter(prefixed_events)
134     try:
135         while True:
136             current, event, value = next(prefixed_events)
137             if current == prefix:
138                 if event in ('start_map', 'start_array'):
139                     builder = ObjectBuilder()
140                     end_event = event.replace('start', 'end')
141                     while (current, event) != (prefix, end_event):
142                         builder.event(event, value)
143                         current, event, value = next(prefixed_events)
144                     yield builder.value
145                 else:
146                     yield value
147     except StopIteration:
148         pass

Benjamin Mako Hill || Want to submit a patch?