2 __all__ = ['BaseConstructor', 'SafeConstructor', 'Constructor',
10 import binascii, re, sys, types
12 class ConstructorError(MarkedYAMLError):
15 class BaseConstructor(object):
17 yaml_constructors = {}
18 yaml_multi_constructors = {}
21 self.constructed_objects = {}
22 self.recursive_objects = {}
23 self.state_generators = []
24 self.deep_construct = False
27 # If there are more documents available?
28 return self.check_node()
31 # Construct and return the next document.
33 return self.construct_document(self.get_node())
35 def get_single_data(self):
36 # Ensure that the stream contains a single document and construct it.
37 node = self.get_single_node()
39 return self.construct_document(node)
42 def construct_document(self, node):
43 data = self.construct_object(node)
44 while self.state_generators:
45 state_generators = self.state_generators
46 self.state_generators = []
47 for generator in state_generators:
48 for dummy in generator:
50 self.constructed_objects = {}
51 self.recursive_objects = {}
52 self.deep_construct = False
55 def construct_object(self, node, deep=False):
56 if node in self.constructed_objects:
57 return self.constructed_objects[node]
59 old_deep = self.deep_construct
60 self.deep_construct = True
61 if node in self.recursive_objects:
62 raise ConstructorError(None, None,
63 "found unconstructable recursive node", node.start_mark)
64 self.recursive_objects[node] = None
67 if node.tag in self.yaml_constructors:
68 constructor = self.yaml_constructors[node.tag]
70 for tag_prefix in self.yaml_multi_constructors:
71 if node.tag.startswith(tag_prefix):
72 tag_suffix = node.tag[len(tag_prefix):]
73 constructor = self.yaml_multi_constructors[tag_prefix]
76 if None in self.yaml_multi_constructors:
78 constructor = self.yaml_multi_constructors[None]
79 elif None in self.yaml_constructors:
80 constructor = self.yaml_constructors[None]
81 elif isinstance(node, ScalarNode):
82 constructor = self.__class__.construct_scalar
83 elif isinstance(node, SequenceNode):
84 constructor = self.__class__.construct_sequence
85 elif isinstance(node, MappingNode):
86 constructor = self.__class__.construct_mapping
87 if tag_suffix is None:
88 data = constructor(self, node)
90 data = constructor(self, tag_suffix, node)
91 if isinstance(data, types.GeneratorType):
93 data = generator.next()
94 if self.deep_construct:
95 for dummy in generator:
98 self.state_generators.append(generator)
99 self.constructed_objects[node] = data
100 del self.recursive_objects[node]
102 self.deep_construct = old_deep
105 def construct_scalar(self, node):
106 if not isinstance(node, ScalarNode):
107 raise ConstructorError(None, None,
108 "expected a scalar node, but found %s" % node.id,
112 def construct_sequence(self, node, deep=False):
113 if not isinstance(node, SequenceNode):
114 raise ConstructorError(None, None,
115 "expected a sequence node, but found %s" % node.id,
117 return [self.construct_object(child, deep=deep)
118 for child in node.value]
120 def construct_mapping(self, node, deep=False):
121 if not isinstance(node, MappingNode):
122 raise ConstructorError(None, None,
123 "expected a mapping node, but found %s" % node.id,
126 for key_node, value_node in node.value:
127 key = self.construct_object(key_node, deep=deep)
130 except TypeError, exc:
131 raise ConstructorError("while constructing a mapping", node.start_mark,
132 "found unacceptable key (%s)" % exc, key_node.start_mark)
133 value = self.construct_object(value_node, deep=deep)
137 def construct_pairs(self, node, deep=False):
138 if not isinstance(node, MappingNode):
139 raise ConstructorError(None, None,
140 "expected a mapping node, but found %s" % node.id,
143 for key_node, value_node in node.value:
144 key = self.construct_object(key_node, deep=deep)
145 value = self.construct_object(value_node, deep=deep)
146 pairs.append((key, value))
149 def add_constructor(cls, tag, constructor):
150 if not 'yaml_constructors' in cls.__dict__:
151 cls.yaml_constructors = cls.yaml_constructors.copy()
152 cls.yaml_constructors[tag] = constructor
153 add_constructor = classmethod(add_constructor)
155 def add_multi_constructor(cls, tag_prefix, multi_constructor):
156 if not 'yaml_multi_constructors' in cls.__dict__:
157 cls.yaml_multi_constructors = cls.yaml_multi_constructors.copy()
158 cls.yaml_multi_constructors[tag_prefix] = multi_constructor
159 add_multi_constructor = classmethod(add_multi_constructor)
161 class SafeConstructor(BaseConstructor):
163 def construct_scalar(self, node):
164 if isinstance(node, MappingNode):
165 for key_node, value_node in node.value:
166 if key_node.tag == u'tag:yaml.org,2002:value':
167 return self.construct_scalar(value_node)
168 return BaseConstructor.construct_scalar(self, node)
170 def flatten_mapping(self, node):
173 while index < len(node.value):
174 key_node, value_node = node.value[index]
175 if key_node.tag == u'tag:yaml.org,2002:merge':
176 del node.value[index]
177 if isinstance(value_node, MappingNode):
178 self.flatten_mapping(value_node)
179 merge.extend(value_node.value)
180 elif isinstance(value_node, SequenceNode):
182 for subnode in value_node.value:
183 if not isinstance(subnode, MappingNode):
184 raise ConstructorError("while constructing a mapping",
186 "expected a mapping for merging, but found %s"
187 % subnode.id, subnode.start_mark)
188 self.flatten_mapping(subnode)
189 submerge.append(subnode.value)
191 for value in submerge:
194 raise ConstructorError("while constructing a mapping", node.start_mark,
195 "expected a mapping or list of mappings for merging, but found %s"
196 % value_node.id, value_node.start_mark)
197 elif key_node.tag == u'tag:yaml.org,2002:value':
198 key_node.tag = u'tag:yaml.org,2002:str'
203 node.value = merge + node.value
205 def construct_mapping(self, node, deep=False):
206 if isinstance(node, MappingNode):
207 self.flatten_mapping(node)
208 return BaseConstructor.construct_mapping(self, node, deep=deep)
210 def construct_yaml_null(self, node):
211 self.construct_scalar(node)
223 def construct_yaml_bool(self, node):
224 value = self.construct_scalar(node)
225 return self.bool_values[value.lower()]
227 def construct_yaml_int(self, node):
228 value = str(self.construct_scalar(node))
229 value = value.replace('_', '')
237 elif value.startswith('0b'):
238 return sign*int(value[2:], 2)
239 elif value.startswith('0x'):
240 return sign*int(value[2:], 16)
241 elif value[0] == '0':
242 return sign*int(value, 8)
244 digits = [int(part) for part in value.split(':')]
253 return sign*int(value)
256 while inf_value != inf_value*inf_value:
257 inf_value *= inf_value
258 nan_value = -inf_value/inf_value # Trying to make a quiet NaN (like C99).
260 def construct_yaml_float(self, node):
261 value = str(self.construct_scalar(node))
262 value = value.replace('_', '').lower()
269 return sign*self.inf_value
270 elif value == '.nan':
271 return self.nan_value
273 digits = [float(part) for part in value.split(':')]
282 return sign*float(value)
284 def construct_yaml_binary(self, node):
285 value = self.construct_scalar(node)
287 return str(value).decode('base64')
288 except (binascii.Error, UnicodeEncodeError), exc:
289 raise ConstructorError(None, None,
290 "failed to decode base64 data: %s" % exc, node.start_mark)
292 timestamp_regexp = re.compile(
293 ur'''^(?P<year>[0-9][0-9][0-9][0-9])
294 -(?P<month>[0-9][0-9]?)
295 -(?P<day>[0-9][0-9]?)
297 (?P<hour>[0-9][0-9]?)
298 :(?P<minute>[0-9][0-9])
299 :(?P<second>[0-9][0-9])
300 (?:\.(?P<fraction>[0-9]*))?
301 (?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
302 (?::(?P<tz_minute>[0-9][0-9]))?))?)?$''', re.X)
304 def construct_yaml_timestamp(self, node):
305 value = self.construct_scalar(node)
306 match = self.timestamp_regexp.match(node.value)
307 values = match.groupdict()
308 year = int(values['year'])
309 month = int(values['month'])
310 day = int(values['day'])
311 if not values['hour']:
312 return datetime.date(year, month, day)
313 hour = int(values['hour'])
314 minute = int(values['minute'])
315 second = int(values['second'])
317 if values['fraction']:
318 fraction = values['fraction'][:6]
319 while len(fraction) < 6:
321 fraction = int(fraction)
323 if values['tz_sign']:
324 tz_hour = int(values['tz_hour'])
325 tz_minute = int(values['tz_minute'] or 0)
326 delta = datetime.timedelta(hours=tz_hour, minutes=tz_minute)
327 if values['tz_sign'] == '-':
329 data = datetime.datetime(year, month, day, hour, minute, second, fraction)
334 def construct_yaml_omap(self, node):
335 # Note: we do not check for duplicate keys, because it's too
339 if not isinstance(node, SequenceNode):
340 raise ConstructorError("while constructing an ordered map", node.start_mark,
341 "expected a sequence, but found %s" % node.id, node.start_mark)
342 for subnode in node.value:
343 if not isinstance(subnode, MappingNode):
344 raise ConstructorError("while constructing an ordered map", node.start_mark,
345 "expected a mapping of length 1, but found %s" % subnode.id,
347 if len(subnode.value) != 1:
348 raise ConstructorError("while constructing an ordered map", node.start_mark,
349 "expected a single mapping item, but found %d items" % len(subnode.value),
351 key_node, value_node = subnode.value[0]
352 key = self.construct_object(key_node)
353 value = self.construct_object(value_node)
354 omap.append((key, value))
356 def construct_yaml_pairs(self, node):
357 # Note: the same code as `construct_yaml_omap`.
360 if not isinstance(node, SequenceNode):
361 raise ConstructorError("while constructing pairs", node.start_mark,
362 "expected a sequence, but found %s" % node.id, node.start_mark)
363 for subnode in node.value:
364 if not isinstance(subnode, MappingNode):
365 raise ConstructorError("while constructing pairs", node.start_mark,
366 "expected a mapping of length 1, but found %s" % subnode.id,
368 if len(subnode.value) != 1:
369 raise ConstructorError("while constructing pairs", node.start_mark,
370 "expected a single mapping item, but found %d items" % len(subnode.value),
372 key_node, value_node = subnode.value[0]
373 key = self.construct_object(key_node)
374 value = self.construct_object(value_node)
375 pairs.append((key, value))
377 def construct_yaml_set(self, node):
380 value = self.construct_mapping(node)
383 def construct_yaml_str(self, node):
384 value = self.construct_scalar(node)
386 return value.encode('ascii')
387 except UnicodeEncodeError:
390 def construct_yaml_seq(self, node):
393 data.extend(self.construct_sequence(node))
395 def construct_yaml_map(self, node):
398 value = self.construct_mapping(node)
401 def construct_yaml_object(self, node, cls):
402 data = cls.__new__(cls)
404 if hasattr(data, '__setstate__'):
405 state = self.construct_mapping(node, deep=True)
406 data.__setstate__(state)
408 state = self.construct_mapping(node)
409 data.__dict__.update(state)
411 def construct_undefined(self, node):
412 raise ConstructorError(None, None,
413 "could not determine a constructor for the tag %r" % node.tag.encode('utf-8'),
416 SafeConstructor.add_constructor(
417 u'tag:yaml.org,2002:null',
418 SafeConstructor.construct_yaml_null)
420 SafeConstructor.add_constructor(
421 u'tag:yaml.org,2002:bool',
422 SafeConstructor.construct_yaml_bool)
424 SafeConstructor.add_constructor(
425 u'tag:yaml.org,2002:int',
426 SafeConstructor.construct_yaml_int)
428 SafeConstructor.add_constructor(
429 u'tag:yaml.org,2002:float',
430 SafeConstructor.construct_yaml_float)
432 SafeConstructor.add_constructor(
433 u'tag:yaml.org,2002:binary',
434 SafeConstructor.construct_yaml_binary)
436 SafeConstructor.add_constructor(
437 u'tag:yaml.org,2002:timestamp',
438 SafeConstructor.construct_yaml_timestamp)
440 SafeConstructor.add_constructor(
441 u'tag:yaml.org,2002:omap',
442 SafeConstructor.construct_yaml_omap)
444 SafeConstructor.add_constructor(
445 u'tag:yaml.org,2002:pairs',
446 SafeConstructor.construct_yaml_pairs)
448 SafeConstructor.add_constructor(
449 u'tag:yaml.org,2002:set',
450 SafeConstructor.construct_yaml_set)
452 SafeConstructor.add_constructor(
453 u'tag:yaml.org,2002:str',
454 SafeConstructor.construct_yaml_str)
456 SafeConstructor.add_constructor(
457 u'tag:yaml.org,2002:seq',
458 SafeConstructor.construct_yaml_seq)
460 SafeConstructor.add_constructor(
461 u'tag:yaml.org,2002:map',
462 SafeConstructor.construct_yaml_map)
464 SafeConstructor.add_constructor(None,
465 SafeConstructor.construct_undefined)
467 class Constructor(SafeConstructor):
469 def construct_python_str(self, node):
470 return self.construct_scalar(node).encode('utf-8')
472 def construct_python_unicode(self, node):
473 return self.construct_scalar(node)
475 def construct_python_long(self, node):
476 return long(self.construct_yaml_int(node))
478 def construct_python_complex(self, node):
479 return complex(self.construct_scalar(node))
481 def construct_python_tuple(self, node):
482 return tuple(self.construct_sequence(node))
484 def find_python_module(self, name, mark):
486 raise ConstructorError("while constructing a Python module", mark,
487 "expected non-empty name appended to the tag", mark)
490 except ImportError, exc:
491 raise ConstructorError("while constructing a Python module", mark,
492 "cannot find module %r (%s)" % (name.encode('utf-8'), exc), mark)
493 return sys.modules[name]
495 def find_python_name(self, name, mark):
497 raise ConstructorError("while constructing a Python object", mark,
498 "expected non-empty name appended to the tag", mark)
500 module_name, object_name = name.rsplit('.', 1)
502 module_name = '__builtin__'
505 __import__(module_name)
506 except ImportError, exc:
507 raise ConstructorError("while constructing a Python object", mark,
508 "cannot find module %r (%s)" % (module_name.encode('utf-8'), exc), mark)
509 module = sys.modules[module_name]
510 if not hasattr(module, object_name):
511 raise ConstructorError("while constructing a Python object", mark,
512 "cannot find %r in the module %r" % (object_name.encode('utf-8'),
513 module.__name__), mark)
514 return getattr(module, object_name)
516 def construct_python_name(self, suffix, node):
517 value = self.construct_scalar(node)
519 raise ConstructorError("while constructing a Python name", node.start_mark,
520 "expected the empty value, but found %r" % value.encode('utf-8'),
522 return self.find_python_name(suffix, node.start_mark)
524 def construct_python_module(self, suffix, node):
525 value = self.construct_scalar(node)
527 raise ConstructorError("while constructing a Python module", node.start_mark,
528 "expected the empty value, but found %r" % value.encode('utf-8'),
530 return self.find_python_module(suffix, node.start_mark)
534 def make_python_instance(self, suffix, node,
535 args=None, kwds=None, newobj=False):
540 cls = self.find_python_name(suffix, node.start_mark)
541 if newobj and isinstance(cls, type(self.classobj)) \
542 and not args and not kwds:
543 instance = self.classobj()
544 instance.__class__ = cls
546 elif newobj and isinstance(cls, type):
547 return cls.__new__(cls, *args, **kwds)
549 return cls(*args, **kwds)
551 def set_python_instance_state(self, instance, state):
552 if hasattr(instance, '__setstate__'):
553 instance.__setstate__(state)
556 if isinstance(state, tuple) and len(state) == 2:
557 state, slotstate = state
558 if hasattr(instance, '__dict__'):
559 instance.__dict__.update(state)
561 slotstate.update(state)
562 for key, value in slotstate.items():
563 setattr(object, key, value)
565 def construct_python_object(self, suffix, node):
567 # !!python/object:module.name { ... state ... }
568 instance = self.make_python_instance(suffix, node, newobj=True)
570 deep = hasattr(instance, '__setstate__')
571 state = self.construct_mapping(node, deep=deep)
572 self.set_python_instance_state(instance, state)
574 def construct_python_object_apply(self, suffix, node, newobj=False):
576 # !!python/object/apply # (or !!python/object/new)
577 # args: [ ... arguments ... ]
578 # kwds: { ... keywords ... }
579 # state: ... state ...
580 # listitems: [ ... listitems ... ]
581 # dictitems: { ... dictitems ... }
583 # !!python/object/apply [ ... arguments ... ]
584 # The difference between !!python/object/apply and !!python/object/new
585 # is how an object is created, check make_python_instance for details.
586 if isinstance(node, SequenceNode):
587 args = self.construct_sequence(node, deep=True)
593 value = self.construct_mapping(node, deep=True)
594 args = value.get('args', [])
595 kwds = value.get('kwds', {})
596 state = value.get('state', {})
597 listitems = value.get('listitems', [])
598 dictitems = value.get('dictitems', {})
599 instance = self.make_python_instance(suffix, node, args, kwds, newobj)
601 self.set_python_instance_state(instance, state)
603 instance.extend(listitems)
605 for key in dictitems:
606 instance[key] = dictitems[key]
609 def construct_python_object_new(self, suffix, node):
610 return self.construct_python_object_apply(suffix, node, newobj=True)
612 Constructor.add_constructor(
613 u'tag:yaml.org,2002:python/none',
614 Constructor.construct_yaml_null)
616 Constructor.add_constructor(
617 u'tag:yaml.org,2002:python/bool',
618 Constructor.construct_yaml_bool)
620 Constructor.add_constructor(
621 u'tag:yaml.org,2002:python/str',
622 Constructor.construct_python_str)
624 Constructor.add_constructor(
625 u'tag:yaml.org,2002:python/unicode',
626 Constructor.construct_python_unicode)
628 Constructor.add_constructor(
629 u'tag:yaml.org,2002:python/int',
630 Constructor.construct_yaml_int)
632 Constructor.add_constructor(
633 u'tag:yaml.org,2002:python/long',
634 Constructor.construct_python_long)
636 Constructor.add_constructor(
637 u'tag:yaml.org,2002:python/float',
638 Constructor.construct_yaml_float)
640 Constructor.add_constructor(
641 u'tag:yaml.org,2002:python/complex',
642 Constructor.construct_python_complex)
644 Constructor.add_constructor(
645 u'tag:yaml.org,2002:python/list',
646 Constructor.construct_yaml_seq)
648 Constructor.add_constructor(
649 u'tag:yaml.org,2002:python/tuple',
650 Constructor.construct_python_tuple)
652 Constructor.add_constructor(
653 u'tag:yaml.org,2002:python/dict',
654 Constructor.construct_yaml_map)
656 Constructor.add_multi_constructor(
657 u'tag:yaml.org,2002:python/name:',
658 Constructor.construct_python_name)
660 Constructor.add_multi_constructor(
661 u'tag:yaml.org,2002:python/module:',
662 Constructor.construct_python_module)
664 Constructor.add_multi_constructor(
665 u'tag:yaml.org,2002:python/object:',
666 Constructor.construct_python_object)
668 Constructor.add_multi_constructor(
669 u'tag:yaml.org,2002:python/object/apply:',
670 Constructor.construct_python_object_apply)
672 Constructor.add_multi_constructor(
673 u'tag:yaml.org,2002:python/object/new:',
674 Constructor.construct_python_object_new)