2 __all__
= ['BaseConstructor', 'Constructor', 'ConstructorError',
3 'YAMLObject', 'YAMLObjectMetaclass']
10 datetime_available
= True
12 datetime_available
= False
17 from sets
import Set
as set
21 class ConstructorError(MarkedYAMLError
):
24 class BaseConstructor
:
26 def __init__(self
, resolver
):
27 self
.resolver
= resolver
28 self
.constructed_objects
= {}
31 # If there are more documents available?
32 return self
.resolver
.check()
35 # Construct and return the next document.
36 if self
.resolver
.check():
37 return self
.construct_document(self
.resolver
.get())
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
= {}
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
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,
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,
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,
90 for key_node
in node
.value
:
91 if key_node
.tag
== u
'tag:yaml.org,2002:merge':
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
):
100 for subnode
in value_node
.value
:
101 if not isinstance(subnode
, MappingNode
):
102 raise ConstructorError("while constructing a mapping",
104 "expected a mapping for merging, but found %s"
105 % subnode
.id, subnode
.start_mark
)
106 merge
.append(self
.construct_mapping(subnode
))
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':
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
])
119 key
= self
.construct_object(key_node
)
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
)
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
])
130 if merge
is not None:
131 merge
.append(mapping
)
133 for submapping
in merge
:
134 mapping
.update(submapping
)
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,
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
))
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
)
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('_', '')
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)
193 digits
= [int(part
) for part
in value
.split(':')]
202 return sign
*int(value
)
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('_', '')
215 if value
.lower() == '.inf':
216 return sign
*self
.inf_value
217 elif value
.lower() == '.nan':
218 return self
.nan_value
220 digits
= [float(part
) for part
in value
.split(':')]
231 def construct_yaml_binary(self
, node
):
232 value
= self
.construct_scalar(node
)
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]?)
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()
257 values
[key
] = int(values
[key
])
260 fraction
= values
['fraction']
262 while 10*fraction
< 1000000:
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'])
270 def construct_yaml_omap(self
, node
):
271 # Note: we do not check for duplicate keys, because it's too
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
)
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,
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
),
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
))
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
)
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,
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
),
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
))
313 def construct_yaml_set(self
, node
):
314 value
= self
.construct_mapping(node
)
317 def construct_yaml_str(self
, node
):
318 value
= self
.construct_scalar(node
)
321 except UnicodeEncodeError:
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'),
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
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
)
409 assert False # needs dumper