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

Revision 49, 12.8 KB checked in by xi, 8 years ago (diff)

Fix segfault under Python2.3.

Support for complex numbers.

Add /usr/local to the search path.

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