Ticket #5: constructor.2.py

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

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

Line 
1
2 __all__ = ['BaseConstructor', 'SafeConstructor', 'Constructor',
3     'ConstructorError']
4
5 from error import *
6 from nodes import *
7 from composer import *
8
9 try:
10     import datetime
11     datetime_available = True
12 except ImportError:
13     datetime_available = False
14
15 try:
16     set
17 except NameError:
18     from sets import Set as set
19
20 import binascii, re, sys
21
22 class ConstructorError(MarkedYAMLError):
23     pass
24
25 class 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
197 class 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
386 SafeConstructor.add_constructor(
387         u'tag:yaml.org,2002:null',
388         SafeConstructor.construct_yaml_null)
389
390 SafeConstructor.add_constructor(
391         u'tag:yaml.org,2002:bool',
392         SafeConstructor.construct_yaml_bool)
393
394 SafeConstructor.add_constructor(
395         u'tag:yaml.org,2002:int',
396         SafeConstructor.construct_yaml_int)
397
398 SafeConstructor.add_constructor(
399         u'tag:yaml.org,2002:float',
400         SafeConstructor.construct_yaml_float)
401
402 SafeConstructor.add_constructor(
403         u'tag:yaml.org,2002:binary',
404         SafeConstructor.construct_yaml_binary)
405
406 if datetime_available:
407     SafeConstructor.add_constructor(
408             u'tag:yaml.org,2002:timestamp',
409             SafeConstructor.construct_yaml_timestamp)
410
411 SafeConstructor.add_constructor(
412         u'tag:yaml.org,2002:omap',
413         SafeConstructor.construct_yaml_omap)
414
415 SafeConstructor.add_constructor(
416         u'tag:yaml.org,2002:pairs',
417         SafeConstructor.construct_yaml_pairs)
418
419 SafeConstructor.add_constructor(
420         u'tag:yaml.org,2002:set',
421         SafeConstructor.construct_yaml_set)
422
423 SafeConstructor.add_constructor(
424         u'tag:yaml.org,2002:str',
425         SafeConstructor.construct_yaml_str)
426
427 SafeConstructor.add_constructor(
428         u'tag:yaml.org,2002:seq',
429         SafeConstructor.construct_yaml_seq)
430
431 SafeConstructor.add_constructor(
432         u'tag:yaml.org,2002:map',
433         SafeConstructor.construct_yaml_map)
434
435 SafeConstructor.add_constructor(None,
436         SafeConstructor.construct_undefined)
437
438 class 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
587 Constructor.add_constructor(
588     u'tag:yaml.org,2002:python/none',
589     Constructor.construct_yaml_null)
590
591 Constructor.add_constructor(
592     u'tag:yaml.org,2002:python/bool',
593     Constructor.construct_yaml_bool)
594
595 Constructor.add_constructor(
596     u'tag:yaml.org,2002:python/str',
597     Constructor.construct_python_str)
598
599 Constructor.add_constructor(
600     u'tag:yaml.org,2002:python/unicode',
601     Constructor.construct_python_unicode)
602
603 Constructor.add_constructor(
604     u'tag:yaml.org,2002:python/int',
605     Constructor.construct_yaml_int)
606
607 Constructor.add_constructor(
608     u'tag:yaml.org,2002:python/long',
609     Constructor.construct_python_long)
610
611 Constructor.add_constructor(
612     u'tag:yaml.org,2002:python/float',
613     Constructor.construct_yaml_float)
614
615 Constructor.add_constructor(
616     u'tag:yaml.org,2002:python/complex',
617     Constructor.construct_python_complex)
618
619 Constructor.add_constructor(
620     u'tag:yaml.org,2002:python/list',
621     Constructor.construct_yaml_seq)
622
623 Constructor.add_constructor(
624     u'tag:yaml.org,2002:python/tuple',
625     Constructor.construct_python_tuple)
626
627 Constructor.add_constructor(
628     u'tag:yaml.org,2002:python/dict',
629     Constructor.construct_yaml_map)
630
631 Constructor.add_multi_constructor(
632     u'tag:yaml.org,2002:python/name:',
633     Constructor.construct_python_name)
634
635 Constructor.add_multi_constructor(
636     u'tag:yaml.org,2002:python/module:',
637     Constructor.construct_python_module)
638
639 Constructor.add_multi_constructor(
640     u'tag:yaml.org,2002:python/object:',
641     Constructor.construct_python_object)
642
643 Constructor.add_multi_constructor(
644     u'tag:yaml.org,2002:python/object/apply:',
645     Constructor.construct_python_object_apply)
646
647 Constructor.add_multi_constructor(
648     u'tag:yaml.org,2002:python/object/new:',
649     Constructor.construct_python_object_new)
650