Add constructors for some simple python types.
[pyyaml.git] / lib / yaml / constructor.py
blobff205c20b3ed7e32ce0eed4ab0b43cc3165c47fe
2 __all__ = ['BaseConstructor', 'SafeConstructor', 'Constructor',
3 'ConstructorError']
5 from error import *
6 from nodes import *
7 from composer import *
9 try:
10 import datetime
11 datetime_available = True
12 except ImportError:
13 datetime_available = False
15 try:
16 set
17 except NameError:
18 from sets import Set as set
20 import binascii, re, sys
22 class ConstructorError(MarkedYAMLError):
23 pass
25 class BaseConstructor(Composer):
27 yaml_constructors = {}
28 yaml_multi_constructors = {}
30 def __init__(self):
31 self.constructed_objects = {}
33 def check_data(self):
34 # If there are more documents available?
35 return self.check_node()
37 def get_data(self):
38 # Construct and return the next document.
39 if self.check_node():
40 return self.construct_document(self.get_node())
42 def __iter__(self):
43 # Iterator protocol.
44 while self.check_node():
45 yield self.construct_document(self.get_node())
47 def construct_document(self, node):
48 data = self.construct_object(node)
49 self.constructed_objects = {}
50 return data
52 def construct_object(self, node):
53 if node in self.constructed_objects:
54 return self.constructed_objects[node]
55 constructor = None
56 if node.tag in self.yaml_constructors:
57 constructor = lambda node: self.yaml_constructors[node.tag](self, node)
58 else:
59 for tag_prefix in self.yaml_multi_constructors:
60 if node.tag.startswith(tag_prefix):
61 tag_suffix = node.tag[len(tag_prefix):]
62 constructor = lambda node: \
63 self.yaml_multi_constructors[tag_prefix](self, tag_suffix, node)
64 break
65 else:
66 if None in self.yaml_multi_constructors:
67 constructor = lambda node: \
68 self.yaml_multi_constructors[None](self, node.tag, node)
69 elif None in self.yaml_constructors:
70 constructor = lambda node: \
71 self.yaml_constructors[None](self, node)
72 elif isinstance(node, ScalarNode):
73 constructor = self.construct_scalar
74 elif isinstance(node, SequenceNode):
75 constructor = self.construct_sequence
76 elif isinstance(node, MappingNode):
77 constructor = self.construct_mapping
78 else:
79 print node.tag
80 data = constructor(node)
81 self.constructed_objects[node] = data
82 return data
84 def construct_scalar(self, node):
85 if not isinstance(node, ScalarNode):
86 if isinstance(node, MappingNode):
87 for key_node in node.value:
88 if key_node.tag == u'tag:yaml.org,2002:value':
89 return self.construct_scalar(node.value[key_node])
90 raise ConstructorError(None, None,
91 "expected a scalar node, but found %s" % node.id,
92 node.start_mark)
93 return node.value
95 def construct_sequence(self, node):
96 if not isinstance(node, SequenceNode):
97 raise ConstructorError(None, None,
98 "expected a sequence node, but found %s" % node.id,
99 node.start_mark)
100 return [self.construct_object(child) for child in node.value]
102 def construct_mapping(self, node):
103 if not isinstance(node, MappingNode):
104 raise ConstructorError(None, None,
105 "expected a mapping node, but found %s" % node.id,
106 node.start_mark)
107 mapping = {}
108 merge = None
109 for key_node in node.value:
110 if key_node.tag == u'tag:yaml.org,2002:merge':
111 if merge is not None:
112 raise ConstructorError("while constructing a mapping", node.start_mark,
113 "found duplicate merge key", key_node.start_mark)
114 value_node = node.value[key_node]
115 if isinstance(value_node, MappingNode):
116 merge = [self.construct_mapping(value_node)]
117 elif isinstance(value_node, SequenceNode):
118 merge = []
119 for subnode in value_node.value:
120 if not isinstance(subnode, MappingNode):
121 raise ConstructorError("while constructing a mapping",
122 node.start_mark,
123 "expected a mapping for merging, but found %s"
124 % subnode.id, subnode.start_mark)
125 merge.append(self.construct_mapping(subnode))
126 merge.reverse()
127 else:
128 raise ConstructorError("while constructing a mapping", node.start_mark,
129 "expected a mapping or list of mappings for merging, but found %s"
130 % value_node.id, value_node.start_mark)
131 elif key_node.tag == u'tag:yaml.org,2002:value':
132 if '=' in mapping:
133 raise ConstructorError("while construction a mapping", node.start_mark,
134 "found duplicate value key", key_node.start_mark)
135 value = self.construct_object(node.value[key_node])
136 mapping['='] = value
137 else:
138 key = self.construct_object(key_node)
139 try:
140 duplicate_key = key in mapping
141 except TypeError, exc:
142 raise ConstructorError("while constructing a mapping", node.start_mark,
143 "found unacceptable key (%s)" % exc, key_node.start_mark)
144 if duplicate_key:
145 raise ConstructorError("while constructing a mapping", node.start_mark,
146 "found duplicate key", key_node.start_mark)
147 value = self.construct_object(node.value[key_node])
148 mapping[key] = value
149 if merge is not None:
150 merge.append(mapping)
151 mapping = {}
152 for submapping in merge:
153 mapping.update(submapping)
154 return mapping
156 def construct_pairs(self, node):
157 if not isinstance(node, MappingNode):
158 raise ConstructorError(None, None,
159 "expected a mapping node, but found %s" % node.id,
160 node.start_mark)
161 pairs = []
162 for key_node in node.value:
163 key = self.construct_object(key_node)
164 value = self.construct_object(node.value[key_node])
165 pairs.append((key, value))
166 return pairs
168 def add_constructor(cls, tag, constructor):
169 if not 'yaml_constructors' in cls.__dict__:
170 cls.yaml_constructors = cls.yaml_constructors.copy()
171 cls.yaml_constructors[tag] = constructor
172 add_constructor = classmethod(add_constructor)
174 def add_multi_constructor(cls, tag_prefix, multi_constructor):
175 if not 'yaml_multi_constructors' in cls.__dict__:
176 cls.yaml_multi_constructors = cls.yaml_multi_constructors.copy()
177 cls.yaml_multi_constructors[tag_prefix] = multi_constructor
178 add_multi_constructor = classmethod(add_multi_constructor)
180 class SafeConstructor(BaseConstructor):
182 def construct_yaml_null(self, node):
183 self.construct_scalar(node)
184 return None
186 bool_values = {
187 u'yes': True,
188 u'no': False,
189 u'true': True,
190 u'false': False,
191 u'on': True,
192 u'off': False,
195 def construct_yaml_bool(self, node):
196 value = self.construct_scalar(node)
197 return self.bool_values[value.lower()]
199 def construct_yaml_int(self, node):
200 value = str(self.construct_scalar(node))
201 value = value.replace('_', '')
202 sign = +1
203 if value[0] == '-':
204 sign = -1
205 if value[0] in '+-':
206 value = value[1:]
207 if value == '0':
208 return 0
209 elif value.startswith('0b'):
210 return sign*int(value[2:], 2)
211 elif value.startswith('0x'):
212 return sign*int(value[2:], 16)
213 elif value[0] == '0':
214 return sign*int(value, 8)
215 elif ':' in value:
216 digits = [int(part) for part in value.split(':')]
217 digits.reverse()
218 base = 1
219 value = 0
220 for digit in digits:
221 value += digit*base
222 base *= 60
223 return sign*value
224 else:
225 return sign*int(value)
227 inf_value = 1e300000
228 nan_value = inf_value/inf_value
230 def construct_yaml_float(self, node):
231 value = str(self.construct_scalar(node))
232 value = value.replace('_', '')
233 sign = +1
234 if value[0] == '-':
235 sign = -1
236 if value[0] in '+-':
237 value = value[1:]
238 if value.lower() == '.inf':
239 return sign*self.inf_value
240 elif value.lower() == '.nan':
241 return self.nan_value
242 elif ':' in value:
243 digits = [float(part) for part in value.split(':')]
244 digits.reverse()
245 base = 1
246 value = 0.0
247 for digit in digits:
248 value += digit*base
249 base *= 60
250 return sign*value
251 else:
252 return float(value)
254 def construct_yaml_binary(self, node):
255 value = self.construct_scalar(node)
256 try:
257 return str(value).decode('base64')
258 except (binascii.Error, UnicodeEncodeError), exc:
259 raise ConstructorError(None, None,
260 "failed to decode base64 data: %s" % exc, node.start_mark)
262 timestamp_regexp = re.compile(
263 ur'''^(?P<year>[0-9][0-9][0-9][0-9])
264 -(?P<month>[0-9][0-9]?)
265 -(?P<day>[0-9][0-9]?)
266 (?:(?:[Tt]|[ \t]+)
267 (?P<hour>[0-9][0-9]?)
268 :(?P<minute>[0-9][0-9])
269 :(?P<second>[0-9][0-9])
270 (?:\.(?P<fraction>[0-9]*))?
271 (?:[ \t]*(?:Z|(?P<tz_hour>[-+][0-9][0-9]?)
272 (?::(?P<tz_minute>[0-9][0-9])?)?))?)?$''', re.X)
274 def construct_yaml_timestamp(self, node):
275 value = self.construct_scalar(node)
276 match = self.timestamp_regexp.match(node.value)
277 values = match.groupdict()
278 for key in values:
279 if values[key]:
280 values[key] = int(values[key])
281 else:
282 values[key] = 0
283 fraction = values['fraction']
284 if fraction:
285 while 10*fraction < 1000000:
286 fraction *= 10
287 values['fraction'] = fraction
288 stamp = datetime.datetime(values['year'], values['month'], values['day'],
289 values['hour'], values['minute'], values['second'], values['fraction'])
290 diff = datetime.timedelta(hours=values['tz_hour'], minutes=values['tz_minute'])
291 return stamp-diff
293 def construct_yaml_omap(self, node):
294 # Note: we do not check for duplicate keys, because it's too
295 # CPU-expensive.
296 if not isinstance(node, SequenceNode):
297 raise ConstructorError("while constructing an ordered map", node.start_mark,
298 "expected a sequence, but found %s" % node.id, node.start_mark)
299 omap = []
300 for subnode in node.value:
301 if not isinstance(subnode, MappingNode):
302 raise ConstructorError("while constructing an ordered map", node.start_mark,
303 "expected a mapping of length 1, but found %s" % subnode.id,
304 subnode.start_mark)
305 if len(subnode.value) != 1:
306 raise ConstructorError("while constructing an ordered map", node.start_mark,
307 "expected a single mapping item, but found %d items" % len(subnode.value),
308 subnode.start_mark)
309 key_node = subnode.value.keys()[0]
310 key = self.construct_object(key_node)
311 value = self.construct_object(subnode.value[key_node])
312 omap.append((key, value))
313 return omap
315 def construct_yaml_pairs(self, node):
316 # Note: the same code as `construct_yaml_omap`.
317 if not isinstance(node, SequenceNode):
318 raise ConstructorError("while constructing pairs", node.start_mark,
319 "expected a sequence, but found %s" % node.id, node.start_mark)
320 pairs = []
321 for subnode in node.value:
322 if not isinstance(subnode, MappingNode):
323 raise ConstructorError("while constructing pairs", node.start_mark,
324 "expected a mapping of length 1, but found %s" % subnode.id,
325 subnode.start_mark)
326 if len(subnode.value) != 1:
327 raise ConstructorError("while constructing pairs", node.start_mark,
328 "expected a single mapping item, but found %d items" % len(subnode.value),
329 subnode.start_mark)
330 key_node = subnode.value.keys()[0]
331 key = self.construct_object(key_node)
332 value = self.construct_object(subnode.value[key_node])
333 pairs.append((key, value))
334 return pairs
336 def construct_yaml_set(self, node):
337 value = self.construct_mapping(node)
338 return set(value)
340 def construct_yaml_str(self, node):
341 value = self.construct_scalar(node)
342 try:
343 return str(value)
344 except UnicodeEncodeError:
345 return value
347 def construct_yaml_seq(self, node):
348 return self.construct_sequence(node)
350 def construct_yaml_map(self, node):
351 return self.construct_mapping(node)
353 def construct_yaml_object(self, node, cls):
354 state = self.construct_mapping(node)
355 data = cls.__new__(cls)
356 if hasattr(data, '__setstate__'):
357 data.__setstate__(state)
358 else:
359 data.__dict__.update(state)
360 return data
362 def construct_undefined(self, node):
363 raise ConstructorError(None, None,
364 "could not determine a constructor for the tag %r" % node.tag.encode('utf-8'),
365 node.start_mark)
367 SafeConstructor.add_constructor(
368 u'tag:yaml.org,2002:null',
369 SafeConstructor.construct_yaml_null)
371 SafeConstructor.add_constructor(
372 u'tag:yaml.org,2002:bool',
373 SafeConstructor.construct_yaml_bool)
375 SafeConstructor.add_constructor(
376 u'tag:yaml.org,2002:int',
377 SafeConstructor.construct_yaml_int)
379 SafeConstructor.add_constructor(
380 u'tag:yaml.org,2002:float',
381 SafeConstructor.construct_yaml_float)
383 SafeConstructor.add_constructor(
384 u'tag:yaml.org,2002:binary',
385 SafeConstructor.construct_yaml_binary)
387 if datetime_available:
388 SafeConstructor.add_constructor(
389 u'tag:yaml.org,2002:timestamp',
390 SafeConstructor.construct_yaml_timestamp)
392 SafeConstructor.add_constructor(
393 u'tag:yaml.org,2002:omap',
394 SafeConstructor.construct_yaml_omap)
396 SafeConstructor.add_constructor(
397 u'tag:yaml.org,2002:pairs',
398 SafeConstructor.construct_yaml_pairs)
400 SafeConstructor.add_constructor(
401 u'tag:yaml.org,2002:set',
402 SafeConstructor.construct_yaml_set)
404 SafeConstructor.add_constructor(
405 u'tag:yaml.org,2002:str',
406 SafeConstructor.construct_yaml_str)
408 SafeConstructor.add_constructor(
409 u'tag:yaml.org,2002:seq',
410 SafeConstructor.construct_yaml_seq)
412 SafeConstructor.add_constructor(
413 u'tag:yaml.org,2002:map',
414 SafeConstructor.construct_yaml_map)
416 SafeConstructor.add_constructor(None,
417 SafeConstructor.construct_undefined)
419 class Constructor(SafeConstructor):
421 def construct_python_str(self, node):
422 return self.construct_scalar(node).encode('utf-8')
424 def construct_python_unicode(self, node):
425 return self.construct_scalar(node)
427 def construct_python_long(self, node):
428 return long(self.construct_yaml_int(node))
430 def construct_python_complex(self, node):
431 return complex(self.construct_scalar(node))
433 def construct_python_tuple(self, node):
434 return tuple(self.construct_yaml_seq(node))
436 def find_python_module(self, name, mark):
437 if not name:
438 raise ConstructorError("while constructing a Python module", mark,
439 "expected non-empty name appended to the tag", mark)
440 try:
441 __import__(name)
442 except ImportError, exc:
443 raise ConstructorError("while constructing a Python module", mark,
444 "cannot find module %r (%s)" % (name.encode('utf-8'), exc), mark)
445 return sys.modules[name]
447 def find_python_name(self, name, mark):
448 if not name:
449 raise ConstructorError("while constructing a Python object", mark,
450 "expected non-empty name appended to the tag", mark)
451 if u'.' in name:
452 module_name, object_name = name.rsplit('.', 1)
453 else:
454 module_name = '__builtin__'
455 object_name = name
456 try:
457 __import__(module_name)
458 except ImportError, exc:
459 raise ConstructorError("while constructing a Python object", mark,
460 "cannot find module %r (%s)" % (module_name.encode('utf-8'), exc), mark)
461 module = sys.modules[module_name]
462 if not hasattr(module, object_name):
463 raise ConstructorError("while constructing a Python object", mark,
464 "cannot find %r in the module %r" % (object_name.encode('utf-8'),
465 module.__name__), mark)
466 return getattr(module, object_name)
468 def construct_python_name(self, suffix, node):
469 value = self.construct_scalar(node)
470 if value:
471 raise ConstructorError("while constructing a Python name", node.start_mark,
472 "expected the empty value, but found %r" % value.encode('utf-8'),
473 node.start_mark)
474 return self.find_python_name(suffix, node.start_mark)
476 def construct_python_module(self, suffix, node):
477 value = self.construct_scalar(node)
478 if value:
479 raise ConstructorError("while constructing a Python module", node.start_mark,
480 "expected the empty value, but found %r" % value.encode('utf-8'),
481 node.start_mark)
482 return self.find_python_module(suffix, node.start_mark)
484 Constructor.add_constructor(
485 u'tag:yaml.org,2002:python/none',
486 Constructor.construct_yaml_null)
488 Constructor.add_constructor(
489 u'tag:yaml.org,2002:python/bool',
490 Constructor.construct_yaml_bool)
492 Constructor.add_constructor(
493 u'tag:yaml.org,2002:python/str',
494 Constructor.construct_python_str)
496 Constructor.add_constructor(
497 u'tag:yaml.org,2002:python/unicode',
498 Constructor.construct_python_unicode)
500 Constructor.add_constructor(
501 u'tag:yaml.org,2002:python/int',
502 Constructor.construct_yaml_int)
504 Constructor.add_constructor(
505 u'tag:yaml.org,2002:python/long',
506 Constructor.construct_python_long)
508 Constructor.add_constructor(
509 u'tag:yaml.org,2002:python/float',
510 Constructor.construct_yaml_float)
512 Constructor.add_constructor(
513 u'tag:yaml.org,2002:python/complex',
514 Constructor.construct_python_complex)
516 Constructor.add_constructor(
517 u'tag:yaml.org,2002:python/list',
518 Constructor.construct_yaml_seq)
520 Constructor.add_constructor(
521 u'tag:yaml.org,2002:python/tuple',
522 Constructor.construct_python_tuple)
524 Constructor.add_constructor(
525 u'tag:yaml.org,2002:python/dict',
526 Constructor.construct_yaml_map)
528 Constructor.add_multi_constructor(
529 u'tag:yaml.org,2002:python/name:',
530 Constructor.construct_python_name)
532 Constructor.add_multi_constructor(
533 u'tag:yaml.org,2002:python/module:',
534 Constructor.construct_python_module)