Back to work :). Rename markers to marks.
[pyyaml/python3.git] / lib / yaml / constructor.py
blobb3f5a88f922cfb88094a9a4a717e7d413fb4af0d
2 __all__ = ['BaseConstructor', 'Constructor', 'ConstructorError',
3 'YAMLObject', 'YAMLObjectMetaclass']
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
21 class ConstructorError(MarkedYAMLError):
22 pass
24 class BaseConstructor:
26 def __init__(self, resolver):
27 self.resolver = resolver
28 self.constructed_objects = {}
30 def check(self):
31 # If there are more documents available?
32 return self.resolver.check()
34 def get(self):
35 # Construct and return the next document.
36 if self.resolver.check():
37 return self.construct_document(self.resolver.get())
39 def __iter__(self):
40 # Iterator protocol.
41 while self.resolver.check():
42 yield self.construct_document(self.resolver.get())
44 def construct_document(self, node):
45 native = self.construct_object(node)
46 self.constructed_objects = {}
47 return native
49 def construct_object(self, node):
50 if node in self.constructed_objects:
51 return self.constructed_objects[node]
52 if node.tag in self.yaml_constructors:
53 native = self.yaml_constructors[node.tag](self, node)
54 elif None in self.yaml_constructors:
55 native = self.yaml_constructors[None](self, node)
56 elif isinstance(node, ScalarNode):
57 native = self.construct_scalar(node)
58 elif isinstance(node, SequenceNode):
59 native = self.construct_sequence(node)
60 elif isinstance(node, MappingNode):
61 native = self.construct_mapping(node)
62 self.constructed_objects[node] = native
63 return native
65 def construct_scalar(self, node):
66 if not isinstance(node, ScalarNode):
67 if isinstance(node, MappingNode):
68 for key_node in node.value:
69 if key_node.tag == u'tag:yaml.org,2002:value':
70 return self.construct_scalar(node.value[key_node])
71 raise ConstructorError(None, None,
72 "expected a scalar node, but found %s" % node.id,
73 node.start_mark)
74 return node.value
76 def construct_sequence(self, node):
77 if not isinstance(node, SequenceNode):
78 raise ConstructorError(None, None,
79 "expected a sequence node, but found %s" % node.id,
80 node.start_mark)
81 return [self.construct_object(child) for child in node.value]
83 def construct_mapping(self, node):
84 if not isinstance(node, MappingNode):
85 raise ConstructorError(None, None,
86 "expected a mapping node, but found %s" % node.id,
87 node.start_mark)
88 mapping = {}
89 merge = None
90 for key_node in node.value:
91 if key_node.tag == u'tag:yaml.org,2002:merge':
92 if merge is not None:
93 raise ConstructorError("while constructing a mapping", node.start_mark,
94 "found duplicate merge key", key_node.start_mark)
95 value_node = node.value[key_node]
96 if isinstance(value_node, MappingNode):
97 merge = [self.construct_mapping(value_node)]
98 elif isinstance(value_node, SequenceNode):
99 merge = []
100 for subnode in value_node.value:
101 if not isinstance(subnode, MappingNode):
102 raise ConstructorError("while constructing a mapping",
103 node.start_mark,
104 "expected a mapping for merging, but found %s"
105 % subnode.id, subnode.start_mark)
106 merge.append(self.construct_mapping(subnode))
107 merge.reverse()
108 else:
109 raise ConstructorError("while constructing a mapping", node.start_mark,
110 "expected a mapping or list of mappings for merging, but found %s"
111 % value_node.id, value_node.start_mark)
112 elif key_node.tag == u'tag:yaml.org,2002:value':
113 if '=' in mapping:
114 raise ConstructorError("while construction a mapping", node.start_mark,
115 "found duplicate value key", key_node.start_mark)
116 value = self.construct_object(node.value[key_node])
117 mapping['='] = value
118 else:
119 key = self.construct_object(key_node)
120 try:
121 duplicate_key = key in mapping
122 except TypeError, exc:
123 raise ConstructorError("while constructing a mapping", node.start_mark,
124 "found unacceptable key (%s)" % exc, key_node.start_mark)
125 if duplicate_key:
126 raise ConstructorError("while constructing a mapping", node.start_mark,
127 "found duplicate key", key_node.start_mark)
128 value = self.construct_object(node.value[key_node])
129 mapping[key] = value
130 if merge is not None:
131 merge.append(mapping)
132 mapping = {}
133 for submapping in merge:
134 mapping.update(submapping)
135 return mapping
137 def construct_pairs(self, node):
138 if not isinstance(node, MappingNode):
139 raise ConstructorError(None, None,
140 "expected a mapping node, but found %s" % node.id,
141 node.start_mark)
142 pairs = []
143 for key_node in node.value:
144 key = self.construct_object(key_node)
145 value = self.construct_object(node.value[key_node])
146 pairs.append((key, value))
147 return pairs
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 yaml_constructors = {}
157 class Constructor(BaseConstructor):
159 def construct_yaml_null(self, node):
160 self.construct_scalar(node)
161 return None
163 bool_values = {
164 u'yes': True,
165 u'no': False,
166 u'true': True,
167 u'false': False,
168 u'on': True,
169 u'off': False,
172 def construct_yaml_bool(self, node):
173 value = self.construct_scalar(node)
174 return self.bool_values[value.lower()]
176 def construct_yaml_int(self, node):
177 value = str(self.construct_scalar(node))
178 value = value.replace('_', '')
179 sign = +1
180 if value[0] == '-':
181 sign = -1
182 if value[0] in '+-':
183 value = value[1:]
184 if value == '0':
185 return 0
186 elif value.startswith('0b'):
187 return sign*int(value[2:], 2)
188 elif value.startswith('0x'):
189 return sign*int(value[2:], 16)
190 elif value[0] == '0':
191 return sign*int(value, 8)
192 elif ':' in value:
193 digits = [int(part) for part in value.split(':')]
194 digits.reverse()
195 base = 1
196 value = 0
197 for digit in digits:
198 value += digit*base
199 base *= 60
200 return sign*value
201 else:
202 return sign*int(value)
204 inf_value = 1e300000
205 nan_value = inf_value/inf_value
207 def construct_yaml_float(self, node):
208 value = str(self.construct_scalar(node))
209 value = value.replace('_', '')
210 sign = +1
211 if value[0] == '-':
212 sign = -1
213 if value[0] in '+-':
214 value = value[1:]
215 if value.lower() == '.inf':
216 return sign*self.inf_value
217 elif value.lower() == '.nan':
218 return self.nan_value
219 elif ':' in value:
220 digits = [float(part) for part in value.split(':')]
221 digits.reverse()
222 base = 1
223 value = 0.0
224 for digit in digits:
225 value += digit*base
226 base *= 60
227 return sign*value
228 else:
229 return float(value)
231 def construct_yaml_binary(self, node):
232 value = self.construct_scalar(node)
233 try:
234 return str(value).decode('base64')
235 except (binascii.Error, UnicodeEncodeError), exc:
236 raise ConstructorError(None, None,
237 "failed to decode base64 data: %s" % exc, node.start_mark)
239 timestamp_regexp = re.compile(
240 ur'''^(?P<year>[0-9][0-9][0-9][0-9])
241 -(?P<month>[0-9][0-9]?)
242 -(?P<day>[0-9][0-9]?)
243 (?:(?:[Tt]|[ \t]+)
244 (?P<hour>[0-9][0-9]?)
245 :(?P<minute>[0-9][0-9])
246 :(?P<second>[0-9][0-9])
247 (?:\.(?P<fraction>[0-9]*))?
248 (?:[ \t]*(?:Z|(?P<tz_hour>[-+][0-9][0-9]?)
249 (?::(?P<tz_minute>[0-9][0-9])?)?))?)?$''', re.X)
251 def construct_yaml_timestamp(self, node):
252 value = self.construct_scalar(node)
253 match = self.timestamp_regexp.match(node.value)
254 values = match.groupdict()
255 for key in values:
256 if values[key]:
257 values[key] = int(values[key])
258 else:
259 values[key] = 0
260 fraction = values['fraction']
261 if fraction:
262 while 10*fraction < 1000000:
263 fraction *= 10
264 values['fraction'] = fraction
265 stamp = datetime.datetime(values['year'], values['month'], values['day'],
266 values['hour'], values['minute'], values['second'], values['fraction'])
267 diff = datetime.timedelta(hours=values['tz_hour'], minutes=values['tz_minute'])
268 return stamp-diff
270 def construct_yaml_omap(self, node):
271 # Note: we do not check for duplicate keys, because it's too
272 # CPU-expensive.
273 if not isinstance(node, SequenceNode):
274 raise ConstructorError("while constructing an ordered map", node.start_mark,
275 "expected a sequence, but found %s" % node.id, node.start_mark)
276 omap = []
277 for subnode in node.value:
278 if not isinstance(subnode, MappingNode):
279 raise ConstructorError("while constructing an ordered map", node.start_mark,
280 "expected a mapping of length 1, but found %s" % subnode.id,
281 subnode.start_mark)
282 if len(subnode.value) != 1:
283 raise ConstructorError("while constructing an ordered map", node.start_mark,
284 "expected a single mapping item, but found %d items" % len(subnode.value),
285 subnode.start_mark)
286 key_node = subnode.value.keys()[0]
287 key = self.construct_object(key_node)
288 value = self.construct_object(subnode.value[key_node])
289 omap.append((key, value))
290 return omap
292 def construct_yaml_pairs(self, node):
293 # Note: the same code as `construct_yaml_omap`.
294 if not isinstance(node, SequenceNode):
295 raise ConstructorError("while constructing pairs", node.start_mark,
296 "expected a sequence, but found %s" % node.id, node.start_mark)
297 pairs = []
298 for subnode in node.value:
299 if not isinstance(subnode, MappingNode):
300 raise ConstructorError("while constructing pairs", node.start_mark,
301 "expected a mapping of length 1, but found %s" % subnode.id,
302 subnode.start_mark)
303 if len(subnode.value) != 1:
304 raise ConstructorError("while constructing pairs", node.start_mark,
305 "expected a single mapping item, but found %d items" % len(subnode.value),
306 subnode.start_mark)
307 key_node = subnode.value.keys()[0]
308 key = self.construct_object(key_node)
309 value = self.construct_object(subnode.value[key_node])
310 pairs.append((key, value))
311 return pairs
313 def construct_yaml_set(self, node):
314 value = self.construct_mapping(node)
315 return set(value)
317 def construct_yaml_str(self, node):
318 value = self.construct_scalar(node)
319 try:
320 return str(value)
321 except UnicodeEncodeError:
322 return value
324 def construct_yaml_seq(self, node):
325 return self.construct_sequence(node)
327 def construct_yaml_map(self, node):
328 return self.construct_mapping(node)
330 def construct_undefined(self, node):
331 raise ConstructorError(None, None,
332 "could not determine a constructor for the tag %r" % node.tag.encode('utf-8'),
333 node.start_mark)
335 Constructor.add_constructor(
336 u'tag:yaml.org,2002:null',
337 Constructor.construct_yaml_null)
339 Constructor.add_constructor(
340 u'tag:yaml.org,2002:bool',
341 Constructor.construct_yaml_bool)
343 Constructor.add_constructor(
344 u'tag:yaml.org,2002:int',
345 Constructor.construct_yaml_int)
347 Constructor.add_constructor(
348 u'tag:yaml.org,2002:float',
349 Constructor.construct_yaml_float)
351 Constructor.add_constructor(
352 u'tag:yaml.org,2002:binary',
353 Constructor.construct_yaml_binary)
355 if datetime_available:
356 Constructor.add_constructor(
357 u'tag:yaml.org,2002:timestamp',
358 Constructor.construct_yaml_timestamp)
360 Constructor.add_constructor(
361 u'tag:yaml.org,2002:omap',
362 Constructor.construct_yaml_omap)
364 Constructor.add_constructor(
365 u'tag:yaml.org,2002:pairs',
366 Constructor.construct_yaml_pairs)
368 Constructor.add_constructor(
369 u'tag:yaml.org,2002:set',
370 Constructor.construct_yaml_set)
372 Constructor.add_constructor(
373 u'tag:yaml.org,2002:str',
374 Constructor.construct_yaml_str)
376 Constructor.add_constructor(
377 u'tag:yaml.org,2002:seq',
378 Constructor.construct_yaml_seq)
380 Constructor.add_constructor(
381 u'tag:yaml.org,2002:map',
382 Constructor.construct_yaml_map)
384 Constructor.add_constructor(None,
385 Constructor.construct_undefined)
387 class YAMLObjectMetaclass(type):
389 def __init__(cls, name, bases, kwds):
390 super(YAMLObjectMetaclass, cls).__init__(name, bases, kwds)
391 if 'yaml_tag' in kwds and kwds['yaml_tag'] is not None:
392 cls.yaml_constructor.add_constructor(cls.yaml_tag, cls.from_yaml)
394 class YAMLObject(object):
396 __metaclass__ = YAMLObjectMetaclass
398 yaml_constructor = Constructor
400 yaml_tag = None
402 def from_yaml(cls, constructor, node):
403 raise ConstructorError(None, None,
404 "found undefined constructor for the tag %r"
405 % node.tag.encode('utf-8'), node.start_mark)
406 from_yaml = classmethod(from_yaml)
408 def to_yaml(self):
409 assert False # needs dumper