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_marker
,
94 "found duplicate merge key", key_node
.start_marker
)
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_marker
)
106 merge
.append(self
.construct_mapping(subnode
))
109 raise ConstructorError("while constructing a mapping", node
.start_marker
,
110 "expected a mapping or list of mappings for merging, but found %s"
111 % value_node
.id, value_node
.start_marker
)
112 elif key_node
.tag
== u
'tag:yaml.org,2002:value':
114 raise ConstructorError("while construction a mapping", node
.start_marker
,
115 "found duplicate value key", key_node
.start_marker
)
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_marker
,
124 "found unacceptable key (%s)" % exc
, key_node
.start_marker
)
126 raise ConstructorError("while constructing a mapping", node
.start_marker
,
127 "found duplicate key", key_node
.start_marker
)
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
)
174 def construct_yaml_bool(self
, node
):
175 value
= self
.construct_scalar(node
)
176 return self
.bool_values
[value
.lower()]
178 def construct_yaml_int(self
, node
):
179 value
= str(self
.construct_scalar(node
))
180 value
= value
.replace('_', '')
188 elif value
.startswith('0b'):
189 return sign
*int(value
[2:], 2)
190 elif value
.startswith('0x'):
191 return sign
*int(value
[2:], 16)
192 elif value
[0] == '0':
193 return sign
*int(value
, 8)
195 digits
= [int(part
) for part
in value
.split(':')]
204 return sign
*int(value
)
207 nan_value
= inf_value
/inf_value
209 def construct_yaml_float(self
, node
):
210 value
= str(self
.construct_scalar(node
))
211 value
= value
.replace('_', '')
217 if value
.lower() == '.inf':
218 return sign
*self
.inf_value
219 elif value
.lower() == '.nan':
220 return self
.nan_value
222 digits
= [float(part
) for part
in value
.split(':')]
233 def construct_yaml_binary(self
, node
):
234 value
= self
.construct_scalar(node
)
236 return str(value
).decode('base64')
237 except (binascii
.Error
, UnicodeEncodeError), exc
:
238 raise ConstructorError(None, None,
239 "failed to decode base64 data: %s" % exc
, node
.start_mark
)
241 timestamp_regexp
= re
.compile(
242 ur
'''^(?P<year>[0-9][0-9][0-9][0-9])
243 -(?P<month>[0-9][0-9]?)
244 -(?P<day>[0-9][0-9]?)
246 (?P<hour>[0-9][0-9]?)
247 :(?P<minute>[0-9][0-9])
248 :(?P<second>[0-9][0-9])
249 (?:\.(?P<fraction>[0-9]*))?
250 (?:[ \t]*(?:Z|(?P<tz_hour>[-+][0-9][0-9]?)
251 (?::(?P<tz_minute>[0-9][0-9])?)))?$''', re
.X
),
253 def construct_yaml_timestamp(self
, node
):
254 value
= self
.construct_scalar(node
)
255 match
= self
.timestamp_expr
.match(node
.value
)
256 values
= match
.groupdict()
259 values
[key
] = int(values
[key
])
262 fraction
= values
['fraction']
264 while 10*fraction
< 1000000:
266 values
['fraction'] = fraction
267 stamp
= datetime
.datetime(values
['year'], values
['month'], values
['day'],
268 values
['hour'], values
['minute'], values
['second'], values
['fraction'])
269 diff
= datetime
.timedelta(hours
=values
['tz_hour'], minutes
=values
['tz_minute'])
272 def construct_yaml_omap(self
, node
):
273 # Note: we do not check for duplicate keys, because it's too
275 if not isinstance(node
, SequenceNode
):
276 raise ConstructorError("while constructing an ordered map", node
.start_marker
,
277 "expected a sequence, but found %s" % node
.id, node
.start_marker
)
279 for subnode
in node
.value
:
280 if not isinstance(subnode
, MappingNode
):
281 raise ConstructorError("while constructing an ordered map", node
.start_marker
,
282 "expected a mapping of length 1, but found %s" % subnode
.id,
283 subnode
.start_marker
)
284 if len(subnode
.value
) != 1:
285 raise ConstructorError("while constructing an ordered map", node
.start_marker
,
286 "expected a single mapping item, but found %d items" % len(subnode
.value
),
287 subnode
.start_marker
)
288 key_node
= subnode
.value
.keys()[0]
289 key
= self
.construct_object(key_node
)
290 value
= self
.construct_object(subnode
.value
[key_node
])
291 omap
.append((key
, value
))
293 def construct_yaml_pairs(self
, node
):
294 # Note: the same code as `construct_yaml_omap`.
295 if not isinstance(node
, SequenceNode
):
296 raise ConstructorError("while constructing pairs", node
.start_marker
,
297 "expected a sequence, but found %s" % node
.id, node
.start_marker
)
299 for subnode
in node
.value
:
300 if not isinstance(subnode
, MappingNode
):
301 raise ConstructorError("while constructing pairs", node
.start_marker
,
302 "expected a mapping of length 1, but found %s" % subnode
.id,
303 subnode
.start_marker
)
304 if len(subnode
.value
) != 1:
305 raise ConstructorError("while constructing pairs", node
.start_marker
,
306 "expected a single mapping item, but found %d items" % len(subnode
.value
),
307 subnode
.start_marker
)
308 key_node
= subnode
.value
.keys()[0]
309 key
= self
.construct_object(key_node
)
310 value
= self
.construct_object(subnode
.value
[key_node
])
311 omap
.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:timestamp',
353 Constructor
.construct_yaml_timestamp
)
355 Constructor
.add_constructor(
356 u
'tag:yaml.org,2002:omap',
357 Constructor
.construct_yaml_omap
)
359 Constructor
.add_constructor(
360 u
'tag:yaml.org,2002:pairs',
361 Constructor
.construct_yaml_pairs
)
363 Constructor
.add_constructor(
364 u
'tag:yaml.org,2002:set',
365 Constructor
.construct_yaml_set
)
367 Constructor
.add_constructor(
368 u
'tag:yaml.org,2002:str',
369 Constructor
.construct_yaml_str
)
371 Constructor
.add_constructor(
372 u
'tag:yaml.org,2002:seq',
373 Constructor
.construct_yaml_seq
)
375 Constructor
.add_constructor(
376 u
'tag:yaml.org,2002:map',
377 Constructor
.construct_yaml_map
)
379 Constructor
.add_constructor(None,
380 Constructor
.construct_undefined
)
382 class YAMLObjectMetaclass(type):
384 def __init__(cls
, name
, bases
, kwds
):
385 super(YAMLObjectMetaclass
, cls
).__init
__(name
, bases
, kwds
)
386 if 'yaml_tag' in kwds
and kwds
['yaml_tag'] is not None:
387 cls
.yaml_constructor_class
.add_constructor(cls
.yaml_tag
, cls
.from_yaml
)
389 class YAMLObject(object):
391 __metaclass__
= YAMLObjectMetaclass
393 yaml_constructor_class
= Constructor
397 def from_yaml(cls
, constructor
, node
):
398 raise ConstructorError(None, None,
399 "found undefined constructor for the tag %r"
400 % node
.tag
.encode('utf-8'), node
.start_marker
)
401 from_yaml
= classmethod(from_yaml
)
404 assert False # needs dumper