source: trunk/packages/pyyaml/lib/yaml/constructor.py @ 1677

Last change on this file since 1677 was 898, checked in by hartmans, 16 years ago

Add pyyaml and libyaml packages
backported from lenny.
There is discussion about how these should go in the repository; these are added in this form
in order to make forward progress.

File size: 24.5 KB
Line 
1
2__all__ = ['BaseConstructor', 'SafeConstructor', 'Constructor',
3    'ConstructorError']
4
5from error import *
6from nodes import *
7
8import datetime
9
10try:
11    set
12except NameError:
13    from sets import Set as set
14
15import binascii, re, sys, types
16
17class ConstructorError(MarkedYAMLError):
18    pass
19
20class BaseConstructor(object):
21
22    yaml_constructors = {}
23    yaml_multi_constructors = {}
24
25    def __init__(self):
26        self.constructed_objects = {}
27        self.recursive_objects = {}
28        self.state_generators = []
29        self.deep_construct = False
30
31    def check_data(self):
32        # If there are more documents available?
33        return self.check_node()
34
35    def get_data(self):
36        # Construct and return the next document.
37        if self.check_node():
38            return self.construct_document(self.get_node())
39
40    def construct_document(self, node):
41        data = self.construct_object(node)
42        while self.state_generators:
43            state_generators = self.state_generators
44            self.state_generators = []
45            for generator in state_generators:
46                for dummy in generator:
47                    pass
48        self.constructed_objects = {}
49        self.recursive_objects = {}
50        self.deep_construct = False
51        return data
52
53    def construct_object(self, node, deep=False):
54        if deep:
55            old_deep = self.deep_construct
56            self.deep_construct = True
57        if node in self.constructed_objects:
58            return self.constructed_objects[node]
59        if node in self.recursive_objects:
60            raise ConstructorError(None, None,
61                    "found unconstructable recursive node", node.start_mark)
62        self.recursive_objects[node] = None
63        constructor = None
64        state_constructor = None
65        tag_suffix = None
66        if node.tag in self.yaml_constructors:
67            constructor = self.yaml_constructors[node.tag]
68        else:
69            for tag_prefix in self.yaml_multi_constructors:
70                if node.tag.startswith(tag_prefix):
71                    tag_suffix = node.tag[len(tag_prefix):]
72                    constructor = self.yaml_multi_constructors[tag_prefix]
73                    break
74            else:
75                if None in self.yaml_multi_constructors:
76                    tag_suffix = node.tag
77                    constructor = self.yaml_multi_constructors[None]
78                elif None in self.yaml_constructors:
79                    constructor = self.yaml_constructors[None]
80                elif isinstance(node, ScalarNode):
81                    constructor = self.__class__.construct_scalar
82                elif isinstance(node, SequenceNode):
83                    constructor = self.__class__.construct_sequence
84                elif isinstance(node, MappingNode):
85                    constructor = self.__class__.construct_mapping
86        if tag_suffix is None:
87            data = constructor(self, node)
88        else:
89            data = constructor(self, tag_suffix, node)
90        if isinstance(data, types.GeneratorType):
91            generator = data
92            data = generator.next()
93            if self.deep_construct:
94                for dummy in generator:
95                    pass
96            else:
97                self.state_generators.append(generator)
98        self.constructed_objects[node] = data
99        del self.recursive_objects[node]
100        if deep:
101            self.deep_construct = old_deep
102        return data
103
104    def construct_scalar(self, node):
105        if not isinstance(node, ScalarNode):
106            raise ConstructorError(None, None,
107                    "expected a scalar node, but found %s" % node.id,
108                    node.start_mark)
109        return node.value
110
111    def construct_sequence(self, node, deep=False):
112        if not isinstance(node, SequenceNode):
113            raise ConstructorError(None, None,
114                    "expected a sequence node, but found %s" % node.id,
115                    node.start_mark)
116        return [self.construct_object(child, deep=deep)
117                for child in node.value]
118
119    def construct_mapping(self, node, deep=False):
120        if not isinstance(node, MappingNode):
121            raise ConstructorError(None, None,
122                    "expected a mapping node, but found %s" % node.id,
123                    node.start_mark)
124        mapping = {}
125        for key_node, value_node in node.value:
126            key = self.construct_object(key_node, deep=deep)
127            try:
128                hash(key)
129            except TypeError, exc:
130                raise ConstructorError("while constructing a mapping", node.start_mark,
131                        "found unacceptable key (%s)" % exc, key_node.start_mark)
132            value = self.construct_object(value_node, deep=deep)
133            mapping[key] = value
134        return mapping
135
136    def construct_pairs(self, node, deep=False):
137        if not isinstance(node, MappingNode):
138            raise ConstructorError(None, None,
139                    "expected a mapping node, but found %s" % node.id,
140                    node.start_mark)
141        pairs = []
142        for key_node, value_node in node.value:
143            key = self.construct_object(key_node, deep=deep)
144            value = self.construct_object(value_node, deep=deep)
145            pairs.append((key, value))
146        return pairs
147
148    def add_constructor(cls, tag, constructor):
149        if not 'yaml_constructors' in cls.__dict__:
150            cls.yaml_constructors = cls.yaml_constructors.copy()
151        cls.yaml_constructors[tag] = constructor
152    add_constructor = classmethod(add_constructor)
153
154    def add_multi_constructor(cls, tag_prefix, multi_constructor):
155        if not 'yaml_multi_constructors' in cls.__dict__:
156            cls.yaml_multi_constructors = cls.yaml_multi_constructors.copy()
157        cls.yaml_multi_constructors[tag_prefix] = multi_constructor
158    add_multi_constructor = classmethod(add_multi_constructor)
159
160class SafeConstructor(BaseConstructor):
161
162    def construct_scalar(self, node):
163        if isinstance(node, MappingNode):
164            for key_node, value_node in node.value:
165                if key_node.tag == u'tag:yaml.org,2002:value':
166                    return self.construct_scalar(value_node)
167        return BaseConstructor.construct_scalar(self, node)
168
169    def flatten_mapping(self, node):
170        merge = []
171        index = 0
172        while index < len(node.value):
173            key_node, value_node = node.value[index]
174            if key_node.tag == u'tag:yaml.org,2002:merge':
175                del node.value[index]
176                if isinstance(value_node, MappingNode):
177                    self.flatten_mapping(value_node)
178                    merge.extend(value_node.value)
179                elif isinstance(value_node, SequenceNode):
180                    submerge = []
181                    for subnode in value_node.value:
182                        if not isinstance(subnode, MappingNode):
183                            raise ConstructorError("while constructing a mapping",
184                                    node.start_mark,
185                                    "expected a mapping for merging, but found %s"
186                                    % subnode.id, subnode.start_mark)
187                        self.flatten_mapping(subnode)
188                        submerge.append(subnode.value)
189                    submerge.reverse()
190                    for value in submerge:
191                        merge.extend(value)
192                else:
193                    raise ConstructorError("while constructing a mapping", node.start_mark,
194                            "expected a mapping or list of mappings for merging, but found %s"
195                            % value_node.id, value_node.start_mark)
196            elif key_node.tag == u'tag:yaml.org,2002:value':
197                key_node.tag = u'tag:yaml.org,2002:str'
198                index += 1
199            else:
200                index += 1
201        if merge:
202            node.value = merge + node.value
203
204    def construct_mapping(self, node, deep=False):
205        if isinstance(node, MappingNode):
206            self.flatten_mapping(node)
207        return BaseConstructor.construct_mapping(self, node, deep=deep)
208
209    def construct_yaml_null(self, node):
210        self.construct_scalar(node)
211        return None
212
213    bool_values = {
214        u'yes':     True,
215        u'no':      False,
216        u'true':    True,
217        u'false':   False,
218        u'on':      True,
219        u'off':     False,
220    }
221
222    def construct_yaml_bool(self, node):
223        value = self.construct_scalar(node)
224        return self.bool_values[value.lower()]
225
226    def construct_yaml_int(self, node):
227        value = str(self.construct_scalar(node))
228        value = value.replace('_', '')
229        sign = +1
230        if value[0] == '-':
231            sign = -1
232        if value[0] in '+-':
233            value = value[1:]
234        if value == '0':
235            return 0
236        elif value.startswith('0b'):
237            return sign*int(value[2:], 2)
238        elif value.startswith('0x'):
239            return sign*int(value[2:], 16)
240        elif value[0] == '0':
241            return sign*int(value, 8)
242        elif ':' in value:
243            digits = [int(part) for part in value.split(':')]
244            digits.reverse()
245            base = 1
246            value = 0
247            for digit in digits:
248                value += digit*base
249                base *= 60
250            return sign*value
251        else:
252            return sign*int(value)
253
254    inf_value = 1e300
255    while inf_value != inf_value*inf_value:
256        inf_value *= inf_value
257    nan_value = -inf_value/inf_value   # Trying to make a quiet NaN (like C99).
258
259    def construct_yaml_float(self, node):
260        value = str(self.construct_scalar(node))
261        value = value.replace('_', '').lower()
262        sign = +1
263        if value[0] == '-':
264            sign = -1
265        if value[0] in '+-':
266            value = value[1:]
267        if value == '.inf':
268            return sign*self.inf_value
269        elif value == '.nan':
270            return self.nan_value
271        elif ':' in value:
272            digits = [float(part) for part in value.split(':')]
273            digits.reverse()
274            base = 1
275            value = 0.0
276            for digit in digits:
277                value += digit*base
278                base *= 60
279            return sign*value
280        else:
281            return sign*float(value)
282
283    def construct_yaml_binary(self, node):
284        value = self.construct_scalar(node)
285        try:
286            return str(value).decode('base64')
287        except (binascii.Error, UnicodeEncodeError), exc:
288            raise ConstructorError(None, None,
289                    "failed to decode base64 data: %s" % exc, node.start_mark) 
290
291    timestamp_regexp = re.compile(
292            ur'''^(?P<year>[0-9][0-9][0-9][0-9])
293                -(?P<month>[0-9][0-9]?)
294                -(?P<day>[0-9][0-9]?)
295                (?:(?:[Tt]|[ \t]+)
296                (?P<hour>[0-9][0-9]?)
297                :(?P<minute>[0-9][0-9])
298                :(?P<second>[0-9][0-9])
299                (?:\.(?P<fraction>[0-9]*))?
300                (?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
301                (?::(?P<tz_minute>[0-9][0-9]))?))?)?$''', re.X)
302
303    def construct_yaml_timestamp(self, node):
304        value = self.construct_scalar(node)
305        match = self.timestamp_regexp.match(node.value)
306        values = match.groupdict()
307        year = int(values['year'])
308        month = int(values['month'])
309        day = int(values['day'])
310        if not values['hour']:
311            return datetime.date(year, month, day)
312        hour = int(values['hour'])
313        minute = int(values['minute'])
314        second = int(values['second'])
315        fraction = 0
316        if values['fraction']:
317            fraction = int(values['fraction'][:6].ljust(6, '0'))
318        delta = None
319        if values['tz_sign']:
320            tz_hour = int(values['tz_hour'])
321            tz_minute = int(values['tz_minute'] or 0)
322            delta = datetime.timedelta(hours=tz_hour, minutes=tz_minute)
323            if values['tz_sign'] == '-':
324                delta = -delta
325        data = datetime.datetime(year, month, day, hour, minute, second, fraction)
326        if delta:
327            data -= delta
328        return data
329
330    def construct_yaml_omap(self, node):
331        # Note: we do not check for duplicate keys, because it's too
332        # CPU-expensive.
333        omap = []
334        yield omap
335        if not isinstance(node, SequenceNode):
336            raise ConstructorError("while constructing an ordered map", node.start_mark,
337                    "expected a sequence, but found %s" % node.id, node.start_mark)
338        for subnode in node.value:
339            if not isinstance(subnode, MappingNode):
340                raise ConstructorError("while constructing an ordered map", node.start_mark,
341                        "expected a mapping of length 1, but found %s" % subnode.id,
342                        subnode.start_mark)
343            if len(subnode.value) != 1:
344                raise ConstructorError("while constructing an ordered map", node.start_mark,
345                        "expected a single mapping item, but found %d items" % len(subnode.value),
346                        subnode.start_mark)
347            key_node, value_node = subnode.value[0]
348            key = self.construct_object(key_node)
349            value = self.construct_object(value_node)
350            omap.append((key, value))
351
352    def construct_yaml_pairs(self, node):
353        # Note: the same code as `construct_yaml_omap`.
354        pairs = []
355        yield pairs
356        if not isinstance(node, SequenceNode):
357            raise ConstructorError("while constructing pairs", node.start_mark,
358                    "expected a sequence, but found %s" % node.id, node.start_mark)
359        for subnode in node.value:
360            if not isinstance(subnode, MappingNode):
361                raise ConstructorError("while constructing pairs", node.start_mark,
362                        "expected a mapping of length 1, but found %s" % subnode.id,
363                        subnode.start_mark)
364            if len(subnode.value) != 1:
365                raise ConstructorError("while constructing pairs", node.start_mark,
366                        "expected a single mapping item, but found %d items" % len(subnode.value),
367                        subnode.start_mark)
368            key_node, value_node = subnode.value[0]
369            key = self.construct_object(key_node)
370            value = self.construct_object(value_node)
371            pairs.append((key, value))
372
373    def construct_yaml_set(self, node):
374        data = set()
375        yield data
376        value = self.construct_mapping(node)
377        data.update(value)
378
379    def construct_yaml_str(self, node):
380        value = self.construct_scalar(node)
381        try:
382            return value.encode('ascii')
383        except UnicodeEncodeError:
384            return value
385
386    def construct_yaml_seq(self, node):
387        data = []
388        yield data
389        data.extend(self.construct_sequence(node))
390
391    def construct_yaml_map(self, node):
392        data = {}
393        yield data
394        value = self.construct_mapping(node)
395        data.update(value)
396
397    def construct_yaml_object(self, node, cls):
398        data = cls.__new__(cls)
399        yield data
400        if hasattr(data, '__setstate__'):
401            state = self.construct_mapping(node, deep=True)
402            data.__setstate__(state)
403        else:
404            state = self.construct_mapping(node)
405            data.__dict__.update(state)
406
407    def construct_undefined(self, node):
408        raise ConstructorError(None, None,
409                "could not determine a constructor for the tag %r" % node.tag.encode('utf-8'),
410                node.start_mark)
411
412SafeConstructor.add_constructor(
413        u'tag:yaml.org,2002:null',
414        SafeConstructor.construct_yaml_null)
415
416SafeConstructor.add_constructor(
417        u'tag:yaml.org,2002:bool',
418        SafeConstructor.construct_yaml_bool)
419
420SafeConstructor.add_constructor(
421        u'tag:yaml.org,2002:int',
422        SafeConstructor.construct_yaml_int)
423
424SafeConstructor.add_constructor(
425        u'tag:yaml.org,2002:float',
426        SafeConstructor.construct_yaml_float)
427
428SafeConstructor.add_constructor(
429        u'tag:yaml.org,2002:binary',
430        SafeConstructor.construct_yaml_binary)
431
432SafeConstructor.add_constructor(
433        u'tag:yaml.org,2002:timestamp',
434        SafeConstructor.construct_yaml_timestamp)
435
436SafeConstructor.add_constructor(
437        u'tag:yaml.org,2002:omap',
438        SafeConstructor.construct_yaml_omap)
439
440SafeConstructor.add_constructor(
441        u'tag:yaml.org,2002:pairs',
442        SafeConstructor.construct_yaml_pairs)
443
444SafeConstructor.add_constructor(
445        u'tag:yaml.org,2002:set',
446        SafeConstructor.construct_yaml_set)
447
448SafeConstructor.add_constructor(
449        u'tag:yaml.org,2002:str',
450        SafeConstructor.construct_yaml_str)
451
452SafeConstructor.add_constructor(
453        u'tag:yaml.org,2002:seq',
454        SafeConstructor.construct_yaml_seq)
455
456SafeConstructor.add_constructor(
457        u'tag:yaml.org,2002:map',
458        SafeConstructor.construct_yaml_map)
459
460SafeConstructor.add_constructor(None,
461        SafeConstructor.construct_undefined)
462
463class Constructor(SafeConstructor):
464
465    def construct_python_str(self, node):
466        return self.construct_scalar(node).encode('utf-8')
467
468    def construct_python_unicode(self, node):
469        return self.construct_scalar(node)
470
471    def construct_python_long(self, node):
472        return long(self.construct_yaml_int(node))
473
474    def construct_python_complex(self, node):
475       return complex(self.construct_scalar(node))
476
477    def construct_python_tuple(self, node):
478        return tuple(self.construct_sequence(node))
479
480    def find_python_module(self, name, mark):
481        if not name:
482            raise ConstructorError("while constructing a Python module", mark,
483                    "expected non-empty name appended to the tag", mark)
484        try:
485            __import__(name)
486        except ImportError, exc:
487            raise ConstructorError("while constructing a Python module", mark,
488                    "cannot find module %r (%s)" % (name.encode('utf-8'), exc), mark)
489        return sys.modules[name]
490
491    def find_python_name(self, name, mark):
492        if not name:
493            raise ConstructorError("while constructing a Python object", mark,
494                    "expected non-empty name appended to the tag", mark)
495        if u'.' in name:
496            # Python 2.4 only
497            #module_name, object_name = name.rsplit('.', 1)
498            items = name.split('.')
499            object_name = items.pop()
500            module_name = '.'.join(items)
501        else:
502            module_name = '__builtin__'
503            object_name = name
504        try:
505            __import__(module_name)
506        except ImportError, exc:
507            raise ConstructorError("while constructing a Python object", mark,
508                    "cannot find module %r (%s)" % (module_name.encode('utf-8'), exc), mark)
509        module = sys.modules[module_name]
510        if not hasattr(module, object_name):
511            raise ConstructorError("while constructing a Python object", mark,
512                    "cannot find %r in the module %r" % (object_name.encode('utf-8'),
513                        module.__name__), mark)
514        return getattr(module, object_name)
515
516    def construct_python_name(self, suffix, node):
517        value = self.construct_scalar(node)
518        if value:
519            raise ConstructorError("while constructing a Python name", node.start_mark,
520                    "expected the empty value, but found %r" % value.encode('utf-8'),
521                    node.start_mark)
522        return self.find_python_name(suffix, node.start_mark)
523
524    def construct_python_module(self, suffix, node):
525        value = self.construct_scalar(node)
526        if value:
527            raise ConstructorError("while constructing a Python module", node.start_mark,
528                    "expected the empty value, but found %r" % value.encode('utf-8'),
529                    node.start_mark)
530        return self.find_python_module(suffix, node.start_mark)
531
532    class classobj: pass
533
534    def make_python_instance(self, suffix, node,
535            args=None, kwds=None, newobj=False):
536        if not args:
537            args = []
538        if not kwds:
539            kwds = {}
540        cls = self.find_python_name(suffix, node.start_mark)
541        if newobj and isinstance(cls, type(self.classobj))  \
542                and not args and not kwds:
543            instance = self.classobj()
544            instance.__class__ = cls
545            return instance
546        elif newobj and isinstance(cls, type):
547            return cls.__new__(cls, *args, **kwds)
548        else:
549            return cls(*args, **kwds)
550
551    def set_python_instance_state(self, instance, state):
552        if hasattr(instance, '__setstate__'):
553            instance.__setstate__(state)
554        else:
555            slotstate = {}
556            if isinstance(state, tuple) and len(state) == 2:
557                state, slotstate = state
558            if hasattr(instance, '__dict__'):
559                instance.__dict__.update(state)
560            elif state:
561                slotstate.update(state)
562            for key, value in slotstate.items():
563                setattr(object, key, value)
564
565    def construct_python_object(self, suffix, node):
566        # Format:
567        #   !!python/object:module.name { ... state ... }
568        instance = self.make_python_instance(suffix, node, newobj=True)
569        yield instance
570        deep = hasattr(instance, '__setstate__')
571        state = self.construct_mapping(node, deep=deep)
572        self.set_python_instance_state(instance, state)
573
574    def construct_python_object_apply(self, suffix, node, newobj=False):
575        # Format:
576        #   !!python/object/apply       # (or !!python/object/new)
577        #   args: [ ... arguments ... ]
578        #   kwds: { ... keywords ... }
579        #   state: ... state ...
580        #   listitems: [ ... listitems ... ]
581        #   dictitems: { ... dictitems ... }
582        # or short format:
583        #   !!python/object/apply [ ... arguments ... ]
584        # The difference between !!python/object/apply and !!python/object/new
585        # is how an object is created, check make_python_instance for details.
586        if isinstance(node, SequenceNode):
587            args = self.construct_sequence(node, deep=True)
588            kwds = {}
589            state = {}
590            listitems = []
591            dictitems = {}
592        else:
593            value = self.construct_mapping(node, deep=True)
594            args = value.get('args', [])
595            kwds = value.get('kwds', {})
596            state = value.get('state', {})
597            listitems = value.get('listitems', [])
598            dictitems = value.get('dictitems', {})
599        instance = self.make_python_instance(suffix, node, args, kwds, newobj)
600        if state:
601            self.set_python_instance_state(instance, state)
602        if listitems:
603            instance.extend(listitems)
604        if dictitems:
605            for key in dictitems:
606                instance[key] = dictitems[key]
607        return instance
608
609    def construct_python_object_new(self, suffix, node):
610        return self.construct_python_object_apply(suffix, node, newobj=True)
611
612Constructor.add_constructor(
613    u'tag:yaml.org,2002:python/none',
614    Constructor.construct_yaml_null)
615
616Constructor.add_constructor(
617    u'tag:yaml.org,2002:python/bool',
618    Constructor.construct_yaml_bool)
619
620Constructor.add_constructor(
621    u'tag:yaml.org,2002:python/str',
622    Constructor.construct_python_str)
623
624Constructor.add_constructor(
625    u'tag:yaml.org,2002:python/unicode',
626    Constructor.construct_python_unicode)
627
628Constructor.add_constructor(
629    u'tag:yaml.org,2002:python/int',
630    Constructor.construct_yaml_int)
631
632Constructor.add_constructor(
633    u'tag:yaml.org,2002:python/long',
634    Constructor.construct_python_long)
635
636Constructor.add_constructor(
637    u'tag:yaml.org,2002:python/float',
638    Constructor.construct_yaml_float)
639
640Constructor.add_constructor(
641    u'tag:yaml.org,2002:python/complex',
642    Constructor.construct_python_complex)
643
644Constructor.add_constructor(
645    u'tag:yaml.org,2002:python/list',
646    Constructor.construct_yaml_seq)
647
648Constructor.add_constructor(
649    u'tag:yaml.org,2002:python/tuple',
650    Constructor.construct_python_tuple)
651
652Constructor.add_constructor(
653    u'tag:yaml.org,2002:python/dict',
654    Constructor.construct_yaml_map)
655
656Constructor.add_multi_constructor(
657    u'tag:yaml.org,2002:python/name:',
658    Constructor.construct_python_name)
659
660Constructor.add_multi_constructor(
661    u'tag:yaml.org,2002:python/module:',
662    Constructor.construct_python_module)
663
664Constructor.add_multi_constructor(
665    u'tag:yaml.org,2002:python/object:',
666    Constructor.construct_python_object)
667
668Constructor.add_multi_constructor(
669    u'tag:yaml.org,2002:python/object/apply:',
670    Constructor.construct_python_object_apply)
671
672Constructor.add_multi_constructor(
673    u'tag:yaml.org,2002:python/object/new:',
674    Constructor.construct_python_object_new)
675
Note: See TracBrowser for help on using the repository browser.