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

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

Add pyrex-based bindings for the libyaml scanner.

RevLine 
[55]1
[133]2__all__ = ['BaseConstructor', 'SafeConstructor', 'Constructor',
3    'ConstructorError']
[57]4
[55]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
[139]19import binascii, re, sys
[55]20
21class ConstructorError(MarkedYAMLError):
22    pass
23
[195]24class BaseConstructor:
[55]25
[136]26    yaml_constructors = {}
27    yaml_multi_constructors = {}
28
29    def __init__(self):
[55]30        self.constructed_objects = {}
[142]31        self.recursive_objects = {}
[55]32
[136]33    def check_data(self):
[55]34        # If there are more documents available?
[136]35        return self.check_node()
[55]36
[136]37    def get_data(self):
[55]38        # Construct and return the next document.
[136]39        if self.check_node():
40            return self.construct_document(self.get_node())
[55]41
42    def __iter__(self):
43        # Iterator protocol.
[136]44        while self.check_node():
45            yield self.construct_document(self.get_node())
[55]46
47    def construct_document(self, node):
[136]48        data = self.construct_object(node)
[55]49        self.constructed_objects = {}
[142]50        self.recursive_objects = {}
[136]51        return data
[55]52
53    def construct_object(self, node):
54        if node in self.constructed_objects:
55            return self.constructed_objects[node]
[142]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
[136]60        constructor = None
[55]61        if node.tag in self.yaml_constructors:
[136]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)
[139]69                    break
[136]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
[139]83                else:
84                    print node.tag
[136]85        data = constructor(node)
86        self.constructed_objects[node] = data
[142]87        del self.recursive_objects[node]
[136]88        return data
[55]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,
[116]98                    node.start_mark)
[55]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,
[116]105                    node.start_mark)
[55]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,
[116]112                    node.start_mark)
[55]113        mapping = {}
[56]114        merge = None
[55]115        for key_node in node.value:
[56]116            if key_node.tag == u'tag:yaml.org,2002:merge':
117                if merge is not None:
[116]118                    raise ConstructorError("while constructing a mapping", node.start_mark,
119                            "found duplicate merge key", key_node.start_mark)
[58]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",
[116]128                                    node.start_mark,
[58]129                                    "expected a mapping for merging, but found %s"
[116]130                                    % subnode.id, subnode.start_mark)
[58]131                        merge.append(self.construct_mapping(subnode))
132                    merge.reverse()
133                else:
[116]134                    raise ConstructorError("while constructing a mapping", node.start_mark,
[58]135                            "expected a mapping or list of mappings for merging, but found %s"
[116]136                            % value_node.id, value_node.start_mark)
[56]137            elif key_node.tag == u'tag:yaml.org,2002:value':
138                if '=' in mapping:
[116]139                    raise ConstructorError("while construction a mapping", node.start_mark,
140                            "found duplicate value key", key_node.start_mark)
[56]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:
[116]148                    raise ConstructorError("while constructing a mapping", node.start_mark,
149                            "found unacceptable key (%s)" % exc, key_node.start_mark)
[56]150                if duplicate_key:
[116]151                    raise ConstructorError("while constructing a mapping", node.start_mark,
152                            "found duplicate key", key_node.start_mark)
[56]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)
[55]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,
[116]166                    node.start_mark)
[55]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
[136]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)
[55]185
[133]186class SafeConstructor(BaseConstructor):
[55]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
[168]233    inf_value = 1e300
[173]234    while inf_value != inf_value*inf_value:
[168]235        inf_value *= inf_value
[173]236    nan_value = -inf_value/inf_value   # Trying to make a quiet NaN (like C99).
[55]237
238    def construct_yaml_float(self, node):
239        value = str(self.construct_scalar(node))
[175]240        value = value.replace('_', '').lower()
[55]241        sign = +1
242        if value[0] == '-':
[58]243            sign = -1
[55]244        if value[0] in '+-':
245            value = value[1:]
[175]246        if value == '.inf':
[55]247            return sign*self.inf_value
[175]248        elif value == '.nan':
[55]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:
[170]260            return sign*float(value)
[55]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,
[116]268                    "failed to decode base64 data: %s" % exc, node.start_mark) 
[55]269
[56]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]?)
[58]274                (?:(?:[Tt]|[ \t]+)
[56]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]?)
[58]280                (?::(?P<tz_minute>[0-9][0-9])?)?))?)?$''', re.X)
[56]281
282    def construct_yaml_timestamp(self, node):
283        value = self.construct_scalar(node)
[58]284        match = self.timestamp_regexp.match(node.value)
[56]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']
[58]292        if fraction:
[56]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):
[116]305            raise ConstructorError("while constructing an ordered map", node.start_mark,
306                    "expected a sequence, but found %s" % node.id, node.start_mark)
[56]307        omap = []
308        for subnode in node.value:
309            if not isinstance(subnode, MappingNode):
[116]310                raise ConstructorError("while constructing an ordered map", node.start_mark,
[56]311                        "expected a mapping of length 1, but found %s" % subnode.id,
[116]312                        subnode.start_mark)
[58]313            if len(subnode.value) != 1:
[116]314                raise ConstructorError("while constructing an ordered map", node.start_mark,
[58]315                        "expected a single mapping item, but found %d items" % len(subnode.value),
[116]316                        subnode.start_mark)
[58]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
[56]322
323    def construct_yaml_pairs(self, node):
324        # Note: the same code as `construct_yaml_omap`.
325        if not isinstance(node, SequenceNode):
[116]326            raise ConstructorError("while constructing pairs", node.start_mark,
327                    "expected a sequence, but found %s" % node.id, node.start_mark)
[58]328        pairs = []
[56]329        for subnode in node.value:
330            if not isinstance(subnode, MappingNode):
[116]331                raise ConstructorError("while constructing pairs", node.start_mark,
[56]332                        "expected a mapping of length 1, but found %s" % subnode.id,
[116]333                        subnode.start_mark)
[58]334            if len(subnode.value) != 1:
[116]335                raise ConstructorError("while constructing pairs", node.start_mark,
[58]336                        "expected a single mapping item, but found %d items" % len(subnode.value),
[116]337                        subnode.start_mark)
[58]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
[56]343
344    def construct_yaml_set(self, node):
345        value = self.construct_mapping(node)
346        return set(value)
347
[55]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
[56]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
[136]361    def construct_yaml_object(self, node, cls):
[139]362        state = self.construct_mapping(node)
[136]363        data = cls.__new__(cls)
364        if hasattr(data, '__setstate__'):
[139]365            data.__setstate__(state)
[136]366        else:
[139]367            data.__dict__.update(state)
[136]368        return data
369
[57]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'),
[116]373                node.start_mark)
[57]374
[133]375SafeConstructor.add_constructor(
[55]376        u'tag:yaml.org,2002:null',
[133]377        SafeConstructor.construct_yaml_null)
[55]378
[133]379SafeConstructor.add_constructor(
[55]380        u'tag:yaml.org,2002:bool',
[133]381        SafeConstructor.construct_yaml_bool)
[55]382
[133]383SafeConstructor.add_constructor(
[55]384        u'tag:yaml.org,2002:int',
[133]385        SafeConstructor.construct_yaml_int)
[55]386
[133]387SafeConstructor.add_constructor(
[55]388        u'tag:yaml.org,2002:float',
[133]389        SafeConstructor.construct_yaml_float)
[55]390
[133]391SafeConstructor.add_constructor(
[58]392        u'tag:yaml.org,2002:binary',
[133]393        SafeConstructor.construct_yaml_binary)
[56]394
[58]395if datetime_available:
[133]396    SafeConstructor.add_constructor(
[58]397            u'tag:yaml.org,2002:timestamp',
[133]398            SafeConstructor.construct_yaml_timestamp)
[58]399
[133]400SafeConstructor.add_constructor(
[56]401        u'tag:yaml.org,2002:omap',
[133]402        SafeConstructor.construct_yaml_omap)
[56]403
[133]404SafeConstructor.add_constructor(
[56]405        u'tag:yaml.org,2002:pairs',
[133]406        SafeConstructor.construct_yaml_pairs)
[56]407
[133]408SafeConstructor.add_constructor(
[56]409        u'tag:yaml.org,2002:set',
[133]410        SafeConstructor.construct_yaml_set)
[56]411
[133]412SafeConstructor.add_constructor(
[55]413        u'tag:yaml.org,2002:str',
[133]414        SafeConstructor.construct_yaml_str)
[55]415
[133]416SafeConstructor.add_constructor(
[56]417        u'tag:yaml.org,2002:seq',
[133]418        SafeConstructor.construct_yaml_seq)
[56]419
[133]420SafeConstructor.add_constructor(
[56]421        u'tag:yaml.org,2002:map',
[133]422        SafeConstructor.construct_yaml_map)
[56]423
[133]424SafeConstructor.add_constructor(None,
425        SafeConstructor.construct_undefined)
[57]426
[133]427class Constructor(SafeConstructor):
[55]428
[139]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:
[146]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)
[139]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
[147]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
[139]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
[147]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.