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