source: pyyaml/trunk/lib/yaml/constructor.py @ 142

Revision 142, 19.9 KB checked in by xi, 8 years ago (diff)

Add support for recursive nodes to Composer. Constructor does not support recursive objects though.

RevLine 
[55]1
[133]2__all__ = ['BaseConstructor', 'SafeConstructor', 'Constructor',
3    'ConstructorError']
[57]4
[55]5from error import *
6from nodes import *
[136]7from composer import *
[55]8
9try:
10    import datetime
11    datetime_available = True
12except ImportError:
13    datetime_available = False
14
15try:
16    set
17except NameError:
18    from sets import Set as set
19
[139]20import binascii, re, sys
[55]21
22class ConstructorError(MarkedYAMLError):
23    pass
24
[136]25class BaseConstructor(Composer):
[55]26
[136]27    yaml_constructors = {}
28    yaml_multi_constructors = {}
29
30    def __init__(self):
[55]31        self.constructed_objects = {}
[142]32        self.recursive_objects = {}
[55]33
[136]34    def check_data(self):
[55]35        # If there are more documents available?
[136]36        return self.check_node()
[55]37
[136]38    def get_data(self):
[55]39        # Construct and return the next document.
[136]40        if self.check_node():
41            return self.construct_document(self.get_node())
[55]42
43    def __iter__(self):
44        # Iterator protocol.
[136]45        while self.check_node():
46            yield self.construct_document(self.get_node())
[55]47
48    def construct_document(self, node):
[136]49        data = self.construct_object(node)
[55]50        self.constructed_objects = {}
[142]51        self.recursive_objects = {}
[136]52        return data
[55]53
54    def construct_object(self, node):
55        if node in self.constructed_objects:
56            return self.constructed_objects[node]
[142]57        if node in self.recursive_objects:
58            raise ConstructorError(None, None,
59                    "found recursive node", node.start_mark)
60        self.recursive_objects[node] = None
[136]61        constructor = None
[55]62        if node.tag in self.yaml_constructors:
[136]63            constructor = lambda node: self.yaml_constructors[node.tag](self, node)
64        else:
65            for tag_prefix in self.yaml_multi_constructors:
66                if node.tag.startswith(tag_prefix):
67                    tag_suffix = node.tag[len(tag_prefix):]
68                    constructor = lambda node:  \
69                            self.yaml_multi_constructors[tag_prefix](self, tag_suffix, node)
[139]70                    break
[136]71            else:
72                if None in self.yaml_multi_constructors:
73                    constructor = lambda node:  \
74                            self.yaml_multi_constructors[None](self, node.tag, node)
75                elif None in self.yaml_constructors:
76                    constructor = lambda node:  \
77                            self.yaml_constructors[None](self, node)
78                elif isinstance(node, ScalarNode):
79                    constructor = self.construct_scalar
80                elif isinstance(node, SequenceNode):
81                    constructor = self.construct_sequence
82                elif isinstance(node, MappingNode):
83                    constructor = self.construct_mapping
[139]84                else:
85                    print node.tag
[136]86        data = constructor(node)
87        self.constructed_objects[node] = data
[142]88        del self.recursive_objects[node]
[136]89        return data
[55]90
91    def construct_scalar(self, node):
92        if not isinstance(node, ScalarNode):
93            if isinstance(node, MappingNode):
94                for key_node in node.value:
95                    if key_node.tag == u'tag:yaml.org,2002:value':
96                        return self.construct_scalar(node.value[key_node])
97            raise ConstructorError(None, None,
98                    "expected a scalar node, but found %s" % node.id,
[116]99                    node.start_mark)
[55]100        return node.value
101
102    def construct_sequence(self, node):
103        if not isinstance(node, SequenceNode):
104            raise ConstructorError(None, None,
105                    "expected a sequence node, but found %s" % node.id,
[116]106                    node.start_mark)
[55]107        return [self.construct_object(child) for child in node.value]
108
109    def construct_mapping(self, node):
110        if not isinstance(node, MappingNode):
111            raise ConstructorError(None, None,
112                    "expected a mapping node, but found %s" % node.id,
[116]113                    node.start_mark)
[55]114        mapping = {}
[56]115        merge = None
[55]116        for key_node in node.value:
[56]117            if key_node.tag == u'tag:yaml.org,2002:merge':
118                if merge is not None:
[116]119                    raise ConstructorError("while constructing a mapping", node.start_mark,
120                            "found duplicate merge key", key_node.start_mark)
[58]121                value_node = node.value[key_node]
122                if isinstance(value_node, MappingNode):
123                    merge = [self.construct_mapping(value_node)]
124                elif isinstance(value_node, SequenceNode):
125                    merge = []
126                    for subnode in value_node.value:
127                        if not isinstance(subnode, MappingNode):
128                            raise ConstructorError("while constructing a mapping",
[116]129                                    node.start_mark,
[58]130                                    "expected a mapping for merging, but found %s"
[116]131                                    % subnode.id, subnode.start_mark)
[58]132                        merge.append(self.construct_mapping(subnode))
133                    merge.reverse()
134                else:
[116]135                    raise ConstructorError("while constructing a mapping", node.start_mark,
[58]136                            "expected a mapping or list of mappings for merging, but found %s"
[116]137                            % value_node.id, value_node.start_mark)
[56]138            elif key_node.tag == u'tag:yaml.org,2002:value':
139                if '=' in mapping:
[116]140                    raise ConstructorError("while construction a mapping", node.start_mark,
141                            "found duplicate value key", key_node.start_mark)
[56]142                value = self.construct_object(node.value[key_node])
143                mapping['='] = value
144            else:
145                key = self.construct_object(key_node)
146                try:
147                    duplicate_key = key in mapping
148                except TypeError, exc:
[116]149                    raise ConstructorError("while constructing a mapping", node.start_mark,
150                            "found unacceptable key (%s)" % exc, key_node.start_mark)
[56]151                if duplicate_key:
[116]152                    raise ConstructorError("while constructing a mapping", node.start_mark,
153                            "found duplicate key", key_node.start_mark)
[56]154                value = self.construct_object(node.value[key_node])
155                mapping[key] = value
156        if merge is not None:
157            merge.append(mapping)
158            mapping = {}
159            for submapping in merge:
160                mapping.update(submapping)
[55]161        return mapping
162
163    def construct_pairs(self, node):
164        if not isinstance(node, MappingNode):
165            raise ConstructorError(None, None,
166                    "expected a mapping node, but found %s" % node.id,
[116]167                    node.start_mark)
[55]168        pairs = []
169        for key_node in node.value:
170            key = self.construct_object(key_node)
171            value = self.construct_object(node.value[key_node])
172            pairs.append((key, value))
173        return pairs
174
175    def add_constructor(cls, tag, constructor):
176        if not 'yaml_constructors' in cls.__dict__:
177            cls.yaml_constructors = cls.yaml_constructors.copy()
178        cls.yaml_constructors[tag] = constructor
179    add_constructor = classmethod(add_constructor)
180
[136]181    def add_multi_constructor(cls, tag_prefix, multi_constructor):
182        if not 'yaml_multi_constructors' in cls.__dict__:
183            cls.yaml_multi_constructors = cls.yaml_multi_constructors.copy()
184        cls.yaml_multi_constructors[tag_prefix] = multi_constructor
185    add_multi_constructor = classmethod(add_multi_constructor)
[55]186
[133]187class SafeConstructor(BaseConstructor):
[55]188
189    def construct_yaml_null(self, node):
190        self.construct_scalar(node)
191        return None
192
193    bool_values = {
194        u'yes':     True,
195        u'no':      False,
196        u'true':    True,
197        u'false':   False,
198        u'on':      True,
199        u'off':     False,
200    }
201
202    def construct_yaml_bool(self, node):
203        value = self.construct_scalar(node)
204        return self.bool_values[value.lower()]
205
206    def construct_yaml_int(self, node):
207        value = str(self.construct_scalar(node))
208        value = value.replace('_', '')
209        sign = +1
210        if value[0] == '-':
211            sign = -1
212        if value[0] in '+-':
213            value = value[1:]
214        if value == '0':
215            return 0
216        elif value.startswith('0b'):
217            return sign*int(value[2:], 2)
218        elif value.startswith('0x'):
219            return sign*int(value[2:], 16)
220        elif value[0] == '0':
221            return sign*int(value, 8)
222        elif ':' in value:
223            digits = [int(part) for part in value.split(':')]
224            digits.reverse()
225            base = 1
226            value = 0
227            for digit in digits:
228                value += digit*base
229                base *= 60
230            return sign*value
231        else:
232            return sign*int(value)
233
234    inf_value = 1e300000
235    nan_value = inf_value/inf_value
236
237    def construct_yaml_float(self, node):
238        value = str(self.construct_scalar(node))
239        value = value.replace('_', '')
240        sign = +1
241        if value[0] == '-':
[58]242            sign = -1
[55]243        if value[0] in '+-':
244            value = value[1:]
245        if value.lower() == '.inf':
246            return sign*self.inf_value
247        elif value.lower() == '.nan':
248            return self.nan_value
249        elif ':' in value:
250            digits = [float(part) for part in value.split(':')]
251            digits.reverse()
252            base = 1
253            value = 0.0
254            for digit in digits:
255                value += digit*base
256                base *= 60
257            return sign*value
258        else:
259            return float(value)
260
261    def construct_yaml_binary(self, node):
262        value = self.construct_scalar(node)
263        try:
264            return str(value).decode('base64')
265        except (binascii.Error, UnicodeEncodeError), exc:
266            raise ConstructorError(None, None,
[116]267                    "failed to decode base64 data: %s" % exc, node.start_mark) 
[55]268
[56]269    timestamp_regexp = re.compile(
270            ur'''^(?P<year>[0-9][0-9][0-9][0-9])
271                -(?P<month>[0-9][0-9]?)
272                -(?P<day>[0-9][0-9]?)
[58]273                (?:(?:[Tt]|[ \t]+)
[56]274                (?P<hour>[0-9][0-9]?)
275                :(?P<minute>[0-9][0-9])
276                :(?P<second>[0-9][0-9])
277                (?:\.(?P<fraction>[0-9]*))?
278                (?:[ \t]*(?:Z|(?P<tz_hour>[-+][0-9][0-9]?)
[58]279                (?::(?P<tz_minute>[0-9][0-9])?)?))?)?$''', re.X)
[56]280
281    def construct_yaml_timestamp(self, node):
282        value = self.construct_scalar(node)
[58]283        match = self.timestamp_regexp.match(node.value)
[56]284        values = match.groupdict()
285        for key in values:
286            if values[key]:
287                values[key] = int(values[key])
288            else:
289                values[key] = 0
290        fraction = values['fraction']
[58]291        if fraction:
[56]292            while 10*fraction < 1000000:
293                fraction *= 10
294            values['fraction'] = fraction
295        stamp = datetime.datetime(values['year'], values['month'], values['day'],
296                values['hour'], values['minute'], values['second'], values['fraction'])
297        diff = datetime.timedelta(hours=values['tz_hour'], minutes=values['tz_minute'])
298        return stamp-diff
299
300    def construct_yaml_omap(self, node):
301        # Note: we do not check for duplicate keys, because it's too
302        # CPU-expensive.
303        if not isinstance(node, SequenceNode):
[116]304            raise ConstructorError("while constructing an ordered map", node.start_mark,
305                    "expected a sequence, but found %s" % node.id, node.start_mark)
[56]306        omap = []
307        for subnode in node.value:
308            if not isinstance(subnode, MappingNode):
[116]309                raise ConstructorError("while constructing an ordered map", node.start_mark,
[56]310                        "expected a mapping of length 1, but found %s" % subnode.id,
[116]311                        subnode.start_mark)
[58]312            if len(subnode.value) != 1:
[116]313                raise ConstructorError("while constructing an ordered map", node.start_mark,
[58]314                        "expected a single mapping item, but found %d items" % len(subnode.value),
[116]315                        subnode.start_mark)
[58]316            key_node = subnode.value.keys()[0]
317            key = self.construct_object(key_node)
318            value = self.construct_object(subnode.value[key_node])
319            omap.append((key, value))
320        return omap
[56]321
322    def construct_yaml_pairs(self, node):
323        # Note: the same code as `construct_yaml_omap`.
324        if not isinstance(node, SequenceNode):
[116]325            raise ConstructorError("while constructing pairs", node.start_mark,
326                    "expected a sequence, but found %s" % node.id, node.start_mark)
[58]327        pairs = []
[56]328        for subnode in node.value:
329            if not isinstance(subnode, MappingNode):
[116]330                raise ConstructorError("while constructing pairs", node.start_mark,
[56]331                        "expected a mapping of length 1, but found %s" % subnode.id,
[116]332                        subnode.start_mark)
[58]333            if len(subnode.value) != 1:
[116]334                raise ConstructorError("while constructing pairs", node.start_mark,
[58]335                        "expected a single mapping item, but found %d items" % len(subnode.value),
[116]336                        subnode.start_mark)
[58]337            key_node = subnode.value.keys()[0]
338            key = self.construct_object(key_node)
339            value = self.construct_object(subnode.value[key_node])
340            pairs.append((key, value))
341        return pairs
[56]342
343    def construct_yaml_set(self, node):
344        value = self.construct_mapping(node)
345        return set(value)
346
[55]347    def construct_yaml_str(self, node):
348        value = self.construct_scalar(node)
349        try:
350            return str(value)
351        except UnicodeEncodeError:
352            return value
353
[56]354    def construct_yaml_seq(self, node):
355        return self.construct_sequence(node)
356
357    def construct_yaml_map(self, node):
358        return self.construct_mapping(node)
359
[136]360    def construct_yaml_object(self, node, cls):
[139]361        state = self.construct_mapping(node)
[136]362        data = cls.__new__(cls)
363        if hasattr(data, '__setstate__'):
[139]364            data.__setstate__(state)
[136]365        else:
[139]366            data.__dict__.update(state)
[136]367        return data
368
[57]369    def construct_undefined(self, node):
370        raise ConstructorError(None, None,
371                "could not determine a constructor for the tag %r" % node.tag.encode('utf-8'),
[116]372                node.start_mark)
[57]373
[133]374SafeConstructor.add_constructor(
[55]375        u'tag:yaml.org,2002:null',
[133]376        SafeConstructor.construct_yaml_null)
[55]377
[133]378SafeConstructor.add_constructor(
[55]379        u'tag:yaml.org,2002:bool',
[133]380        SafeConstructor.construct_yaml_bool)
[55]381
[133]382SafeConstructor.add_constructor(
[55]383        u'tag:yaml.org,2002:int',
[133]384        SafeConstructor.construct_yaml_int)
[55]385
[133]386SafeConstructor.add_constructor(
[55]387        u'tag:yaml.org,2002:float',
[133]388        SafeConstructor.construct_yaml_float)
[55]389
[133]390SafeConstructor.add_constructor(
[58]391        u'tag:yaml.org,2002:binary',
[133]392        SafeConstructor.construct_yaml_binary)
[56]393
[58]394if datetime_available:
[133]395    SafeConstructor.add_constructor(
[58]396            u'tag:yaml.org,2002:timestamp',
[133]397            SafeConstructor.construct_yaml_timestamp)
[58]398
[133]399SafeConstructor.add_constructor(
[56]400        u'tag:yaml.org,2002:omap',
[133]401        SafeConstructor.construct_yaml_omap)
[56]402
[133]403SafeConstructor.add_constructor(
[56]404        u'tag:yaml.org,2002:pairs',
[133]405        SafeConstructor.construct_yaml_pairs)
[56]406
[133]407SafeConstructor.add_constructor(
[56]408        u'tag:yaml.org,2002:set',
[133]409        SafeConstructor.construct_yaml_set)
[56]410
[133]411SafeConstructor.add_constructor(
[55]412        u'tag:yaml.org,2002:str',
[133]413        SafeConstructor.construct_yaml_str)
[55]414
[133]415SafeConstructor.add_constructor(
[56]416        u'tag:yaml.org,2002:seq',
[133]417        SafeConstructor.construct_yaml_seq)
[56]418
[133]419SafeConstructor.add_constructor(
[56]420        u'tag:yaml.org,2002:map',
[133]421        SafeConstructor.construct_yaml_map)
[56]422
[133]423SafeConstructor.add_constructor(None,
424        SafeConstructor.construct_undefined)
[57]425
[133]426class Constructor(SafeConstructor):
[55]427
[139]428    def construct_python_str(self, node):
429        return self.construct_scalar(node).encode('utf-8')
430
431    def construct_python_unicode(self, node):
432        return self.construct_scalar(node)
433
434    def construct_python_long(self, node):
435        return long(self.construct_yaml_int(node))
436
437    def construct_python_complex(self, node):
438       return complex(self.construct_scalar(node))
439
440    def construct_python_tuple(self, node):
441        return tuple(self.construct_yaml_seq(node))
442
443    def find_python_module(self, name, mark):
444        if not name:
445            raise ConstructorError("while constructing a Python module", mark,
446                    "expected non-empty name appended to the tag", mark)
447        try:
448            __import__(name)
449        except ImportError, exc:
450            raise ConstructorError("while constructing a Python module", mark,
451                    "cannot find module %r (%s)" % (name.encode('utf-8'), exc), mark)
452        return sys.modules[name]
453
454    def find_python_name(self, name, mark):
455        if not name:
456            raise ConstructorError("while constructing a Python object", mark,
457                    "expected non-empty name appended to the tag", mark)
458        if u'.' in name:
459            module_name, object_name = name.rsplit('.', 1)
460        else:
461            module_name = '__builtin__'
462            object_name = name
463        try:
464            __import__(module_name)
465        except ImportError, exc:
466            raise ConstructorError("while constructing a Python object", mark,
467                    "cannot find module %r (%s)" % (module_name.encode('utf-8'), exc), mark)
468        module = sys.modules[module_name]
469        if not hasattr(module, object_name):
470            raise ConstructorError("while constructing a Python object", mark,
471                    "cannot find %r in the module %r" % (object_name.encode('utf-8'),
472                        module.__name__), mark)
473        return getattr(module, object_name)
474
475    def construct_python_name(self, suffix, node):
476        value = self.construct_scalar(node)
477        if value:
478            raise ConstructorError("while constructing a Python name", node.start_mark,
479                    "expected the empty value, but found %r" % value.encode('utf-8'),
480                    node.start_mark)
481        return self.find_python_name(suffix, node.start_mark)
482
483    def construct_python_module(self, suffix, node):
484        value = self.construct_scalar(node)
485        if value:
486            raise ConstructorError("while constructing a Python module", node.start_mark,
487                    "expected the empty value, but found %r" % value.encode('utf-8'),
488                    node.start_mark)
489        return self.find_python_module(suffix, node.start_mark)
490
491Constructor.add_constructor(
492    u'tag:yaml.org,2002:python/none',
493    Constructor.construct_yaml_null)
494
495Constructor.add_constructor(
496    u'tag:yaml.org,2002:python/bool',
497    Constructor.construct_yaml_bool)
498
499Constructor.add_constructor(
500    u'tag:yaml.org,2002:python/str',
501    Constructor.construct_python_str)
502
503Constructor.add_constructor(
504    u'tag:yaml.org,2002:python/unicode',
505    Constructor.construct_python_unicode)
506
507Constructor.add_constructor(
508    u'tag:yaml.org,2002:python/int',
509    Constructor.construct_yaml_int)
510
511Constructor.add_constructor(
512    u'tag:yaml.org,2002:python/long',
513    Constructor.construct_python_long)
514
515Constructor.add_constructor(
516    u'tag:yaml.org,2002:python/float',
517    Constructor.construct_yaml_float)
518
519Constructor.add_constructor(
520    u'tag:yaml.org,2002:python/complex',
521    Constructor.construct_python_complex)
522
523Constructor.add_constructor(
524    u'tag:yaml.org,2002:python/list',
525    Constructor.construct_yaml_seq)
526
527Constructor.add_constructor(
528    u'tag:yaml.org,2002:python/tuple',
529    Constructor.construct_python_tuple)
530
531Constructor.add_constructor(
532    u'tag:yaml.org,2002:python/dict',
533    Constructor.construct_yaml_map)
534
535Constructor.add_multi_constructor(
536    u'tag:yaml.org,2002:python/name:',
537    Constructor.construct_python_name)
538
539Constructor.add_multi_constructor(
540    u'tag:yaml.org,2002:python/module:',
541    Constructor.construct_python_module)
542
Note: See TracBrowser for help on using the repository browser.