Ticket #5: constructor.2.py

File constructor.2.py, 23.9 KB (added by Peter Murphy (pkmurphy at postmaster dot co dot uk), 8 years ago)

This is my modified version of constructor.py - second version.

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