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

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

Ready for the initial release.

Line 
1
2__all__ = ['BaseConstructor', 'Constructor', 'ConstructorError',
3    'YAMLObject', 'YAMLObjectMetaclass']
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
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 = {}
89        merge = None
90        for key_node in node.value:
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)
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            sign = -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_marker) 
240
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_regexp.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 fraction:
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        return omap
293
294    def construct_yaml_pairs(self, node):
295        # Note: the same code as `construct_yaml_omap`.
296        if not isinstance(node, SequenceNode):
297            raise ConstructorError("while constructing pairs", node.start_marker,
298                    "expected a sequence, but found %s" % node.id, node.start_marker)
299        pairs = []
300        for subnode in node.value:
301            if not isinstance(subnode, MappingNode):
302                raise ConstructorError("while constructing pairs", node.start_marker,
303                        "expected a mapping of length 1, but found %s" % subnode.id,
304                        subnode.start_marker)
305            if len(subnode.value) != 1:
306                raise ConstructorError("while constructing pairs", node.start_marker,
307                        "expected a single mapping item, but found %d items" % len(subnode.value),
308                        subnode.start_marker)
309            key_node = subnode.value.keys()[0]
310            key = self.construct_object(key_node)
311            value = self.construct_object(subnode.value[key_node])
312            pairs.append((key, value))
313        return pairs
314
315    def construct_yaml_set(self, node):
316        value = self.construct_mapping(node)
317        return set(value)
318
319    def construct_yaml_str(self, node):
320        value = self.construct_scalar(node)
321        try:
322            return str(value)
323        except UnicodeEncodeError:
324            return value
325
326    def construct_yaml_seq(self, node):
327        return self.construct_sequence(node)
328
329    def construct_yaml_map(self, node):
330        return self.construct_mapping(node)
331
332    def construct_undefined(self, node):
333        raise ConstructorError(None, None,
334                "could not determine a constructor for the tag %r" % node.tag.encode('utf-8'),
335                node.start_marker)
336
337Constructor.add_constructor(
338        u'tag:yaml.org,2002:null',
339        Constructor.construct_yaml_null)
340
341Constructor.add_constructor(
342        u'tag:yaml.org,2002:bool',
343        Constructor.construct_yaml_bool)
344
345Constructor.add_constructor(
346        u'tag:yaml.org,2002:int',
347        Constructor.construct_yaml_int)
348
349Constructor.add_constructor(
350        u'tag:yaml.org,2002:float',
351        Constructor.construct_yaml_float)
352
353Constructor.add_constructor(
354        u'tag:yaml.org,2002:binary',
355        Constructor.construct_yaml_binary)
356
357if datetime_available:
358    Constructor.add_constructor(
359            u'tag:yaml.org,2002:timestamp',
360            Constructor.construct_yaml_timestamp)
361
362Constructor.add_constructor(
363        u'tag:yaml.org,2002:omap',
364        Constructor.construct_yaml_omap)
365
366Constructor.add_constructor(
367        u'tag:yaml.org,2002:pairs',
368        Constructor.construct_yaml_pairs)
369
370Constructor.add_constructor(
371        u'tag:yaml.org,2002:set',
372        Constructor.construct_yaml_set)
373
374Constructor.add_constructor(
375        u'tag:yaml.org,2002:str',
376        Constructor.construct_yaml_str)
377
378Constructor.add_constructor(
379        u'tag:yaml.org,2002:seq',
380        Constructor.construct_yaml_seq)
381
382Constructor.add_constructor(
383        u'tag:yaml.org,2002:map',
384        Constructor.construct_yaml_map)
385
386Constructor.add_constructor(None,
387        Constructor.construct_undefined)
388
389class YAMLObjectMetaclass(type):
390
391    def __init__(cls, name, bases, kwds):
392        super(YAMLObjectMetaclass, cls).__init__(name, bases, kwds)
393        if 'yaml_tag' in kwds and kwds['yaml_tag'] is not None:
394            cls.yaml_constructor.add_constructor(cls.yaml_tag, cls.from_yaml)
395
396class YAMLObject(object):
397
398    __metaclass__ = YAMLObjectMetaclass
399
400    yaml_constructor = Constructor
401
402    yaml_tag = None
403
404    def from_yaml(cls, constructor, node):
405        raise ConstructorError(None, None,
406                "found undefined constructor for the tag %r"
407                % node.tag.encode('utf-8'), node.start_marker)
408    from_yaml = classmethod(from_yaml)
409
410    def to_yaml(self):
411        assert False    # needs dumper
412
Note: See TracBrowser for help on using the repository browser.