Subclass all base classes from `object`.
[pyyaml/python3.git] / lib / yaml / constructor.py
blobcbbcd78f325c1c7bc684ebcd0aa85e31f8b6d196
2 __all__ = ['BaseConstructor', 'SafeConstructor', 'Constructor',
3 'ConstructorError']
5 from error import *
6 from nodes import *
8 try:
9 import datetime
10 datetime_available = True
11 except ImportError:
12 datetime_available = False
14 try:
15 set
16 except NameError:
17 from sets import Set as set
19 import binascii, re, sys
21 class ConstructorError(MarkedYAMLError):
22 pass
24 class BaseConstructor(object):
26 yaml_constructors = {}
27 yaml_multi_constructors = {}
29 def __init__(self):
30 self.constructed_objects = {}
31 self.recursive_objects = {}
32 self.state_generators = []
33 self.deep_construct = False
35 def check_data(self):
36 # If there are more documents available?
37 return self.check_node()
39 def get_data(self):
40 # Construct and return the next document.
41 if self.check_node():
42 return self.construct_document(self.get_node())
44 def g(): yield None
45 generator_type = type(g())
46 del g
48 def construct_document(self, node):
49 data = self.construct_object(node)
50 while self.state_generators:
51 state_generators = self.state_generators
52 self.state_generators = []
53 for generator in state_generators:
54 for dummy in generator:
55 pass
56 self.constructed_objects = {}
57 self.recursive_objects = {}
58 self.deep_construct = False
59 return data
61 def construct_object(self, node, deep=False):
62 if deep:
63 old_deep = self.deep_construct
64 self.deep_construct = True
65 if node in self.constructed_objects:
66 return self.constructed_objects[node]
67 if node in self.recursive_objects:
68 raise ConstructorError(None, None,
69 "found unconstructable recursive node", node.start_mark)
70 self.recursive_objects[node] = None
71 constructor = None
72 state_constructor = None
73 tag_suffix = None
74 if node.tag in self.yaml_constructors:
75 constructor = self.yaml_constructors[node.tag]
76 else:
77 for tag_prefix in self.yaml_multi_constructors:
78 if node.tag.startswith(tag_prefix):
79 tag_suffix = node.tag[len(tag_prefix):]
80 constructor = self.yaml_multi_constructors[tag_prefix]
81 break
82 else:
83 if None in self.yaml_multi_constructors:
84 tag_suffix = node.tag
85 constructor = self.yaml_multi_constructors[None]
86 elif None in self.yaml_constructors:
87 constructor = self.yaml_constructors[None]
88 elif isinstance(node, ScalarNode):
89 constructor = self.__class__.construct_scalar
90 elif isinstance(node, SequenceNode):
91 constructor = self.__class__.construct_sequence
92 elif isinstance(node, MappingNode):
93 constructor = self.__class__.construct_mapping
94 if tag_suffix is None:
95 data = constructor(self, node)
96 else:
97 data = constructor(self, tag_suffix, node)
98 if isinstance(data, self.generator_type):
99 generator = data
100 data = generator.next()
101 if self.deep_construct:
102 for dummy in generator:
103 pass
104 else:
105 self.state_generators.append(generator)
106 self.constructed_objects[node] = data
107 del self.recursive_objects[node]
108 if deep:
109 self.deep_construct = old_deep
110 return data
112 def construct_scalar(self, node):
113 if not isinstance(node, ScalarNode):
114 raise ConstructorError(None, None,
115 "expected a scalar node, but found %s" % node.id,
116 node.start_mark)
117 return node.value
119 def construct_sequence(self, node, deep=False):
120 if not isinstance(node, SequenceNode):
121 raise ConstructorError(None, None,
122 "expected a sequence node, but found %s" % node.id,
123 node.start_mark)
124 return [self.construct_object(child, deep=deep)
125 for child in node.value]
127 def construct_mapping(self, node, deep=False):
128 if not isinstance(node, MappingNode):
129 raise ConstructorError(None, None,
130 "expected a mapping node, but found %s" % node.id,
131 node.start_mark)
132 mapping = {}
133 for key_node, value_node in node.value:
134 key = self.construct_object(key_node, deep=deep)
135 try:
136 hash(key)
137 except TypeError, exc:
138 raise ConstructorError("while constructing a mapping", node.start_mark,
139 "found unacceptable key (%s)" % exc, key_node.start_mark)
140 value = self.construct_object(value_node, deep=deep)
141 mapping[key] = value
142 return mapping
144 def construct_pairs(self, node, deep=False):
145 if not isinstance(node, MappingNode):
146 raise ConstructorError(None, None,
147 "expected a mapping node, but found %s" % node.id,
148 node.start_mark)
149 pairs = []
150 for key_node, value_node in node.value:
151 key = self.construct_object(key_node, deep=deep)
152 value = self.construct_object(value_node, deep=deep)
153 pairs.append((key, value))
154 return pairs
156 def add_constructor(cls, tag, constructor):
157 if not 'yaml_constructors' in cls.__dict__:
158 cls.yaml_constructors = cls.yaml_constructors.copy()
159 cls.yaml_constructors[tag] = constructor
160 add_constructor = classmethod(add_constructor)
162 def add_multi_constructor(cls, tag_prefix, multi_constructor):
163 if not 'yaml_multi_constructors' in cls.__dict__:
164 cls.yaml_multi_constructors = cls.yaml_multi_constructors.copy()
165 cls.yaml_multi_constructors[tag_prefix] = multi_constructor
166 add_multi_constructor = classmethod(add_multi_constructor)
168 class SafeConstructor(BaseConstructor):
170 def construct_scalar(self, node):
171 if isinstance(node, MappingNode):
172 for key_node, value_node in node.value:
173 if key_node.tag == u'tag:yaml.org,2002:value':
174 return self.construct_scalar(value_node)
175 return BaseConstructor.construct_scalar(self, node)
177 def flatten_mapping(self, node):
178 merge = []
179 index = 0
180 while index < len(node.value):
181 key_node, value_node = node.value[index]
182 if key_node.tag == u'tag:yaml.org,2002:merge':
183 del node.value[index]
184 if isinstance(value_node, MappingNode):
185 self.flatten_mapping(value_node)
186 merge.extend(value_node.value)
187 elif isinstance(value_node, SequenceNode):
188 submerge = []
189 for subnode in value_node.value:
190 if not isinstance(subnode, MappingNode):
191 raise ConstructorError("while constructing a mapping",
192 node.start_mark,
193 "expected a mapping for merging, but found %s"
194 % subnode.id, subnode.start_mark)
195 self.flatten_mapping(subnode)
196 submerge.append(subnode.value)
197 submerge.reverse()
198 for value in submerge:
199 merge.extend(value)
200 else:
201 raise ConstructorError("while constructing a mapping", node.start_mark,
202 "expected a mapping or list of mappings for merging, but found %s"
203 % value_node.id, value_node.start_mark)
204 elif key_node.tag == u'tag:yaml.org,2002:value':
205 key_node.tag = u'tag:yaml.org,2002:str'
206 index += 1
207 else:
208 index += 1
209 if merge:
210 node.value = merge + node.value
212 def construct_mapping(self, node, deep=False):
213 if isinstance(node, MappingNode):
214 self.flatten_mapping(node)
215 return BaseConstructor.construct_mapping(self, node, deep=deep)
217 def construct_yaml_null(self, node):
218 self.construct_scalar(node)
219 return None
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,
230 def construct_yaml_bool(self, node):
231 value = self.construct_scalar(node)
232 return self.bool_values[value.lower()]
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)
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).
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)
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)
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)
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
330 def construct_yaml_omap(self, node):
331 # Note: we do not check for duplicate keys, because it's too
332 # CPU-expensive.
333 omap = []
334 yield omap
335 if not isinstance(node, SequenceNode):
336 raise ConstructorError("while constructing an ordered map", node.start_mark,
337 "expected a sequence, but found %s" % node.id, node.start_mark)
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, value_node = subnode.value[0]
348 key = self.construct_object(key_node)
349 value = self.construct_object(value_node)
350 omap.append((key, value))
352 def construct_yaml_pairs(self, node):
353 # Note: the same code as `construct_yaml_omap`.
354 pairs = []
355 yield pairs
356 if not isinstance(node, SequenceNode):
357 raise ConstructorError("while constructing pairs", node.start_mark,
358 "expected a sequence, but found %s" % node.id, node.start_mark)
359 for subnode in node.value:
360 if not isinstance(subnode, MappingNode):
361 raise ConstructorError("while constructing pairs", node.start_mark,
362 "expected a mapping of length 1, but found %s" % subnode.id,
363 subnode.start_mark)
364 if len(subnode.value) != 1:
365 raise ConstructorError("while constructing pairs", node.start_mark,
366 "expected a single mapping item, but found %d items" % len(subnode.value),
367 subnode.start_mark)
368 key_node, value_node = subnode.value[0]
369 key = self.construct_object(key_node)
370 value = self.construct_object(value_node)
371 pairs.append((key, value))
373 def construct_yaml_set(self, node):
374 data = set()
375 yield data
376 value = self.construct_mapping(node)
377 data.update(value)
379 def construct_yaml_str(self, node):
380 value = self.construct_scalar(node)
381 try:
382 return str(value)
383 except UnicodeEncodeError:
384 return value
386 def construct_yaml_seq(self, node):
387 data = []
388 yield data
389 data.extend(self.construct_sequence(node))
391 def construct_yaml_map(self, node):
392 data = {}
393 yield data
394 value = self.construct_mapping(node)
395 data.update(value)
397 def construct_yaml_object(self, node, cls):
398 data = cls.__new__(cls)
399 yield data
400 if hasattr(data, '__setstate__'):
401 state = self.construct_mapping(node, deep=True)
402 data.__setstate__(state)
403 else:
404 state = self.construct_mapping(node)
405 data.__dict__.update(state)
407 def construct_undefined(self, node):
408 raise ConstructorError(None, None,
409 "could not determine a constructor for the tag %r" % node.tag.encode('utf-8'),
410 node.start_mark)
412 SafeConstructor.add_constructor(
413 u'tag:yaml.org,2002:null',
414 SafeConstructor.construct_yaml_null)
416 SafeConstructor.add_constructor(
417 u'tag:yaml.org,2002:bool',
418 SafeConstructor.construct_yaml_bool)
420 SafeConstructor.add_constructor(
421 u'tag:yaml.org,2002:int',
422 SafeConstructor.construct_yaml_int)
424 SafeConstructor.add_constructor(
425 u'tag:yaml.org,2002:float',
426 SafeConstructor.construct_yaml_float)
428 SafeConstructor.add_constructor(
429 u'tag:yaml.org,2002:binary',
430 SafeConstructor.construct_yaml_binary)
432 if datetime_available:
433 SafeConstructor.add_constructor(
434 u'tag:yaml.org,2002:timestamp',
435 SafeConstructor.construct_yaml_timestamp)
437 SafeConstructor.add_constructor(
438 u'tag:yaml.org,2002:omap',
439 SafeConstructor.construct_yaml_omap)
441 SafeConstructor.add_constructor(
442 u'tag:yaml.org,2002:pairs',
443 SafeConstructor.construct_yaml_pairs)
445 SafeConstructor.add_constructor(
446 u'tag:yaml.org,2002:set',
447 SafeConstructor.construct_yaml_set)
449 SafeConstructor.add_constructor(
450 u'tag:yaml.org,2002:str',
451 SafeConstructor.construct_yaml_str)
453 SafeConstructor.add_constructor(
454 u'tag:yaml.org,2002:seq',
455 SafeConstructor.construct_yaml_seq)
457 SafeConstructor.add_constructor(
458 u'tag:yaml.org,2002:map',
459 SafeConstructor.construct_yaml_map)
461 SafeConstructor.add_constructor(None,
462 SafeConstructor.construct_undefined)
464 class Constructor(SafeConstructor):
466 def construct_python_str(self, node):
467 return self.construct_scalar(node).encode('utf-8')
469 def construct_python_unicode(self, node):
470 return self.construct_scalar(node)
472 def construct_python_long(self, node):
473 return long(self.construct_yaml_int(node))
475 def construct_python_complex(self, node):
476 return complex(self.construct_scalar(node))
478 def construct_python_tuple(self, node):
479 return tuple(self.construct_sequence(node))
481 def find_python_module(self, name, mark):
482 if not name:
483 raise ConstructorError("while constructing a Python module", mark,
484 "expected non-empty name appended to the tag", mark)
485 try:
486 __import__(name)
487 except ImportError, exc:
488 raise ConstructorError("while constructing a Python module", mark,
489 "cannot find module %r (%s)" % (name.encode('utf-8'), exc), mark)
490 return sys.modules[name]
492 def find_python_name(self, name, mark):
493 if not name:
494 raise ConstructorError("while constructing a Python object", mark,
495 "expected non-empty name appended to the tag", mark)
496 if u'.' in name:
497 # Python 2.4 only
498 #module_name, object_name = name.rsplit('.', 1)
499 items = name.split('.')
500 object_name = items.pop()
501 module_name = '.'.join(items)
502 else:
503 module_name = '__builtin__'
504 object_name = name
505 try:
506 __import__(module_name)
507 except ImportError, exc:
508 raise ConstructorError("while constructing a Python object", mark,
509 "cannot find module %r (%s)" % (module_name.encode('utf-8'), exc), mark)
510 module = sys.modules[module_name]
511 if not hasattr(module, object_name):
512 raise ConstructorError("while constructing a Python object", mark,
513 "cannot find %r in the module %r" % (object_name.encode('utf-8'),
514 module.__name__), mark)
515 return getattr(module, object_name)
517 def construct_python_name(self, suffix, node):
518 value = self.construct_scalar(node)
519 if value:
520 raise ConstructorError("while constructing a Python name", node.start_mark,
521 "expected the empty value, but found %r" % value.encode('utf-8'),
522 node.start_mark)
523 return self.find_python_name(suffix, node.start_mark)
525 def construct_python_module(self, suffix, node):
526 value = self.construct_scalar(node)
527 if value:
528 raise ConstructorError("while constructing a Python module", node.start_mark,
529 "expected the empty value, but found %r" % value.encode('utf-8'),
530 node.start_mark)
531 return self.find_python_module(suffix, node.start_mark)
533 class classobj: pass
535 def make_python_instance(self, suffix, node,
536 args=None, kwds=None, newobj=False):
537 if not args:
538 args = []
539 if not kwds:
540 kwds = {}
541 cls = self.find_python_name(suffix, node.start_mark)
542 if newobj and isinstance(cls, type(self.classobj)) \
543 and not args and not kwds:
544 instance = self.classobj()
545 instance.__class__ = cls
546 return instance
547 elif newobj and isinstance(cls, type):
548 return cls.__new__(cls, *args, **kwds)
549 else:
550 return cls(*args, **kwds)
552 def set_python_instance_state(self, instance, state):
553 if hasattr(instance, '__setstate__'):
554 instance.__setstate__(state)
555 else:
556 slotstate = {}
557 if isinstance(state, tuple) and len(state) == 2:
558 state, slotstate = state
559 if hasattr(instance, '__dict__'):
560 instance.__dict__.update(state)
561 elif state:
562 slotstate.update(state)
563 for key, value in slotstate.items():
564 setattr(object, key, value)
566 def construct_python_object(self, suffix, node):
567 # Format:
568 # !!python/object:module.name { ... state ... }
569 instance = self.make_python_instance(suffix, node, newobj=True)
570 yield instance
571 deep = hasattr(instance, '__setstate__')
572 state = self.construct_mapping(node, deep=deep)
573 self.set_python_instance_state(instance, state)
575 def construct_python_object_apply(self, suffix, node, newobj=False):
576 # Format:
577 # !!python/object/apply # (or !!python/object/new)
578 # args: [ ... arguments ... ]
579 # kwds: { ... keywords ... }
580 # state: ... state ...
581 # listitems: [ ... listitems ... ]
582 # dictitems: { ... dictitems ... }
583 # or short format:
584 # !!python/object/apply [ ... arguments ... ]
585 # The difference between !!python/object/apply and !!python/object/new
586 # is how an object is created, check make_python_instance for details.
587 if isinstance(node, SequenceNode):
588 args = self.construct_sequence(node, deep=True)
589 kwds = {}
590 state = {}
591 listitems = []
592 dictitems = {}
593 else:
594 value = self.construct_mapping(node, deep=True)
595 args = value.get('args', [])
596 kwds = value.get('kwds', {})
597 state = value.get('state', {})
598 listitems = value.get('listitems', [])
599 dictitems = value.get('dictitems', {})
600 instance = self.make_python_instance(suffix, node, args, kwds, newobj)
601 if state:
602 self.set_python_instance_state(instance, state)
603 if listitems:
604 instance.extend(listitems)
605 if dictitems:
606 for key in dictitems:
607 instance[key] = dictitems[key]
608 return instance
610 def construct_python_object_new(self, suffix, node):
611 return self.construct_python_object_apply(suffix, node, newobj=True)
613 Constructor.add_constructor(
614 u'tag:yaml.org,2002:python/none',
615 Constructor.construct_yaml_null)
617 Constructor.add_constructor(
618 u'tag:yaml.org,2002:python/bool',
619 Constructor.construct_yaml_bool)
621 Constructor.add_constructor(
622 u'tag:yaml.org,2002:python/str',
623 Constructor.construct_python_str)
625 Constructor.add_constructor(
626 u'tag:yaml.org,2002:python/unicode',
627 Constructor.construct_python_unicode)
629 Constructor.add_constructor(
630 u'tag:yaml.org,2002:python/int',
631 Constructor.construct_yaml_int)
633 Constructor.add_constructor(
634 u'tag:yaml.org,2002:python/long',
635 Constructor.construct_python_long)
637 Constructor.add_constructor(
638 u'tag:yaml.org,2002:python/float',
639 Constructor.construct_yaml_float)
641 Constructor.add_constructor(
642 u'tag:yaml.org,2002:python/complex',
643 Constructor.construct_python_complex)
645 Constructor.add_constructor(
646 u'tag:yaml.org,2002:python/list',
647 Constructor.construct_yaml_seq)
649 Constructor.add_constructor(
650 u'tag:yaml.org,2002:python/tuple',
651 Constructor.construct_python_tuple)
653 Constructor.add_constructor(
654 u'tag:yaml.org,2002:python/dict',
655 Constructor.construct_yaml_map)
657 Constructor.add_multi_constructor(
658 u'tag:yaml.org,2002:python/name:',
659 Constructor.construct_python_name)
661 Constructor.add_multi_constructor(
662 u'tag:yaml.org,2002:python/module:',
663 Constructor.construct_python_module)
665 Constructor.add_multi_constructor(
666 u'tag:yaml.org,2002:python/object:',
667 Constructor.construct_python_object)
669 Constructor.add_multi_constructor(
670 u'tag:yaml.org,2002:python/object/apply:',
671 Constructor.construct_python_object_apply)
673 Constructor.add_multi_constructor(
674 u'tag:yaml.org,2002:python/object/new:',
675 Constructor.construct_python_object_new)