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

Revision 195, 23.5 KB checked in by xi, 8 years ago (diff)

Add pyrex-based bindings for the libyaml scanner.

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