Ticket #5: constructor.4.py

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

The final version of constructor.py? (Solves sets.)

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# While we are in the middle of constructing a recursive tuple, we may encounter other things
31# that contain this tuple. For quicker store, we take a note of them. What we do is make a key
32# (actually by node id again), and the values are a list containing of structures containing this
33# tuple directly.
34
35    def __init__(self):
36        self.constructed_objects = {}
37        self.recursive_objects = {}
38
39# PKM - We now add an extra map that contains a map of nodes to temporary sequences. These
40# temporary sequences can be used in place of tuples until all tuples have been constructed. Then
41# these sequences are "replaced" by the actual tuples.
42
43        self.yaml_tuple_replacement = {};
44
45    def check_data(self):
46        # If there are more documents available?
47        return self.check_node()
48
49    def get_data(self):
50        # Construct and return the next document.
51        if self.check_node():
52            return self.construct_document(self.get_node())
53
54    def __iter__(self):
55        # Iterator protocol.
56        while self.check_node():
57            yield self.construct_document(self.get_node())
58
59    def construct_document(self, node):
60        data = self.construct_object(node)
61        self.constructed_objects = {}
62        self.recursive_objects = {}
63        self.yaml_tuple_replacement = {}; # PKM2006.
64        return data
65
66    def construct_object(self, node):
67        if node in self.constructed_objects:
68            return self.constructed_objects[node]
69           
70#        if node in self.recursive_objects:
71#            raise ConstructorError(None, None,
72#                    "found recursive node", node.start_mark)
73#        self.recursive_objects[node] = None
74
75        constructor = None
76        if node.tag in self.yaml_constructors:
77            constructor = lambda node: self.yaml_constructors[node.tag](self, node) #Modify BBB2
78        else:
79            for tag_prefix in self.yaml_multi_constructors:
80                if node.tag.startswith(tag_prefix):
81                    tag_suffix = node.tag[len(tag_prefix):]
82                    constructor = lambda node:  \
83                            self.yaml_multi_constructors[tag_prefix](self, tag_suffix, node)
84                    break
85            else:
86                if None in self.yaml_multi_constructors:
87                    constructor = lambda node:  \
88                            self.yaml_multi_constructors[None](self, node.tag, node)
89                elif None in self.yaml_constructors:
90                    constructor = lambda node:  \
91                            self.yaml_constructors[None](self, node)
92                elif isinstance(node, ScalarNode):
93                    constructor = self.construct_scalar
94                elif isinstance(node, SequenceNode):
95                    constructor = self.construct_sequence
96                elif isinstance(node, MappingNode):
97                    constructor = self.construct_mapping
98                else:
99                    pass; #print node.tag PKM
100       
101        data = constructor(node) #Modify BBB1
102        self.constructed_objects[node] = data
103#        del self.recursive_objects[node]
104        return data
105
106    def construct_scalar(self, node):
107        if not isinstance(node, ScalarNode):
108            if isinstance(node, MappingNode):
109                for key_node in node.value:
110                    if key_node.tag == u'tag:yaml.org,2002:value':
111                        return self.construct_scalar(node.value[key_node])
112            raise ConstructorError(None, None,
113                    "expected a scalar node, but found %s" % node.id,
114                    node.start_mark)
115        return node.value
116
117    def construct_sequence(self, node, setkey = True):
118        if not isinstance(node, SequenceNode):
119            raise ConstructorError(None, None,
120                    "expected a sequence node, but found %s" % node.id,
121                    node.start_mark)
122
123#PKM2006.
124                   
125        data = [];
126        if setkey: # PKM2006
127            self.constructed_objects[node] = data; # PKM2006
128        for child in node.value:
129            data.append(self.construct_object(child));
130        return data;                   
131                   
132#        return [self.construct_object(child) for child in node.value] #MODIFY BBB4
133
134    def construct_mapping(self, node, setkey = True):
135        if not isinstance(node, MappingNode):
136            raise ConstructorError(None, None,
137                    "expected a mapping node, but found %s" % node.id,
138                    node.start_mark)
139        mapping = {}
140        if setkey: # PKM2006
141            self.constructed_objects[node] = mapping; # PKM2006
142        merge = None
143        for key_node in node.value:
144            if key_node.tag == u'tag:yaml.org,2002:merge':
145                if merge is not None:
146                    raise ConstructorError("while constructing a mapping", node.start_mark,
147                            "found duplicate merge key", key_node.start_mark)
148                value_node = node.value[key_node]
149                if isinstance(value_node, MappingNode):
150                    merge = [self.construct_mapping(value_node)]
151                elif isinstance(value_node, SequenceNode):
152                    merge = []
153                    for subnode in value_node.value:
154                        if not isinstance(subnode, MappingNode):
155                            raise ConstructorError("while constructing a mapping",
156                                    node.start_mark,
157                                    "expected a mapping for merging, but found %s"
158                                    % subnode.id, subnode.start_mark)
159                        merge.append(self.construct_mapping(subnode))
160                    merge.reverse()
161                else:
162                    raise ConstructorError("while constructing a mapping", node.start_mark,
163                            "expected a mapping or list of mappings for merging, but found %s"
164                            % value_node.id, value_node.start_mark)
165            elif key_node.tag == u'tag:yaml.org,2002:value':
166                if '=' in mapping:
167                    raise ConstructorError("while construction a mapping", node.start_mark,
168                            "found duplicate value key", key_node.start_mark)
169                value = self.construct_object(node.value[key_node])
170                mapping['='] = value
171            else:
172                key = self.construct_object(key_node)
173                try:
174                    duplicate_key = key in mapping
175                except TypeError, exc:
176                    raise ConstructorError("while constructing a mapping", node.start_mark,
177                            "found unacceptable key (%s)" % exc, key_node.start_mark)
178                if duplicate_key:
179                    raise ConstructorError("while constructing a mapping", node.start_mark,
180                            "found duplicate key", key_node.start_mark)
181                value = self.construct_object(node.value[key_node])
182                mapping[key] = value
183        if merge is not None:
184            merge.append(mapping)
185            mapping.clear(); # PKM2006 = {}
186            for submapping in merge:
187                mapping.update(submapping)
188        return mapping
189
190    def construct_pairs(self, node):
191        if not isinstance(node, MappingNode):
192            raise ConstructorError(None, None,
193                    "expected a mapping node, but found %s" % node.id,
194                    node.start_mark)
195        pairs = []
196        self.constructed_objects[node] = pairs; # PKM2006
197        for key_node in node.value:
198            key = self.construct_object(key_node)
199            value = self.construct_object(node.value[key_node])
200            pairs.append((key, value))
201        return pairs
202
203    def add_constructor(cls, tag, constructor):
204        if not 'yaml_constructors' in cls.__dict__:
205            cls.yaml_constructors = cls.yaml_constructors.copy()
206        cls.yaml_constructors[tag] = constructor
207    add_constructor = classmethod(add_constructor)
208
209    def add_multi_constructor(cls, tag_prefix, multi_constructor):
210        if not 'yaml_multi_constructors' in cls.__dict__:
211            cls.yaml_multi_constructors = cls.yaml_multi_constructors.copy()
212        cls.yaml_multi_constructors[tag_prefix] = multi_constructor
213    add_multi_constructor = classmethod(add_multi_constructor)
214
215class SafeConstructor(BaseConstructor):
216
217    def construct_yaml_null(self, node):
218        self.construct_scalar(node)
219        return None
220
221    bool_values = {
222        u'yes':     True,
223        u'no':      False,
224        u'true':    True,
225        u'false':   False,
226        u'on':      True,
227        u'off':     False,
228    }
229
230    def construct_yaml_bool(self, node):
231        value = self.construct_scalar(node)
232        return self.bool_values[value.lower()]
233
234    def construct_yaml_int(self, node):
235        value = str(self.construct_scalar(node))
236        value = value.replace('_', '')
237        sign = +1
238        if value[0] == '-':
239            sign = -1
240        if value[0] in '+-':
241            value = value[1:]
242        if value == '0':
243            return 0
244        elif value.startswith('0b'):
245            return sign*int(value[2:], 2)
246        elif value.startswith('0x'):
247            return sign*int(value[2:], 16)
248        elif value[0] == '0':
249            return sign*int(value, 8)
250        elif ':' in value:
251            digits = [int(part) for part in value.split(':')]
252            digits.reverse()
253            base = 1
254            value = 0
255            for digit in digits:
256                value += digit*base
257                base *= 60
258            return sign*value
259        else:
260            return sign*int(value)
261
262    inf_value = 1e300
263    while inf_value != inf_value*inf_value:
264        inf_value *= inf_value
265    nan_value = -inf_value/inf_value   # Trying to make a quiet NaN (like C99).
266
267    def construct_yaml_float(self, node):
268        value = str(self.construct_scalar(node))
269        value = value.replace('_', '').lower()
270        sign = +1
271        if value[0] == '-':
272            sign = -1
273        if value[0] in '+-':
274            value = value[1:]
275        if value == '.inf':
276            return sign*self.inf_value
277        elif value == '.nan':
278            return self.nan_value
279        elif ':' in value:
280            digits = [float(part) for part in value.split(':')]
281            digits.reverse()
282            base = 1
283            value = 0.0
284            for digit in digits:
285                value += digit*base
286                base *= 60
287            return sign*value
288        else:
289            return sign*float(value)
290
291    def construct_yaml_binary(self, node):
292        value = self.construct_scalar(node)
293        try:
294            return str(value).decode('base64')
295        except (binascii.Error, UnicodeEncodeError), exc:
296            raise ConstructorError(None, None,
297                    "failed to decode base64 data: %s" % exc, node.start_mark) 
298
299    timestamp_regexp = re.compile(
300            ur'''^(?P<year>[0-9][0-9][0-9][0-9])
301                -(?P<month>[0-9][0-9]?)
302                -(?P<day>[0-9][0-9]?)
303                (?:(?:[Tt]|[ \t]+)
304                (?P<hour>[0-9][0-9]?)
305                :(?P<minute>[0-9][0-9])
306                :(?P<second>[0-9][0-9])
307                (?:\.(?P<fraction>[0-9]*))?
308                (?:[ \t]*(?:Z|(?P<tz_hour>[-+][0-9][0-9]?)
309                (?::(?P<tz_minute>[0-9][0-9])?)?))?)?$''', re.X)
310
311    def construct_yaml_timestamp(self, node):
312        value = self.construct_scalar(node)
313        match = self.timestamp_regexp.match(node.value)
314        values = match.groupdict()
315        for key in values:
316            if values[key]:
317                values[key] = int(values[key])
318            else:
319                values[key] = 0
320        fraction = values['fraction']
321        if fraction:
322            while 10*fraction < 1000000:
323                fraction *= 10
324            values['fraction'] = fraction
325        stamp = datetime.datetime(values['year'], values['month'], values['day'],
326                values['hour'], values['minute'], values['second'], values['fraction'])
327        diff = datetime.timedelta(hours=values['tz_hour'], minutes=values['tz_minute'])
328        return stamp-diff
329
330    def construct_yaml_omap(self, node):
331        # Note: we do not check for duplicate keys, because it's too
332        # CPU-expensive.
333        if not isinstance(node, SequenceNode):
334            raise ConstructorError("while constructing an ordered map", node.start_mark,
335                    "expected a sequence, but found %s" % node.id, node.start_mark)
336        omap = []
337        self.constructed_objects[node] = omap; # PKM2006
338        for subnode in node.value:
339            if not isinstance(subnode, MappingNode):
340                raise ConstructorError("while constructing an ordered map", node.start_mark,
341                        "expected a mapping of length 1, but found %s" % subnode.id,
342                        subnode.start_mark)
343            if len(subnode.value) != 1:
344                raise ConstructorError("while constructing an ordered map", node.start_mark,
345                        "expected a single mapping item, but found %d items" % len(subnode.value),
346                        subnode.start_mark)
347            key_node = subnode.value.keys()[0]
348            key = self.construct_object(key_node)
349            value = self.construct_object(subnode.value[key_node])
350            omap.append((key, value))
351        return omap
352
353    def construct_yaml_pairs(self, node):
354        # Note: the same code as `construct_yaml_omap`.
355        if not isinstance(node, SequenceNode):
356            raise ConstructorError("while constructing pairs", node.start_mark,
357                    "expected a sequence, but found %s" % node.id, node.start_mark)
358        pairs = []
359        self.constructed_objects[node] = pairs; # PKM2006
360        for subnode in node.value:
361            if not isinstance(subnode, MappingNode):
362                raise ConstructorError("while constructing pairs", node.start_mark,
363                        "expected a mapping of length 1, but found %s" % subnode.id,
364                        subnode.start_mark)
365            if len(subnode.value) != 1:
366                raise ConstructorError("while constructing pairs", node.start_mark,
367                        "expected a single mapping item, but found %d items" % len(subnode.value),
368                        subnode.start_mark)
369            key_node = subnode.value.keys()[0]
370            key = self.construct_object(key_node)
371            value = self.construct_object(subnode.value[key_node])
372            pairs.append((key, value))
373        return pairs
374
375    def construct_yaml_set(self, node):
376# PKM - We comment out the following code.       
377#        value = self.construct_mapping(node)
378#        return set(value)
379
380# The following implementation is quite naive - it simply constructs a set, and then
381# elements are added by the add function. It does not deal with issues like merge and
382# value keys.
383
384        if not isinstance(node, MappingNode):
385            raise ConstructorError(None, None,
386                    "expected a mapping node, but found %s" % node.id,
387                    node.start_mark)
388
389#PKM2006.
390                   
391        data = set();
392        self.constructed_objects[node] = data; # PKM2006
393        for child in node.value:
394            our_construct = self.construct_object(child);
395            data.add(our_construct);
396        return data;                   
397       
398
399    def construct_yaml_str(self, node):
400        value = self.construct_scalar(node)
401        try:
402            return str(value)
403        except UnicodeEncodeError:
404            return value
405
406    def construct_yaml_seq(self, node, setkey = True):
407        return self.construct_sequence(node, setkey) #MODIFY BBB3
408
409    def construct_yaml_map(self, node, setkey = True):
410        return self.construct_mapping(node, setkey)
411
412    def construct_yaml_object(self, node, cls):
413        #Must modify this
414        # Put this first:
415        data = cls.__new__(cls)
416        self.constructed_objects[node] = data # NEW - PKM
417        # Then:
418        state = self.construct_mapping(node, False)
419        if hasattr(data, '__setstate__'):
420            data.__setstate__(state)
421        else:
422            data.__dict__.update(state)
423        return data
424
425    def construct_undefined(self, node):
426        raise ConstructorError(None, None,
427                "could not determine a constructor for the tag %r" % node.tag.encode('utf-8'),
428                node.start_mark)
429
430SafeConstructor.add_constructor(
431        u'tag:yaml.org,2002:null',
432        SafeConstructor.construct_yaml_null)
433
434SafeConstructor.add_constructor(
435        u'tag:yaml.org,2002:bool',
436        SafeConstructor.construct_yaml_bool)
437
438SafeConstructor.add_constructor(
439        u'tag:yaml.org,2002:int',
440        SafeConstructor.construct_yaml_int)
441
442SafeConstructor.add_constructor(
443        u'tag:yaml.org,2002:float',
444        SafeConstructor.construct_yaml_float)
445
446SafeConstructor.add_constructor(
447        u'tag:yaml.org,2002:binary',
448        SafeConstructor.construct_yaml_binary)
449
450if datetime_available:
451    SafeConstructor.add_constructor(
452            u'tag:yaml.org,2002:timestamp',
453            SafeConstructor.construct_yaml_timestamp)
454
455SafeConstructor.add_constructor(
456        u'tag:yaml.org,2002:omap',
457        SafeConstructor.construct_yaml_omap)
458
459SafeConstructor.add_constructor(
460        u'tag:yaml.org,2002:pairs',
461        SafeConstructor.construct_yaml_pairs)
462
463SafeConstructor.add_constructor(
464        u'tag:yaml.org,2002:set',
465        SafeConstructor.construct_yaml_set)
466
467SafeConstructor.add_constructor(
468        u'tag:yaml.org,2002:str',
469        SafeConstructor.construct_yaml_str)
470
471SafeConstructor.add_constructor(
472        u'tag:yaml.org,2002:seq',
473        SafeConstructor.construct_yaml_seq)
474
475SafeConstructor.add_constructor(
476        u'tag:yaml.org,2002:map',
477        SafeConstructor.construct_yaml_map)
478
479SafeConstructor.add_constructor(None,
480        SafeConstructor.construct_undefined)
481
482class Constructor(SafeConstructor):
483
484    def construct_python_str(self, node):
485        return self.construct_scalar(node).encode('utf-8')
486
487    def construct_python_unicode(self, node):
488        return self.construct_scalar(node)
489
490    def construct_python_long(self, node):
491        return long(self.construct_yaml_int(node))
492
493    def construct_python_complex(self, node):
494       return complex(self.construct_scalar(node))
495
496# PKM - This is not recursive enough... not at all.
497#    def construct_python_tuple(self, node):
498#        return tuple(self.construct_yaml_seq(node))
499# So we roll our own.
500
501    def construct_python_tuple(self, node):
502
503# Tuples are strange beasts. What we need for each tuple T is to construct a
504# sequence S first, and then make a tuple T = tuple([S]). The requirements of
505# the function are:
506# 1 - S must be fully created with all elements before making a tuple out of
507# it.
508# 2 - Any object R that references T must reference T only after it has been
509# created.
510# 3 - We want the same strategy to construct the rest of the YAML tree - top
511# down construction with a little bit of recursion.
512# 4 - Make time minimal.
513# 5 - Make least use of memory.
514# 6 - Write the code that limits the effect on the rest of the system. (I am
515# thinking of third-parties that add their own constructors. Do they have to
516# make any special accommodation for tuples? I hope not.
517
518# The function will have a very weird recursive structure. Check it out.
519
520# As normal, we do this.       
521
522        if not isinstance(node, SequenceNode):
523            raise ConstructorError(None, None,
524                    "expected a sequence node, but found %s" % node.id,
525                    node.start_mark)
526
527# Now we might have a situation with Tuple -> List or Map ... -> Tuple. The
528# following code is probably unnecessary, but I'll put it in.
529
530        if node in self.constructed_objects:
531            return self.constructed_objects[node]
532
533# Let's get this out of the way.
534
535        if (len(node.value) == 0):
536            data = tuple();
537            self.constructed_objects[node] = data;
538            return data;
539
540# What is more likely is that in the Tuple -> List or Map ... -> Tuple, we
541# have construct_python_tuple(tuplenode) called twice for the same tuplenode,
542# and worse, the corresponding tuple will not have been fully constructed.
543# What we need is to check if the process has already been done.
544
545        ournodeid = id(node);
546
547        if ournodeid in self.yaml_tuple_replacement:
548            thisdata = (self.yaml_tuple_replacement[ournodeid])[0];
549            thislength = (self.yaml_tuple_replacement[ournodeid])[1];
550            thisindex = (self.yaml_tuple_replacement[ournodeid])[2];
551        else:
552            thisdata = [];
553            thislength = len(node.value);
554            thisindex = 0;
555            self.yaml_tuple_replacement[ournodeid] = [thisdata, thislength, thisindex];
556
557# Now we loop over each other
558
559        if (thislength == thisindex):
560            loopWhile = False;
561        else:
562            loopWhile = True;
563
564        while loopWhile:
565            child = node.value[thisindex];
566            self.yaml_tuple_replacement[ournodeid] = [thisdata, thislength, thisindex];
567            ourobject = self.construct_object(child);
568
569# The tuple might have been constructed somewhere else. In that case, we can
570# return.
571
572            if ournodeid not in self.yaml_tuple_replacement:
573                return self.constructed_objects[node];
574               
575# Now we append only if the object has not been added to the tuple.
576
577            ournewindex = (self.yaml_tuple_replacement[ournodeid])[2];
578            if (ournewindex == thisindex):
579                thisdata.append(ourobject);
580
581# We can end when we have run out of objects to construct.
582
583            thisindex = thisindex + 1;
584            if (thisindex == thislength):
585                loopWhile = False;
586           
587# Now that we have everything there, we can construct our tuple.
588
589        ourtuple = tuple(thisdata);
590        self.constructed_objects[node] = tuple;
591       
592# Then we can go around swapping references from the old sequence.
593
594# Now we can let the old sequence go.
595
596        del self.yaml_tuple_replacement[ournodeid];
597        del thisdata;
598        return ourtuple;                   
599
600    def find_python_module(self, name, mark):
601        if not name:
602            raise ConstructorError("while constructing a Python module", mark,
603                    "expected non-empty name appended to the tag", mark)
604        try:
605            __import__(name)
606        except ImportError, exc:
607            raise ConstructorError("while constructing a Python module", mark,
608                    "cannot find module %r (%s)" % (name.encode('utf-8'), exc), mark)
609        return sys.modules[name]
610
611    def find_python_name(self, name, mark):
612        if not name:
613            raise ConstructorError("while constructing a Python object", mark,
614                    "expected non-empty name appended to the tag", mark)
615        if u'.' in name:
616            # Python 2.4 only
617            #module_name, object_name = name.rsplit('.', 1)
618            items = name.split('.')
619            object_name = items.pop()
620            module_name = '.'.join(items)
621        else:
622            module_name = '__builtin__'
623            object_name = name
624        try:
625            __import__(module_name)
626        except ImportError, exc:
627            raise ConstructorError("while constructing a Python object", mark,
628                    "cannot find module %r (%s)" % (module_name.encode('utf-8'), exc), mark)
629        module = sys.modules[module_name]
630        if not hasattr(module, object_name):
631            raise ConstructorError("while constructing a Python object", mark,
632                    "cannot find %r in the module %r" % (object_name.encode('utf-8'),
633                        module.__name__), mark)
634        getatti = getattr(module,object_name); # PKM
635        return getatti;
636
637    def construct_python_name(self, suffix, node):
638        value = self.construct_scalar(node)
639        if value:
640            raise ConstructorError("while constructing a Python name", node.start_mark,
641                    "expected the empty value, but found %r" % value.encode('utf-8'),
642                    node.start_mark)
643        return self.find_python_name(suffix, node.start_mark)
644
645    def construct_python_module(self, suffix, node):
646        value = self.construct_scalar(node)
647        if value:
648            raise ConstructorError("while constructing a Python module", node.start_mark,
649                    "expected the empty value, but found %r" % value.encode('utf-8'),
650                    node.start_mark)
651        return self.find_python_module(suffix, node.start_mark)
652
653    class classobj: pass
654
655    def make_python_instance(self, suffix, node,
656            args=None, kwds=None, newobj=False):
657        if not args:
658            args = []
659        if not kwds:
660            kwds = {}
661        cls = self.find_python_name(suffix, node.start_mark)
662        if newobj and isinstance(cls, type(self.classobj))  \
663                and not args and not kwds:
664            instance = self.classobj()
665            instance.__class__ = cls
666            return instance
667        elif newobj and isinstance(cls, type):
668            return cls.__new__(cls, *args, **kwds)
669        else:
670            return cls(*args, **kwds)
671
672    def set_python_instance_state(self, instance, state):
673        if hasattr(instance, '__setstate__'):
674            instance.__setstate__(state)
675        else:
676            slotstate = {}
677            if isinstance(state, tuple) and len(state) == 2:
678                state, slotstate = state
679            if hasattr(instance, '__dict__'):
680                instance.__dict__.update(state)
681            elif state:
682                slotstate.update(state)
683            for key, value in slotstate.items():
684                setattr(object, key, value)
685
686    def construct_python_object(self, suffix, node):
687        # Format:
688        #   !!python/object:module.name { ... state ... }
689        # Must modify this.
690        instance = self.make_python_instance(suffix, node, newobj=True)
691        self.constructed_objects[node] = instance; #NEW - PKM2006.
692        state = self.construct_mapping(node, False) # PKM2006
693        self.set_python_instance_state(instance, state)
694        return instance
695
696    def construct_python_object_apply(self, suffix, node, newobj=False):
697        # Format:
698        #   !!python/object/apply       # (or !!python/object/new)
699        #   args: [ ... arguments ... ]
700        #   kwds: { ... keywords ... }
701        #   state: ... state ...
702        #   listitems: [ ... listitems ... ]
703        #   dictitems: { ... dictitems ... }
704        # or short format:
705        #   !!python/object/apply [ ... arguments ... ]
706        # The difference between !!python/object/apply and !!python/object/new
707        # is how an object is created, check make_python_instance for details.
708        # Must modify this
709        if isinstance(node, SequenceNode):
710            args = self.construct_sequence(node, False) # PKM2006.
711            kwds = {}
712            state = {}
713            listitems = []
714            dictitems = {}
715        else:
716            value = self.construct_mapping(node, False) # PKM2006.
717            args = value.get('args', [])
718            kwds = value.get('kwds', {})
719            state = value.get('state', {})
720            listitems = value.get('listitems', [])
721            dictitems = value.get('dictitems', {})
722        instance = self.make_python_instance(suffix, node, args, kwds, newobj)
723        self.constructed_objects[node] = instance; # PKM2006.
724        if state:
725            self.set_python_instance_state(instance, state)
726        if listitems:
727            instance.extend(listitems)
728        if dictitems:
729            for key in dictitems:
730                instance[key] = dictitems[key]
731        return instance
732
733    def construct_python_object_new(self, suffix, node):
734        return self.construct_python_object_apply(suffix, node, newobj=True)
735
736
737Constructor.add_constructor(
738    u'tag:yaml.org,2002:python/none',
739    Constructor.construct_yaml_null)
740
741Constructor.add_constructor(
742    u'tag:yaml.org,2002:python/bool',
743    Constructor.construct_yaml_bool)
744
745Constructor.add_constructor(
746    u'tag:yaml.org,2002:python/str',
747    Constructor.construct_python_str)
748
749Constructor.add_constructor(
750    u'tag:yaml.org,2002:python/unicode',
751    Constructor.construct_python_unicode)
752
753Constructor.add_constructor(
754    u'tag:yaml.org,2002:python/int',
755    Constructor.construct_yaml_int)
756
757Constructor.add_constructor(
758    u'tag:yaml.org,2002:python/long',
759    Constructor.construct_python_long)
760
761Constructor.add_constructor(
762    u'tag:yaml.org,2002:python/float',
763    Constructor.construct_yaml_float)
764
765Constructor.add_constructor(
766    u'tag:yaml.org,2002:python/complex',
767    Constructor.construct_python_complex)
768
769Constructor.add_constructor(
770    u'tag:yaml.org,2002:python/list',
771    Constructor.construct_yaml_seq)
772
773Constructor.add_constructor(
774    u'tag:yaml.org,2002:python/tuple',
775    Constructor.construct_python_tuple)
776
777Constructor.add_constructor(
778    u'tag:yaml.org,2002:python/dict',
779    Constructor.construct_yaml_map)
780
781Constructor.add_multi_constructor(
782    u'tag:yaml.org,2002:python/name:',
783    Constructor.construct_python_name)
784
785Constructor.add_multi_constructor(
786    u'tag:yaml.org,2002:python/module:',
787    Constructor.construct_python_module)
788
789Constructor.add_multi_constructor(
790    u'tag:yaml.org,2002:python/object:',
791    Constructor.construct_python_object)
792
793Constructor.add_multi_constructor(
794    u'tag:yaml.org,2002:python/object/apply:',
795    Constructor.construct_python_object_apply)
796
797Constructor.add_multi_constructor(
798    u'tag:yaml.org,2002:python/object/new:',
799    Constructor.construct_python_object_new)
800