source: branches/pyyaml3000/lib/yaml/constructor.py @ 57

Revision 57, 14.8 KB checked in by xi, 8 years ago (diff)

Fix a few bugs.

RevLine 
[55]1
[57]2__all__ = ['BaseConstructor', 'Constructor', 'ConstructorError',
3    'YAMLObject', 'YAMLObjectMetaclass']
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
[56]19import binascii, re
[55]20
21class ConstructorError(MarkedYAMLError):
22    pass
23
24class BaseConstructor:
25
26    def __init__(self, resolver):
27        self.resolver = resolver
28        self.constructed_objects = {}
29
30    def check(self):
31        # If there are more documents available?
32        return self.resolver.check()
33
34    def get(self):
35        # Construct and return the next document.
36        if self.resolver.check():
37            return self.construct_document(self.resolver.get())
38
39    def __iter__(self):
40        # Iterator protocol.
41        while self.resolver.check():
42            yield self.construct_document(self.resolver.get())
43
44    def construct_document(self, node):
45        native = self.construct_object(node)
46        self.constructed_objects = {}
47        return native
48
49    def construct_object(self, node):
50        if node in self.constructed_objects:
51            return self.constructed_objects[node]
52        if node.tag in self.yaml_constructors:
53            native = self.yaml_constructors[node.tag](self, node)
54        elif None in self.yaml_constructors:
55            native = self.yaml_constructors[None](self, node)
56        elif isinstance(node, ScalarNode):
57            native = self.construct_scalar(node)
58        elif isinstance(node, SequenceNode):
59            native = self.construct_sequence(node)
60        elif isinstance(node, MappingNode):
61            native = self.construct_mapping(node)
62        self.constructed_objects[node] = native
63        return native
64
65    def construct_scalar(self, node):
66        if not isinstance(node, ScalarNode):
67            if isinstance(node, MappingNode):
68                for key_node in node.value:
69                    if key_node.tag == u'tag:yaml.org,2002:value':
70                        return self.construct_scalar(node.value[key_node])
71            raise ConstructorError(None, None,
72                    "expected a scalar node, but found %s" % node.id,
73                    node.start_marker)
74        return node.value
75
76    def construct_sequence(self, node):
77        if not isinstance(node, SequenceNode):
78            raise ConstructorError(None, None,
79                    "expected a sequence node, but found %s" % node.id,
80                    node.start_marker)
81        return [self.construct_object(child) for child in node.value]
82
83    def construct_mapping(self, node):
84        if not isinstance(node, MappingNode):
85            raise ConstructorError(None, None,
86                    "expected a mapping node, but found %s" % node.id,
87                    node.start_marker)
88        mapping = {}
[56]89        merge = None
[55]90        for key_node in node.value:
[56]91            if key_node.tag == u'tag:yaml.org,2002:merge':
92                if merge is not None:
93                    raise ConstructorError("while constructing a mapping", node.start_marker,
94                            "found duplicate merge key", key_node.start_marker)
95                    value_node = node.value[key_node]
96                    if isinstance(value_node, MappingNode):
97                        merge = [self.construct_mapping(value_node)]
98                    elif isinstance(value_node, SequenceNode):
99                        merge = []
100                        for subnode in value_node.value:
101                            if not isinstance(subnode, MappingNode):
102                                raise ConstructorError("while constructing a mapping",
103                                        node.start_marker,
104                                        "expected a mapping for merging, but found %s"
105                                        % subnode.id, subnode.start_marker)
106                            merge.append(self.construct_mapping(subnode))
107                        merge.reverse()
108                    else:
109                        raise ConstructorError("while constructing a mapping", node.start_marker,
110                                "expected a mapping or list of mappings for merging, but found %s"
111                                % value_node.id, value_node.start_marker)
112            elif key_node.tag == u'tag:yaml.org,2002:value':
113                if '=' in mapping:
114                    raise ConstructorError("while construction a mapping", node.start_marker,
115                            "found duplicate value key", key_node.start_marker)
116                value = self.construct_object(node.value[key_node])
117                mapping['='] = value
118            else:
119                key = self.construct_object(key_node)
120                try:
121                    duplicate_key = key in mapping
122                except TypeError, exc:
123                    raise ConstructorError("while constructing a mapping", node.start_marker,
124                            "found unacceptable key (%s)" % exc, key_node.start_marker)
125                if duplicate_key:
126                    raise ConstructorError("while constructing a mapping", node.start_marker,
127                            "found duplicate key", key_node.start_marker)
128                value = self.construct_object(node.value[key_node])
129                mapping[key] = value
130        if merge is not None:
131            merge.append(mapping)
132            mapping = {}
133            for submapping in merge:
134                mapping.update(submapping)
[55]135        return mapping
136
137    def construct_pairs(self, node):
138        if not isinstance(node, MappingNode):
139            raise ConstructorError(None, None,
140                    "expected a mapping node, but found %s" % node.id,
141                    node.start_marker)
142        pairs = []
143        for key_node in node.value:
144            key = self.construct_object(key_node)
145            value = self.construct_object(node.value[key_node])
146            pairs.append((key, value))
147        return pairs
148
149    def add_constructor(cls, tag, constructor):
150        if not 'yaml_constructors' in cls.__dict__:
151            cls.yaml_constructors = cls.yaml_constructors.copy()
152        cls.yaml_constructors[tag] = constructor
153    add_constructor = classmethod(add_constructor)
154
155    yaml_constructors = {}
156
157class Constructor(BaseConstructor):
158
159    def construct_yaml_null(self, node):
160        self.construct_scalar(node)
161        return None
162
163    bool_values = {
164        u'y':       True,
165        u'yes':     True,
166        u'n':       False,
167        u'no':      False,
168        u'true':    True,
169        u'false':   False,
170        u'on':      True,
171        u'off':     False,
172    }
173
174    def construct_yaml_bool(self, node):
175        value = self.construct_scalar(node)
176        return self.bool_values[value.lower()]
177
178    def construct_yaml_int(self, node):
179        value = str(self.construct_scalar(node))
180        value = value.replace('_', '')
181        sign = +1
182        if value[0] == '-':
183            sign = -1
184        if value[0] in '+-':
185            value = value[1:]
186        if value == '0':
187            return 0
188        elif value.startswith('0b'):
189            return sign*int(value[2:], 2)
190        elif value.startswith('0x'):
191            return sign*int(value[2:], 16)
192        elif value[0] == '0':
193            return sign*int(value, 8)
194        elif ':' in value:
195            digits = [int(part) for part in value.split(':')]
196            digits.reverse()
197            base = 1
198            value = 0
199            for digit in digits:
200                value += digit*base
201                base *= 60
202            return sign*value
203        else:
204            return sign*int(value)
205
206    inf_value = 1e300000
207    nan_value = inf_value/inf_value
208
209    def construct_yaml_float(self, node):
210        value = str(self.construct_scalar(node))
211        value = value.replace('_', '')
212        sign = +1
213        if value[0] == '-':
214            value = -1
215        if value[0] in '+-':
216            value = value[1:]
217        if value.lower() == '.inf':
218            return sign*self.inf_value
219        elif value.lower() == '.nan':
220            return self.nan_value
221        elif ':' in value:
222            digits = [float(part) for part in value.split(':')]
223            digits.reverse()
224            base = 1
225            value = 0.0
226            for digit in digits:
227                value += digit*base
228                base *= 60
229            return sign*value
230        else:
231            return float(value)
232
233    def construct_yaml_binary(self, node):
234        value = self.construct_scalar(node)
235        try:
236            return str(value).decode('base64')
237        except (binascii.Error, UnicodeEncodeError), exc:
238            raise ConstructorError(None, None,
239                    "failed to decode base64 data: %s" % exc, node.start_mark) 
240
[56]241    timestamp_regexp = re.compile(
242            ur'''^(?P<year>[0-9][0-9][0-9][0-9])
243                -(?P<month>[0-9][0-9]?)
244                -(?P<day>[0-9][0-9]?)
245                (?:[Tt]|[ \t]+)
246                (?P<hour>[0-9][0-9]?)
247                :(?P<minute>[0-9][0-9])
248                :(?P<second>[0-9][0-9])
249                (?:\.(?P<fraction>[0-9]*))?
250                (?:[ \t]*(?:Z|(?P<tz_hour>[-+][0-9][0-9]?)
251                (?::(?P<tz_minute>[0-9][0-9])?)))?$''', re.X),
252
253    def construct_yaml_timestamp(self, node):
254        value = self.construct_scalar(node)
255        match = self.timestamp_expr.match(node.value)
256        values = match.groupdict()
257        for key in values:
258            if values[key]:
259                values[key] = int(values[key])
260            else:
261                values[key] = 0
262        fraction = values['fraction']
263        if micro:
264            while 10*fraction < 1000000:
265                fraction *= 10
266            values['fraction'] = fraction
267        stamp = datetime.datetime(values['year'], values['month'], values['day'],
268                values['hour'], values['minute'], values['second'], values['fraction'])
269        diff = datetime.timedelta(hours=values['tz_hour'], minutes=values['tz_minute'])
270        return stamp-diff
271
272    def construct_yaml_omap(self, node):
273        # Note: we do not check for duplicate keys, because it's too
274        # CPU-expensive.
275        if not isinstance(node, SequenceNode):
276            raise ConstructorError("while constructing an ordered map", node.start_marker,
277                    "expected a sequence, but found %s" % node.id, node.start_marker)
278        omap = []
279        for subnode in node.value:
280            if not isinstance(subnode, MappingNode):
281                raise ConstructorError("while constructing an ordered map", node.start_marker,
282                        "expected a mapping of length 1, but found %s" % subnode.id,
283                        subnode.start_marker)
284                if len(subnode.value) != 1:
285                    raise ConstructorError("while constructing an ordered map", node.start_marker,
286                            "expected a single mapping item, but found %d items" % len(subnode.value),
287                            subnode.start_marker)
288                key_node = subnode.value.keys()[0]
289                key = self.construct_object(key_node)
290                value = self.construct_object(subnode.value[key_node])
291                omap.append((key, value))
292
293    def construct_yaml_pairs(self, node):
294        # Note: the same code as `construct_yaml_omap`.
295        if not isinstance(node, SequenceNode):
296            raise ConstructorError("while constructing pairs", node.start_marker,
297                    "expected a sequence, but found %s" % node.id, node.start_marker)
298        omap = []
299        for subnode in node.value:
300            if not isinstance(subnode, MappingNode):
301                raise ConstructorError("while constructing pairs", node.start_marker,
302                        "expected a mapping of length 1, but found %s" % subnode.id,
303                        subnode.start_marker)
304                if len(subnode.value) != 1:
305                    raise ConstructorError("while constructing pairs", node.start_marker,
306                            "expected a single mapping item, but found %d items" % len(subnode.value),
307                            subnode.start_marker)
308                key_node = subnode.value.keys()[0]
309                key = self.construct_object(key_node)
310                value = self.construct_object(subnode.value[key_node])
311                omap.append((key, value))
312
313    def construct_yaml_set(self, node):
314        value = self.construct_mapping(node)
315        return set(value)
316
[55]317    def construct_yaml_str(self, node):
318        value = self.construct_scalar(node)
319        try:
320            return str(value)
321        except UnicodeEncodeError:
322            return value
323
[56]324    def construct_yaml_seq(self, node):
325        return self.construct_sequence(node)
326
327    def construct_yaml_map(self, node):
328        return self.construct_mapping(node)
329
[57]330    def construct_undefined(self, node):
331        raise ConstructorError(None, None,
332                "could not determine a constructor for the tag %r" % node.tag.encode('utf-8'),
333                node.start_marker)
334
[55]335Constructor.add_constructor(
336        u'tag:yaml.org,2002:null',
337        Constructor.construct_yaml_null)
338
339Constructor.add_constructor(
340        u'tag:yaml.org,2002:bool',
341        Constructor.construct_yaml_bool)
342
343Constructor.add_constructor(
344        u'tag:yaml.org,2002:int',
345        Constructor.construct_yaml_int)
346
347Constructor.add_constructor(
348        u'tag:yaml.org,2002:float',
349        Constructor.construct_yaml_float)
350
351Constructor.add_constructor(
[56]352        u'tag:yaml.org,2002:timestamp',
353        Constructor.construct_yaml_timestamp)
354
355Constructor.add_constructor(
356        u'tag:yaml.org,2002:omap',
357        Constructor.construct_yaml_omap)
358
359Constructor.add_constructor(
360        u'tag:yaml.org,2002:pairs',
361        Constructor.construct_yaml_pairs)
362
363Constructor.add_constructor(
364        u'tag:yaml.org,2002:set',
365        Constructor.construct_yaml_set)
366
367Constructor.add_constructor(
[55]368        u'tag:yaml.org,2002:str',
369        Constructor.construct_yaml_str)
370
[56]371Constructor.add_constructor(
372        u'tag:yaml.org,2002:seq',
373        Constructor.construct_yaml_seq)
374
375Constructor.add_constructor(
376        u'tag:yaml.org,2002:map',
377        Constructor.construct_yaml_map)
378
[57]379Constructor.add_constructor(None,
380        Constructor.construct_undefined)
381
[55]382class YAMLObjectMetaclass(type):
383
384    def __init__(cls, name, bases, kwds):
385        super(YAMLObjectMetaclass, cls).__init__(name, bases, kwds)
386        if 'yaml_tag' in kwds and kwds['yaml_tag'] is not None:
387            cls.yaml_constructor_class.add_constructor(cls.yaml_tag, cls.from_yaml)
388
389class YAMLObject(object):
390
391    __metaclass__ = YAMLObjectMetaclass
392
393    yaml_constructor_class = Constructor
394
395    yaml_tag = None
396
397    def from_yaml(cls, constructor, node):
398        raise ConstructorError(None, None,
399                "found undefined constructor for the tag %r"
400                % node.tag.encode('utf-8'), node.start_marker)
401    from_yaml = classmethod(from_yaml)
402
403    def to_yaml(self):
404        assert False    # needs dumper
405
Note: See TracBrowser for help on using the repository browser.