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

Revision 56, 14.4 KB checked in by xi, 8 years ago (diff)

Constructor is done.

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