source: trunk/lib/syck/loaders.py @ 21

Revision 21, 10.6 KB checked in by xi, 9 years ago (diff)

Now it dumps and loads python objects!

Line 
1
2# Python 2.2 compatibility
3from __future__ import generators
4
5try:
6    import datetime
7except ImportError:
8    pass
9
10try:
11    import sets
12except ImportError:
13    class _sets:
14        def Set(self, items):
15            set = {}
16            for items in items:
17                set[items] = None
18            return set
19    sets = _sets()
20
21import _syck
22
23import sys, re
24
25__all__ = ['GenericLoader', 'Loader',
26    'parse', 'load', 'parse_documents', 'load_documents']
27
28class GenericLoader(_syck.Parser):
29
30    def load(self):
31        node = self.parse()
32        if self.eof:
33            return
34        return self._convert(node, {})
35
36    def _convert(self, node, node_to_object):
37        if node in node_to_object:
38            return node_to_object[node]
39        value = None
40        if node.kind == 'scalar':
41            value = node.value
42        elif node.kind == 'seq':
43            value = []
44            for item_node in node.value:
45                value.append(self._convert(item_node, node_to_object))
46        elif node.kind == 'map':
47            value = {}
48            for key_node in node.value:
49                key_object = self._convert(key_node, node_to_object)
50                value_object = self._convert(node.value[key_node],
51                        node_to_object)
52                if key_object in value:
53                    value = None
54                    break
55                try:
56                    value[key_object] = value_object
57                except TypeError:
58                    value = None
59                    break
60            if value is None:
61                value = []
62                for key_node in node.value:
63                    key_object = self_convert(key_node, node_to_object)
64                    value_object = self._convert(node.value[key_node],
65                            node_to_object)
66                value.append((key_object, value_object))
67        node.value = value
68        object = self.construct(node)
69        node_to_object[node] = object
70        return object
71
72    def construct(self, node):
73        return node.value
74
75class Merge:
76    pass
77
78class Default:
79    pass
80
81class Loader(GenericLoader):
82
83    inf_value = 1e300000
84    nan_value = inf_value/inf_value
85
86    timestamp_expr = re.compile(r'(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d)'
87            r'(?:'
88                r'(?:[Tt]|[ \t]+)(?P<hour>\d\d):(?P<minute>\d\d):(?P<second>\d\d)'
89                r'(?:\.(?P<micro>\d+)?)?'
90                r'[ \t]*(?:Z|(?P<zhour>[+-]\d\d)(?::(?P<zminute>\d\d))?)?'
91            r')?')
92
93    merge_key = Merge()
94    default_key = Default()
95
96    non_ascii = []
97    for i in range(256):
98        ch = chr(i)
99        if ch.isalnum():
100            non_ascii.append(ch)
101        else:
102            non_ascii.append('_')
103    non_ascii = ''.join(non_ascii)
104
105    python_bools = {'True': True, 'False': False}
106
107    class python_class:
108        pass
109
110    def find_constructor(self, node):
111        parts = []
112        if node.tag:
113            parts = node.tag.split(':')
114        if parts:
115            if parts[0] == 'tag':
116                parts.pop(0)
117                if parts:
118                    if parts[0] == 'yaml.org,2002':
119                        parts.pop(0)
120                    elif parts[0] == 'python.yaml.org,2002':
121                        parts[0] = 'python'
122            elif parts[0] == 'x-private':
123                parts[0] = 'private'
124        parts = [part.translate(self.non_ascii) for part in parts]
125        while parts:
126            method = 'construct_'+'_'.join(parts)
127            if hasattr(self, method):
128                return getattr(self, method)
129            parts.pop()
130
131    def construct(self, node):
132        if node.kind == 'map' and self.merge_key in node.value:
133            self.merge_maps(node)
134        constructor = self.find_constructor(node)
135        if constructor:
136            return constructor(node)
137        else:
138            return node.value
139
140    def construct_null(self, node):
141        return None
142
143    def construct_bool_yes(self, node):
144        return True
145
146    def construct_bool_no(self, node):
147        return False
148
149    def construct_numeric_base60(self, num_type, node):
150        digits = [num_type(part) for part in node.value.split(':')]
151        digits.reverse()
152        base = 1
153        value = num_type(0)
154        for digit in digits:
155            value += digit*base
156            base *= 60
157        return value
158
159    def construct_int(self, node):
160        return int(node.value)
161
162    def construct_int_hex(self, node):
163        return int(node.value, 16)
164
165    def construct_int_oct(self, node):
166        return int(node.value, 8)
167
168    def construct_int_base60(self, node):
169        return self.construct_numeric_base60(int, node)
170
171    def construct_float(self, node):
172        return float(node.value)
173    construct_float_fix = construct_float
174    construct_float_exp = construct_float
175
176    def construct_float_base60(self, node):
177        return self.construct_numeric_base60(float, node)
178
179    def construct_float_inf(self, node):
180        return self.inf_value
181
182    def construct_float_neginf(self, node):
183        return -self.inf_value
184
185    def construct_float_nan(self, node):
186        return self.nan_value
187
188    def construct_binary(self, node):
189        return node.value.decode('base64')
190
191    def construct_timestamp(self, node):
192        match = self.timestamp_expr.match(node.value)
193        values = match.groupdict()
194        for key in values:
195            if values[key]:
196                values[key] = int(values[key])
197            else:
198                values[key] = 0
199        micro = values['micro']
200        if micro:
201            while 10*micro < 1000000:
202                micro *= 10
203        stamp = datetime.datetime(values['year'], values['month'], values['day'],
204                values['hour'], values['minute'], values['second'], micro)
205        diff = datetime.timedelta(hours=values['zhour'], minutes=values['zminute'])
206        return stamp-diff
207    construct_timestamp_ymd = construct_timestamp
208    construct_timestamp_iso8601 = construct_timestamp
209    construct_timestamp_spaced = construct_timestamp
210
211    def construct_merge(self, node):
212        return self.merge_key
213
214    def construct_default(self, node):
215        return self.default_key
216
217    def merge_maps(self, node):
218        maps = node.value[self.merge_key]
219        del node.value[self.merge_key]
220        if not isinstance(maps, list):
221            maps = [maps]
222        maps.reverse()
223        maps.append(node.value.copy())
224        for item in maps:
225            node.value.update(item)
226
227    def construct_omap(self, node):
228        omap = []
229        for mapping in node.value:
230            for key in mapping:
231                omap.append((key, mapping[key]))
232        return omap
233
234    def construct_pairs(self, node): # Same as construct_omap.
235        pairs = []
236        for mapping in node.value:
237            for key in mapping:
238                pairs.append((key, mapping[key]))
239        return pairs
240
241    def construct_set(self, node):
242        return sets.Set(node.value)
243
244    def construct_python_none(self, node):
245        return None
246
247    def construct_python_bool(self, node):
248        return self.python_bools[node.value]
249
250    def construct_python_int(self, node):
251        return int(node.value)
252
253    def construct_python_long(self, node):
254        return long(node.value)
255
256    def construct_python_float(self, node):
257        return float(node.value)
258
259    def construct_python_str(self, node):
260        return str(node.value)
261
262    def construct_python_unicode(self, node):
263        return unicode(node.value, 'utf-8')
264
265    def construct_python_list(self, node):
266        return node.value
267
268    def construct_python_tuple(self, node):
269        return tuple(node.value)
270
271    def construct_python_dict(self, node):
272        return node.value
273
274    def find_python_object(self, node):
275        full_name = node.tag.split(':')[3]
276        parts = full_name.split('.')
277        object_name = parts.pop()
278        module_name = '.'.join(parts)
279        if not module_name:
280            module_name = '__builtin__'
281        else:
282            __import__(module_name)
283        return getattr(sys.modules[module_name], object_name)
284
285    def find_python_state(self, node):
286        if node.kind == 'seq':
287            args = node.value
288            kwds = {}
289            state = {}
290        else:
291            args = node.value.get('args', [])
292            kwds = node.value.get('kwds', {})
293            state = node.value.get('state', {})
294        return args, kwds, state
295
296    def set_python_state(self, object, state):
297        if hasattr(object, '__setstate__'):
298            object.__setstate__(state)
299        else:
300            slotstate = {}
301            if isinstance(state, tuple) and len(state) == 2:
302                state, slotstate = state
303            object.__dict__.update(state)
304            for key, value in slotstate.items():
305                setattr(object, key, value)
306
307    def construct_python_name(self, node):
308        return self.find_python_object(node)
309
310    def construct_python_object(self, node):
311        cls = self.find_python_object(node)
312        if type(cls) is type(self.python_class):
313            if hasattr(cls, '__getnewargs__'):
314                object = cls()
315            else:
316                object = self.python_class()
317                object.__class__ = cls
318        else:
319            object = cls.__new__(cls)
320        self.set_python_state(object, node.value)
321        return object
322
323    def construct_python_new(self, node):
324        cls = self.find_python_object(node)
325        args, kwds, state = self.find_python_state(node)
326        if type(cls) is type(self.python_class):
327            object = cls(*args, **kwds)
328        else:
329            object = cls.__new__(cls, *args, **kwds)
330        self.set_python_state(object, state)
331        return object
332
333    def construct_python_apply(self, node):
334        constructor = self.find_python_object(node)
335        args, kwds, state = self.find_python_state(node)
336        object = constructor(*args, **kwds)
337        self.set_python_state(object, state)
338        return object
339
340def parse(source):
341    """Parses 'source' and returns the root of the 'Node' graph."""
342    loader = Loader(source)
343    return loader.parse()
344
345def load(source):
346    """Parses 'source' and returns the root object."""
347    loader = Loader(source)
348    return loader.load()
349
350def parse_documents(source):
351    """Iterates over 'source' and yields the root node of each document."""
352    loader = Loader(source)
353    while True:
354        node = loader.parse()
355        if loader.eof:
356            break
357        yield node
358
359def load_documents(source):
360    """Iterates over 'source' and yields the root object of each document."""
361    loader = Loader(source)
362    while True:
363        object = loader.load()
364        if loader.eof:
365            break
366        yield object
367
Note: See TracBrowser for help on using the repository browser.