Fix a bug in Representer.represent_object: copy_reg.dispatch_table was not correctly...
[pyyaml/python3.git] / lib / yaml / representer.py
blob251681380006521c582912f67ab806bee4bd722a
2 __all__ = ['BaseRepresenter', 'SafeRepresenter', 'Representer',
3 'RepresenterError']
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 sys, copy_reg
21 class RepresenterError(YAMLError):
22 pass
24 class BaseRepresenter:
26 yaml_representers = {}
27 yaml_multi_representers = {}
29 def __init__(self, default_style=None, default_flow_style=None):
30 self.default_style = default_style
31 self.default_flow_style = default_flow_style
32 self.represented_objects = {}
34 def represent(self, data):
35 node = self.represent_data(data)
36 self.serialize(node)
37 self.represented_objects = {}
39 class C: pass
40 c = C()
41 def f(): pass
42 classobj_type = type(C)
43 instance_type = type(c)
44 function_type = type(f)
45 builtin_function_type = type(abs)
46 module_type = type(sys)
47 del C, c, f
49 def get_classobj_bases(self, cls):
50 bases = [cls]
51 for base in cls.__bases__:
52 bases.extend(self.get_classobj_bases(base))
53 return bases
55 def represent_data(self, data):
56 if self.ignore_aliases(data):
57 alias_key = None
58 else:
59 alias_key = id(data)
60 if alias_key is not None:
61 if alias_key in self.represented_objects:
62 node = self.represented_objects[alias_key]
63 if node is None:
64 raise RepresenterError("recursive objects are not allowed: %r" % data)
65 return node
66 self.represented_objects[alias_key] = None
67 data_types = type(data).__mro__
68 if type(data) is self.instance_type:
69 data_types = self.get_classobj_bases(data.__class__)+list(data_types)
70 if data_types[0] in self.yaml_representers:
71 node = self.yaml_representers[data_types[0]](self, data)
72 else:
73 for data_type in data_types:
74 if data_type in self.yaml_multi_representers:
75 node = self.yaml_multi_representers[data_type](self, data)
76 break
77 else:
78 if None in self.yaml_multi_representers:
79 node = self.yaml_multi_representers[None](self, data)
80 elif None in self.yaml_representers:
81 node = self.yaml_representers[None](self, data)
82 else:
83 node = ScalarNode(None, unicode(data))
84 if alias_key is not None:
85 self.represented_objects[alias_key] = node
86 return node
88 def add_representer(cls, data_type, representer):
89 if not 'yaml_representers' in cls.__dict__:
90 cls.yaml_representers = cls.yaml_representers.copy()
91 cls.yaml_representers[data_type] = representer
92 add_representer = classmethod(add_representer)
94 def add_multi_representer(cls, data_type, representer):
95 if not 'yaml_multi_representers' in cls.__dict__:
96 cls.yaml_multi_representers = cls.yaml_multi_representers.copy()
97 cls.yaml_multi_representers[data_type] = representer
98 add_multi_representer = classmethod(add_multi_representer)
100 def represent_scalar(self, tag, value, style=None):
101 if style is None:
102 style = self.default_style
103 return ScalarNode(tag, value, style=style)
105 def represent_sequence(self, tag, sequence, flow_style=None):
106 best_style = True
107 value = []
108 for item in sequence:
109 node_item = self.represent_data(item)
110 if not (isinstance(node_item, ScalarNode) and not node_item.style):
111 best_style = False
112 value.append(self.represent_data(item))
113 if flow_style is None:
114 flow_style = self.default_flow_style
115 if flow_style is None:
116 flow_style = best_style
117 return SequenceNode(tag, value, flow_style=flow_style)
119 def represent_mapping(self, tag, mapping, flow_style=None):
120 best_style = True
121 if hasattr(mapping, 'keys'):
122 value = {}
123 for item_key in mapping.keys():
124 item_value = mapping[item_key]
125 node_key = self.represent_data(item_key)
126 node_value = self.represent_data(item_value)
127 if not (isinstance(node_key, ScalarNode) and not node_key.style):
128 best_style = False
129 if not (isinstance(node_value, ScalarNode) and not node_value.style):
130 best_style = False
131 value[node_key] = node_value
132 else:
133 value = []
134 for item_key, item_value in mapping:
135 node_key = self.represent_data(item_key)
136 node_value = self.represent_data(item_value)
137 if not (isinstance(node_key, ScalarNode) and not node_key.style):
138 best_style = False
139 if not (isinstance(node_value, ScalarNode) and not node_value.style):
140 best_style = False
141 value.append((node_key, node_value))
142 if flow_style is None:
143 flow_style = self.default_flow_style
144 if flow_style is None:
145 flow_style = best_style
146 return MappingNode(tag, value, flow_style=flow_style)
148 def ignore_aliases(self, data):
149 return False
151 class SafeRepresenter(BaseRepresenter):
153 def ignore_aliases(self, data):
154 if data in [None, ()]:
155 return True
156 if isinstance(data, (str, unicode, bool, int, float)):
157 return True
159 def represent_none(self, data):
160 return self.represent_scalar(u'tag:yaml.org,2002:null',
161 u'null')
163 def represent_str(self, data):
164 tag = None
165 style = None
166 try:
167 data = unicode(data, 'ascii')
168 tag = u'tag:yaml.org,2002:str'
169 except UnicodeDecodeError:
170 try:
171 data = unicode(data, 'utf-8')
172 tag = u'tag:yaml.org,2002:str'
173 except UnicodeDecodeError:
174 data = data.encode('base64')
175 tag = u'tag:yaml.org,2002:binary'
176 style = '|'
177 return self.represent_scalar(tag, data, style=style)
179 def represent_unicode(self, data):
180 return self.represent_scalar(u'tag:yaml.org,2002:str', data)
182 def represent_bool(self, data):
183 if data:
184 value = u'true'
185 else:
186 value = u'false'
187 return self.represent_scalar(u'tag:yaml.org,2002:bool', value)
189 def represent_int(self, data):
190 return self.represent_scalar(u'tag:yaml.org,2002:int', unicode(data))
192 def represent_long(self, data):
193 return self.represent_scalar(u'tag:yaml.org,2002:int', unicode(data))
195 inf_value = 1e300
196 while repr(inf_value) != repr(inf_value*inf_value):
197 inf_value *= inf_value
199 def represent_float(self, data):
200 if data != data or (data == 0.0 and data == 1.0):
201 value = u'.nan'
202 elif data == self.inf_value:
203 value = u'.inf'
204 elif data == -self.inf_value:
205 value = u'-.inf'
206 else:
207 value = unicode(repr(data))
208 return self.represent_scalar(u'tag:yaml.org,2002:float', value)
210 def represent_list(self, data):
211 pairs = (len(data) > 0 and isinstance(data, list))
212 if pairs:
213 for item in data:
214 if not isinstance(item, tuple) or len(item) != 2:
215 pairs = False
216 break
217 if not pairs:
218 return self.represent_sequence(u'tag:yaml.org,2002:seq', data)
219 value = []
220 for item_key, item_value in data:
221 value.append(self.represent_mapping(u'tag:yaml.org,2002:map',
222 [(item_key, item_value)]))
223 return SequenceNode(u'tag:yaml.org,2002:pairs', value)
225 def represent_dict(self, data):
226 return self.represent_mapping(u'tag:yaml.org,2002:map', data)
228 def represent_set(self, data):
229 value = {}
230 for key in data:
231 value[key] = None
232 return self.represent_mapping(u'tag:yaml.org,2002:set', value)
234 def represent_date(self, data):
235 value = u'%04d-%02d-%02d' % (data.year, data.month, data.day)
236 return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value)
238 def represent_datetime(self, data):
239 value = u'%04d-%02d-%02d %02d:%02d:%02d' \
240 % (data.year, data.month, data.day,
241 data.hour, data.minute, data.second)
242 if data.microsecond:
243 value += u'.' + unicode(data.microsecond/1000000.0).split(u'.')[1]
244 if data.utcoffset():
245 value += unicode(data.utcoffset())
246 return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value)
248 def represent_yaml_object(self, tag, data, cls, flow_style=None):
249 if hasattr(data, '__getstate__'):
250 state = data.__getstate__()
251 else:
252 state = data.__dict__.copy()
253 if isinstance(state, dict):
254 state = state.items()
255 state.sort()
256 return self.represent_mapping(tag, state, flow_style=flow_style)
258 def represent_undefined(self, data):
259 raise RepresenterError("cannot represent an object: %s" % data)
261 SafeRepresenter.add_representer(type(None),
262 SafeRepresenter.represent_none)
264 SafeRepresenter.add_representer(str,
265 SafeRepresenter.represent_str)
267 SafeRepresenter.add_representer(unicode,
268 SafeRepresenter.represent_unicode)
270 SafeRepresenter.add_representer(bool,
271 SafeRepresenter.represent_bool)
273 SafeRepresenter.add_representer(int,
274 SafeRepresenter.represent_int)
276 SafeRepresenter.add_representer(long,
277 SafeRepresenter.represent_long)
279 SafeRepresenter.add_representer(float,
280 SafeRepresenter.represent_float)
282 SafeRepresenter.add_representer(list,
283 SafeRepresenter.represent_list)
285 SafeRepresenter.add_representer(tuple,
286 SafeRepresenter.represent_list)
288 SafeRepresenter.add_representer(dict,
289 SafeRepresenter.represent_dict)
291 SafeRepresenter.add_representer(set,
292 SafeRepresenter.represent_set)
294 if datetime_available:
295 SafeRepresenter.add_representer(datetime.date,
296 SafeRepresenter.represent_date)
297 SafeRepresenter.add_representer(datetime.datetime,
298 SafeRepresenter.represent_datetime)
300 SafeRepresenter.add_representer(None,
301 SafeRepresenter.represent_undefined)
303 class Representer(SafeRepresenter):
305 def represent_str(self, data):
306 tag = None
307 style = None
308 try:
309 data = unicode(data, 'ascii')
310 tag = u'tag:yaml.org,2002:str'
311 except UnicodeDecodeError:
312 try:
313 data = unicode(data, 'utf-8')
314 tag = u'tag:yaml.org,2002:python/str'
315 except UnicodeDecodeError:
316 data = data.encode('base64')
317 tag = u'tag:yaml.org,2002:binary'
318 style = '|'
319 return self.represent_scalar(tag, data, style=style)
321 def represent_unicode(self, data):
322 tag = None
323 try:
324 data.encode('ascii')
325 tag = u'tag:yaml.org,2002:python/unicode'
326 except UnicodeEncodeError:
327 tag = u'tag:yaml.org,2002:str'
328 return self.represent_scalar(tag, data)
330 def represent_long(self, data):
331 tag = u'tag:yaml.org,2002:int'
332 if int(data) is not data:
333 tag = u'tag:yaml.org,2002:python/long'
334 return self.represent_scalar(tag, unicode(data))
336 def represent_complex(self, data):
337 if data.imag == 0.0:
338 data = u'%r' % data.real
339 elif data.real == 0.0:
340 data = u'%rj' % data.imag
341 elif data.imag > 0:
342 data = u'%r+%rj' % (data.real, data.imag)
343 else:
344 data = u'%r%rj' % (data.real, data.imag)
345 return self.represent_scalar(u'tag:yaml.org,2002:python/complex', data)
347 def represent_tuple(self, data):
348 return self.represent_sequence(u'tag:yaml.org,2002:python/tuple', data)
350 def represent_name(self, data):
351 name = u'%s.%s' % (data.__module__, data.__name__)
352 return self.represent_scalar(u'tag:yaml.org,2002:python/name:'+name, u'')
354 def represent_module(self, data):
355 return self.represent_scalar(
356 u'tag:yaml.org,2002:python/module:'+data.__name__, u'')
358 def represent_instance(self, data):
359 # For instances of classic classes, we use __getinitargs__ and
360 # __getstate__ to serialize the data.
362 # If data.__getinitargs__ exists, the object must be reconstructed by
363 # calling cls(**args), where args is a tuple returned by
364 # __getinitargs__. Otherwise, the cls.__init__ method should never be
365 # called and the class instance is created by instantiating a trivial
366 # class and assigning to the instance's __class__ variable.
368 # If data.__getstate__ exists, it returns the state of the object.
369 # Otherwise, the state of the object is data.__dict__.
371 # We produce either a !!python/object or !!python/object/new node.
372 # If data.__getinitargs__ does not exist and state is a dictionary, we
373 # produce a !!python/object node . Otherwise we produce a
374 # !!python/object/new node.
376 cls = data.__class__
377 class_name = u'%s.%s' % (cls.__module__, cls.__name__)
378 args = None
379 state = None
380 if hasattr(data, '__getinitargs__'):
381 args = list(data.__getinitargs__())
382 if hasattr(data, '__getstate__'):
383 state = data.__getstate__()
384 else:
385 state = data.__dict__
386 if args is None and isinstance(state, dict):
387 state = state.items()
388 state.sort()
389 return self.represent_mapping(
390 u'tag:yaml.org,2002:python/object:'+class_name, state)
391 if isinstance(state, dict) and not state:
392 return self.represent_sequence(
393 u'tag:yaml.org,2002:python/object/new:'+class_name, args)
394 value = {}
395 if args:
396 value['args'] = args
397 value['state'] = state
398 return self.represent_mapping(
399 u'tag:yaml.org,2002:python/object/new:'+class_name, value)
401 def represent_object(self, data):
402 # We use __reduce__ API to save the data. data.__reduce__ returns
403 # a tuple of length 2-5:
404 # (function, args, state, listitems, dictitems)
406 # For reconstructing, we calls function(*args), then set its state,
407 # listitems, and dictitems if they are not None.
409 # A special case is when function.__name__ == '__newobj__'. In this
410 # case we create the object with args[0].__new__(*args).
412 # Another special case is when __reduce__ returns a string - we don't
413 # support it.
415 # We produce a !!python/object, !!python/object/new or
416 # !!python/object/apply node.
418 cls = type(data)
419 if cls in copy_reg.dispatch_table:
420 reduce = copy_reg.dispatch_table[cls](data)
421 elif hasattr(data, '__reduce_ex__'):
422 reduce = data.__reduce_ex__(2)
423 elif hasattr(data, '__reduce__'):
424 reduce = data.__reduce__()
425 else:
426 raise RepresenterError("cannot represent object: %r" % data)
427 reduce = (list(reduce)+[None]*5)[:5]
428 function, args, state, listitems, dictitems = reduce
429 args = list(args)
430 if state is None:
431 state = {}
432 if listitems is not None:
433 listitems = list(listitems)
434 if dictitems is not None:
435 dictitems = dict(dictitems)
436 if function.__name__ == '__newobj__':
437 function = args[0]
438 args = args[1:]
439 tag = u'tag:yaml.org,2002:python/object/new:'
440 newobj = True
441 else:
442 tag = u'tag:yaml.org,2002:python/object/apply:'
443 newobj = False
444 function_name = u'%s.%s' % (function.__module__, function.__name__)
445 if not args and not listitems and not dictitems \
446 and isinstance(state, dict) and newobj:
447 state = state.items()
448 state.sort()
449 return self.represent_mapping(
450 u'tag:yaml.org,2002:python/object:'+function_name, state)
451 if not listitems and not dictitems \
452 and isinstance(state, dict) and not state:
453 return self.represent_sequence(tag+function_name, args)
454 value = {}
455 if args:
456 value['args'] = args
457 if state or not isinstance(state, dict):
458 value['state'] = state
459 if listitems:
460 value['listitems'] = listitems
461 if dictitems:
462 value['dictitems'] = dictitems
463 return self.represent_mapping(tag+function_name, value)
465 Representer.add_representer(str,
466 Representer.represent_str)
468 Representer.add_representer(unicode,
469 Representer.represent_unicode)
471 Representer.add_representer(long,
472 Representer.represent_long)
474 Representer.add_representer(complex,
475 Representer.represent_complex)
477 Representer.add_representer(tuple,
478 Representer.represent_tuple)
480 Representer.add_representer(type,
481 Representer.represent_name)
483 Representer.add_representer(Representer.classobj_type,
484 Representer.represent_name)
486 Representer.add_representer(Representer.function_type,
487 Representer.represent_name)
489 Representer.add_representer(Representer.builtin_function_type,
490 Representer.represent_name)
492 Representer.add_representer(Representer.module_type,
493 Representer.represent_module)
495 Representer.add_multi_representer(Representer.instance_type,
496 Representer.represent_instance)
498 Representer.add_multi_representer(object,
499 Representer.represent_object)