Add a way to override default style chosen by Representer: fix #9
[pyyaml/python3.git] / lib / yaml / representer.py
blobcb37169d8bce76e29ed38d71cded44a06815d3f7
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 repr_pos_inf = repr(1e300000)
196 repr_neg_inf = repr(-1e300000)
197 repr_nan = repr(1e300000/1e300000)
199 def represent_float(self, data):
200 repr_data = repr(data)
201 if repr_data == self.repr_pos_inf:
202 value = u'.inf'
203 elif repr_data == self.repr_neg_inf:
204 value = u'-.inf'
205 elif repr_data == self.repr_nan:
206 value = u'.nan'
207 else:
208 value = unicode(repr_data)
209 return self.represent_scalar(u'tag:yaml.org,2002:float', value)
211 def represent_list(self, data):
212 pairs = (len(data) > 0 and isinstance(data, list))
213 if pairs:
214 for item in data:
215 if not isinstance(item, tuple) or len(item) != 2:
216 pairs = False
217 break
218 if not pairs:
219 return self.represent_sequence(u'tag:yaml.org,2002:seq', data)
220 value = []
221 for item_key, item_value in data:
222 value.append(self.represent_mapping(u'tag:yaml.org,2002:map',
223 [(item_key, item_value)]))
224 return SequenceNode(u'tag:yaml.org,2002:pairs', value)
226 def represent_dict(self, data):
227 return self.represent_mapping(u'tag:yaml.org,2002:map', data)
229 def represent_set(self, data):
230 value = {}
231 for key in data:
232 value[key] = None
233 return self.represent_mapping(u'tag:yaml.org,2002:set', value)
235 def represent_date(self, data):
236 value = u'%04d-%02d-%02d' % (data.year, data.month, data.day)
237 return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value)
239 def represent_datetime(self, data):
240 value = u'%04d-%02d-%02d %02d:%02d:%02d' \
241 % (data.year, data.month, data.day,
242 data.hour, data.minute, data.second)
243 if data.microsecond:
244 value += u'.' + unicode(data.microsecond/1000000.0).split(u'.')[1]
245 if data.utcoffset():
246 value += unicode(data.utcoffset())
247 return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value)
249 def represent_yaml_object(self, tag, data, cls, flow_style=None):
250 if hasattr(data, '__getstate__'):
251 state = data.__getstate__()
252 else:
253 state = data.__dict__.copy()
254 if isinstance(state, dict):
255 state = state.items()
256 state.sort()
257 return self.represent_mapping(tag, state, flow_style=flow_style)
259 def represent_undefined(self, data):
260 raise RepresenterError("cannot represent an object: %s" % data)
262 SafeRepresenter.add_representer(type(None),
263 SafeRepresenter.represent_none)
265 SafeRepresenter.add_representer(str,
266 SafeRepresenter.represent_str)
268 SafeRepresenter.add_representer(unicode,
269 SafeRepresenter.represent_unicode)
271 SafeRepresenter.add_representer(bool,
272 SafeRepresenter.represent_bool)
274 SafeRepresenter.add_representer(int,
275 SafeRepresenter.represent_int)
277 SafeRepresenter.add_representer(long,
278 SafeRepresenter.represent_long)
280 SafeRepresenter.add_representer(float,
281 SafeRepresenter.represent_float)
283 SafeRepresenter.add_representer(list,
284 SafeRepresenter.represent_list)
286 SafeRepresenter.add_representer(tuple,
287 SafeRepresenter.represent_list)
289 SafeRepresenter.add_representer(dict,
290 SafeRepresenter.represent_dict)
292 SafeRepresenter.add_representer(set,
293 SafeRepresenter.represent_set)
295 if datetime_available:
296 SafeRepresenter.add_representer(datetime.date,
297 SafeRepresenter.represent_date)
298 SafeRepresenter.add_representer(datetime.datetime,
299 SafeRepresenter.represent_datetime)
301 SafeRepresenter.add_representer(None,
302 SafeRepresenter.represent_undefined)
304 class Representer(SafeRepresenter):
306 def represent_str(self, data):
307 tag = None
308 style = None
309 try:
310 data = unicode(data, 'ascii')
311 tag = u'tag:yaml.org,2002:str'
312 except UnicodeDecodeError:
313 try:
314 data = unicode(data, 'utf-8')
315 tag = u'tag:yaml.org,2002:python/str'
316 except UnicodeDecodeError:
317 data = data.encode('base64')
318 tag = u'tag:yaml.org,2002:binary'
319 style = '|'
320 return self.represent_scalar(tag, data, style=style)
322 def represent_unicode(self, data):
323 tag = None
324 try:
325 data.encode('ascii')
326 tag = u'tag:yaml.org,2002:python/unicode'
327 except UnicodeEncodeError:
328 tag = u'tag:yaml.org,2002:str'
329 return self.represent_scalar(tag, data)
331 def represent_long(self, data):
332 tag = u'tag:yaml.org,2002:int'
333 if int(data) is not data:
334 tag = u'tag:yaml.org,2002:python/long'
335 return self.represent_scalar(tag, unicode(data))
337 def represent_complex(self, data):
338 if data.imag == 0.0:
339 data = u'%r' % data.real
340 elif data.real == 0.0:
341 data = u'%rj' % data.imag
342 elif data.imag > 0:
343 data = u'%r+%rj' % (data.real, data.imag)
344 else:
345 data = u'%r%rj' % (data.real, data.imag)
346 return self.represent_scalar(u'tag:yaml.org,2002:python/complex', data)
348 def represent_tuple(self, data):
349 return self.represent_sequence(u'tag:yaml.org,2002:python/tuple', data)
351 def represent_name(self, data):
352 name = u'%s.%s' % (data.__module__, data.__name__)
353 return self.represent_scalar(u'tag:yaml.org,2002:python/name:'+name, u'')
355 def represent_module(self, data):
356 return self.represent_scalar(
357 u'tag:yaml.org,2002:python/module:'+data.__name__, u'')
359 def represent_instance(self, data):
360 # For instances of classic classes, we use __getinitargs__ and
361 # __getstate__ to serialize the data.
363 # If data.__getinitargs__ exists, the object must be reconstructed by
364 # calling cls(**args), where args is a tuple returned by
365 # __getinitargs__. Otherwise, the cls.__init__ method should never be
366 # called and the class instance is created by instantiating a trivial
367 # class and assigning to the instance's __class__ variable.
369 # If data.__getstate__ exists, it returns the state of the object.
370 # Otherwise, the state of the object is data.__dict__.
372 # We produce either a !!python/object or !!python/object/new node.
373 # If data.__getinitargs__ does not exist and state is a dictionary, we
374 # produce a !!python/object node . Otherwise we produce a
375 # !!python/object/new node.
377 cls = data.__class__
378 class_name = u'%s.%s' % (cls.__module__, cls.__name__)
379 args = None
380 state = None
381 if hasattr(data, '__getinitargs__'):
382 args = list(data.__getinitargs__())
383 if hasattr(data, '__getstate__'):
384 state = data.__getstate__()
385 else:
386 state = data.__dict__
387 if args is None and isinstance(state, dict):
388 state = state.items()
389 state.sort()
390 return self.represent_mapping(
391 u'tag:yaml.org,2002:python/object:'+class_name, state)
392 if isinstance(state, dict) and not state:
393 return self.represent_sequence(
394 u'tag:yaml.org,2002:python/object/new:'+class_name, args)
395 value = {}
396 if args:
397 value['args'] = args
398 value['state'] = state
399 return self.represent_mapping(
400 u'tag:yaml.org,2002:python/object/new:'+class_name, value)
402 def represent_object(self, data):
403 # We use __reduce__ API to save the data. data.__reduce__ returns
404 # a tuple of length 2-5:
405 # (function, args, state, listitems, dictitems)
407 # For reconstructing, we calls function(*args), then set its state,
408 # listitems, and dictitems if they are not None.
410 # A special case is when function.__name__ == '__newobj__'. In this
411 # case we create the object with args[0].__new__(*args).
413 # Another special case is when __reduce__ returns a string - we don't
414 # support it.
416 # We produce a !!python/object, !!python/object/new or
417 # !!python/object/apply node.
419 cls = type(data)
420 if cls in copy_reg.dispatch_table:
421 reduce = copy_reg.dispatch_table[cls]
422 elif hasattr(data, '__reduce_ex__'):
423 reduce = data.__reduce_ex__(2)
424 elif hasattr(data, '__reduce__'):
425 reduce = data.__reduce__()
426 else:
427 raise RepresenterError("cannot represent object: %r" % data)
428 reduce = (list(reduce)+[None]*5)[:5]
429 function, args, state, listitems, dictitems = reduce
430 args = list(args)
431 if state is None:
432 state = {}
433 if listitems is not None:
434 listitems = list(listitems)
435 if dictitems is not None:
436 dictitems = dict(dictitems)
437 if function.__name__ == '__newobj__':
438 function = args[0]
439 args = args[1:]
440 tag = u'tag:yaml.org,2002:python/object/new:'
441 newobj = True
442 else:
443 tag = u'tag:yaml.org,2002:python/object/apply:'
444 newobj = False
445 function_name = u'%s.%s' % (function.__module__, function.__name__)
446 if not args and not listitems and not dictitems \
447 and isinstance(state, dict) and newobj:
448 state = state.items()
449 state.sort()
450 return self.represent_mapping(
451 u'tag:yaml.org,2002:python/object:'+function_name, state)
452 if not listitems and not dictitems \
453 and isinstance(state, dict) and not state:
454 return self.represent_sequence(tag+function_name, args)
455 value = {}
456 if args:
457 value['args'] = args
458 if state or not isinstance(state, dict):
459 value['state'] = state
460 if listitems:
461 value['listitems'] = listitems
462 if dictitems:
463 value['dictitems'] = dictitems
464 return self.represent_mapping(tag+function_name, value)
466 Representer.add_representer(str,
467 Representer.represent_str)
469 Representer.add_representer(unicode,
470 Representer.represent_unicode)
472 Representer.add_representer(long,
473 Representer.represent_long)
475 Representer.add_representer(complex,
476 Representer.represent_complex)
478 Representer.add_representer(tuple,
479 Representer.represent_tuple)
481 Representer.add_representer(type,
482 Representer.represent_name)
484 Representer.add_representer(Representer.classobj_type,
485 Representer.represent_name)
487 Representer.add_representer(Representer.function_type,
488 Representer.represent_name)
490 Representer.add_representer(Representer.builtin_function_type,
491 Representer.represent_name)
493 Representer.add_representer(Representer.module_type,
494 Representer.represent_module)
496 Representer.add_multi_representer(Representer.instance_type,
497 Representer.represent_instance)
499 Representer.add_multi_representer(object,
500 Representer.represent_object)