Ticket #5: representer.py

File representer.py, 19.0 kB (added by Peter Murphy (pkmurphy at postmaster dot co dot uk), 4 years ago)

Possible patch

Line 
1
2 __all__ = ['BaseRepresenter', 'SafeRepresenter', 'Representer',
3     'RepresenterError']
4
5 from error import *
6 from nodes import *
7
8 try:
9     import datetime
10     datetime_available = True
11 except ImportError:
12     datetime_available = False
13
14 try:
15     set
16 except NameError:
17     from sets import Set as set
18
19 import sys, copy_reg
20
21 class RepresenterError(YAMLError):
22     pass
23
24 class BaseRepresenter:
25
26     yaml_representers = {}
27     yaml_multi_representers = {}
28
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
34     def represent(self, data):
35         node = self.represent_data(data)
36         self.serialize(node)
37         self.represented_objects = {}
38
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
48
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
54
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) #Call AAA1
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
87
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)
93
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)
99
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)
104
105     def represent_sequence(self, tag, sequence, flow_style=None):
106         best_style = True
107         value = []
108        
109 # PKM We construct the sequence node here, rather than at the end of the function.
110
111         ourSeqNode = SequenceNode(tag, value, flow_style);
112         alias_key = id(sequence);
113         self.represented_objects[alias_key] = ourSeqNode;
114        
115 # End PKM.       
116        
117         for item in sequence:
118             node_item = self.represent_data(item)
119             if not (isinstance(node_item, ScalarNode) and not node_item.style): #Call AAA3
120                 best_style = False
121             value.append(self.represent_data(item))
122         if flow_style is None:
123             flow_style = self.default_flow_style
124         if flow_style is None:
125             flow_style = best_style
126            
127 # PKM Now we reset the flow styles attribute.
128
129         ourSeqNode.style = flow_style;
130 #  PKM: replace  return SequenceNode(tag, value, flow_style=flow_style)
131         return ourSeqNode;
132
133     def represent_mapping(self, tag, mapping, flow_style=None):
134         best_style = True
135         if hasattr(mapping, 'keys'):
136             value = {}
137            
138 # PKM We construct the pairs node here, rather than at the end of the function.
139
140             ourMapNode = MappingNode(tag, value, flow_style);
141             alias_key = id(mapping);
142             self.represented_objects[alias_key] = ourMapNode;
143        
144 # End PKM.       
145            
146             for item_key in mapping.keys():
147                 item_value = mapping[item_key]
148                 node_key = self.represent_data(item_key)
149                 node_value = self.represent_data(item_value)
150                 if not (isinstance(node_key, ScalarNode) and not node_key.style):
151                     best_style = False
152                 if not (isinstance(node_value, ScalarNode) and not node_value.style):
153                     best_style = False
154                 value[node_key] = node_value
155         else:
156             value = []
157            
158 # PKM We construct the pairs node here, rather than at the end of the function.
159
160             ourMapNode = MappingNode(tag, value, flow_style);
161             alias_key = id(mapping);
162             self.represented_objects[alias_key] = ourMapNode;
163        
164 # End PKM.       
165            
166             for item_key, item_value in mapping:
167                 node_key = self.represent_data(item_key)
168                 node_value = self.represent_data(item_value)
169                 if not (isinstance(node_key, ScalarNode) and not node_key.style):
170                     best_style = False
171                 if not (isinstance(node_value, ScalarNode) and not node_value.style):
172                     best_style = False
173                 value.append((node_key, node_value))
174         if flow_style is None:
175             flow_style = self.default_flow_style
176         if flow_style is None:
177             flow_style = best_style
178            
179 #PKM comment out:        return MappingNode(tag, value, flow_style=flow_style)
180         ourMapNode.style = flow_style;
181         return ourMapNode;
182
183     def ignore_aliases(self, data):
184         return False
185
186 class SafeRepresenter(BaseRepresenter):
187
188     def ignore_aliases(self, data):
189         if data in [None, ()]:
190             return True
191         if isinstance(data, (str, unicode, bool, int, float)):
192             return True
193
194     def represent_none(self, data):
195         return self.represent_scalar(u'tag:yaml.org,2002:null',
196                 u'null')
197
198     def represent_str(self, data):
199         tag = None
200         style = None
201         try:
202             data = unicode(data, 'ascii')
203             tag = u'tag:yaml.org,2002:str'
204         except UnicodeDecodeError:
205             try:
206                 data = unicode(data, 'utf-8')
207                 tag = u'tag:yaml.org,2002:str'
208             except UnicodeDecodeError:
209                 data = data.encode('base64')
210                 tag = u'tag:yaml.org,2002:binary'
211                 style = '|'
212         return self.represent_scalar(tag, data, style=style)
213
214     def represent_unicode(self, data):
215         return self.represent_scalar(u'tag:yaml.org,2002:str', data)
216
217     def represent_bool(self, data):
218         if data:
219             value = u'true'
220         else:
221             value = u'false'
222         return self.represent_scalar(u'tag:yaml.org,2002:bool', value)
223
224     def represent_int(self, data):
225         return self.represent_scalar(u'tag:yaml.org,2002:int', unicode(data))
226
227     def represent_long(self, data):
228         return self.represent_scalar(u'tag:yaml.org,2002:int', unicode(data))
229
230     inf_value = 1e300
231     while repr(inf_value) != repr(inf_value*inf_value):
232         inf_value *= inf_value
233
234     def represent_float(self, data):
235         if data != data or (data == 0.0 and data == 1.0):
236             value = u'.nan'
237         elif data == self.inf_value:
238             value = u'.inf'
239         elif data == -self.inf_value:
240             value = u'-.inf'
241         else:
242             value = unicode(repr(data))
243         return self.represent_scalar(u'tag:yaml.org,2002:float', value)
244
245     def represent_list(self, data):
246         pairs = (len(data) > 0 and isinstance(data, list))
247         if pairs:
248             for item in data:
249                 if not isinstance(item, tuple) or len(item) != 2:
250                     pairs = False
251                     break
252         if not pairs:
253             return self.represent_sequence(u'tag:yaml.org,2002:seq', data) #Call AAA2
254         value = []
255        
256 # PKM We construct the pairs node here, rather than at the end of the function.
257
258         ourPairsNode = SequenceNode(u'tag:yaml.org,2002:pairs', value);
259         alias_key = id(data);
260         self.represented_objects[alias_key] = ourPairsNode;
261        
262 # End PKM.       
263        
264         for item_key, item_value in data:
265             value.append(self.represent_mapping(u'tag:yaml.org,2002:map',
266                 [(item_key, item_value)]))
267 # PKM comment out        return SequenceNode(u'tag:yaml.org,2002:pairs', value)
268         return ourPairsNode;
269
270     def represent_dict(self, data):
271         return self.represent_mapping(u'tag:yaml.org,2002:map', data)
272
273     def represent_set(self, data):
274         value = {}
275         for key in data:
276             value[key] = None
277         return self.represent_mapping(u'tag:yaml.org,2002:set', value)
278
279     def represent_date(self, data):
280         value = u'%04d-%02d-%02d' % (data.year, data.month, data.day)
281         return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value)
282
283     def represent_datetime(self, data):
284         value = u'%04d-%02d-%02d %02d:%02d:%02d' \
285                 % (data.year, data.month, data.day,
286                     data.hour, data.minute, data.second)
287         if data.microsecond:
288             value += u'.' + unicode(data.microsecond/1000000.0).split(u'.')[1]
289         if data.utcoffset():
290             value += unicode(data.utcoffset())
291         return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value)
292
293     def represent_yaml_object(self, tag, data, cls, flow_style=None):
294         if hasattr(data, '__getstate__'):
295             state = data.__getstate__()
296         else:
297             state = data.__dict__.copy()
298         if isinstance(state, dict):
299             state = state.items()
300             state.sort()
301         return self.represent_mapping(tag, state, flow_style=flow_style)
302
303     def represent_undefined(self, data):
304         raise RepresenterError("cannot represent an object: %s" % data)
305
306 SafeRepresenter.add_representer(type(None),
307         SafeRepresenter.represent_none)
308
309 SafeRepresenter.add_representer(str,
310         SafeRepresenter.represent_str)
311
312 SafeRepresenter.add_representer(unicode,
313         SafeRepresenter.represent_unicode)
314
315 SafeRepresenter.add_representer(bool,
316         SafeRepresenter.represent_bool)
317
318 SafeRepresenter.add_representer(int,
319         SafeRepresenter.represent_int)
320
321 SafeRepresenter.add_representer(long,
322         SafeRepresenter.represent_long)
323
324 SafeRepresenter.add_representer(float,
325         SafeRepresenter.represent_float)
326
327 SafeRepresenter.add_representer(list,
328         SafeRepresenter.represent_list)
329
330 SafeRepresenter.add_representer(tuple,
331         SafeRepresenter.represent_list)
332
333 SafeRepresenter.add_representer(dict,
334         SafeRepresenter.represent_dict)
335
336 SafeRepresenter.add_representer(set,
337         SafeRepresenter.represent_set)
338
339 if datetime_available:
340     SafeRepresenter.add_representer(datetime.date,
341             SafeRepresenter.represent_date)
342     SafeRepresenter.add_representer(datetime.datetime,
343             SafeRepresenter.represent_datetime)
344
345 SafeRepresenter.add_representer(None,
346         SafeRepresenter.represent_undefined)
347
348 class Representer(SafeRepresenter):
349
350     def represent_str(self, data):
351         tag = None
352         style = None
353         try:
354             data = unicode(data, 'ascii')
355             tag = u'tag:yaml.org,2002:str'
356         except UnicodeDecodeError:
357             try:
358                 data = unicode(data, 'utf-8')
359                 tag = u'tag:yaml.org,2002:python/str'
360             except UnicodeDecodeError:
361                 data = data.encode('base64')
362                 tag = u'tag:yaml.org,2002:binary'
363                 style = '|'
364         return self.represent_scalar(tag, data, style=style)
365
366     def represent_unicode(self, data):
367         tag = None
368         try:
369             data.encode('ascii')
370             tag = u'tag:yaml.org,2002:python/unicode'
371         except UnicodeEncodeError:
372             tag = u'tag:yaml.org,2002:str'
373         return self.represent_scalar(tag, data)
374
375     def represent_long(self, data):
376         tag = u'tag:yaml.org,2002:int'
377         if int(data) is not data:
378             tag = u'tag:yaml.org,2002:python/long'
379         return self.represent_scalar(tag, unicode(data))
380
381     def represent_complex(self, data):
382         if data.imag == 0.0:
383             data = u'%r' % data.real
384         elif data.real == 0.0:
385             data = u'%rj' % data.imag
386         elif data.imag > 0:
387             data = u'%r+%rj' % (data.real, data.imag)
388         else:
389             data = u'%r%rj' % (data.real, data.imag)
390         return self.represent_scalar(u'tag:yaml.org,2002:python/complex', data)
391
392     def represent_tuple(self, data):
393         return self.represent_sequence(u'tag:yaml.org,2002:python/tuple', data)
394
395     def represent_name(self, data):
396         name = u'%s.%s' % (data.__module__, data.__name__)
397         return self.represent_scalar(u'tag:yaml.org,2002:python/name:'+name, u'')
398
399     def represent_module(self, data):
400         return self.represent_scalar(
401                 u'tag:yaml.org,2002:python/module:'+data.__name__, u'')
402
403     def represent_instance(self, data):
404         # For instances of classic classes, we use __getinitargs__ and
405         # __getstate__ to serialize the data.
406
407         # If data.__getinitargs__ exists, the object must be reconstructed by
408         # calling cls(**args), where args is a tuple returned by
409         # __getinitargs__. Otherwise, the cls.__init__ method should never be
410         # called and the class instance is created by instantiating a trivial
411         # class and assigning to the instance's __class__ variable.
412
413         # If data.__getstate__ exists, it returns the state of the object.
414         # Otherwise, the state of the object is data.__dict__.
415
416         # We produce either a !!python/object or !!python/object/new node.
417         # If data.__getinitargs__ does not exist and state is a dictionary, we
418         # produce a !!python/object node . Otherwise we produce a
419         # !!python/object/new node.
420
421         cls = data.__class__
422         class_name = u'%s.%s' % (cls.__module__, cls.__name__)
423         args = None
424         state = None
425         if hasattr(data, '__getinitargs__'):
426             args = list(data.__getinitargs__())
427         if hasattr(data, '__getstate__'):
428             state = data.__getstate__()
429         else:
430             state = data.__dict__
431         if args is None and isinstance(state, dict):
432             state = state.items()
433             state.sort()
434             return self.represent_mapping(
435                     u'tag:yaml.org,2002:python/object:'+class_name, state)
436         if isinstance(state, dict) and not state:
437             return self.represent_sequence(
438                     u'tag:yaml.org,2002:python/object/new:'+class_name, args)
439         value = {}
440         if args:
441             value['args'] = args
442         value['state'] = state
443         return self.represent_mapping(
444                 u'tag:yaml.org,2002:python/object/new:'+class_name, value)
445
446     def represent_object(self, data):
447         # We use __reduce__ API to save the data. data.__reduce__ returns
448         # a tuple of length 2-5:
449         #   (function, args, state, listitems, dictitems)
450
451         # For reconstructing, we calls function(*args), then set its state,
452         # listitems, and dictitems if they are not None.
453
454         # A special case is when function.__name__ == '__newobj__'. In this
455         # case we create the object with args[0].__new__(*args).
456
457         # Another special case is when __reduce__ returns a string - we don't
458         # support it.
459
460         # We produce a !!python/object, !!python/object/new or
461         # !!python/object/apply node.
462
463         cls = type(data)
464         if cls in copy_reg.dispatch_table:
465             reduce = copy_reg.dispatch_table[cls]
466         elif hasattr(data, '__reduce_ex__'):
467             reduce = data.__reduce_ex__(2)
468         elif hasattr(data, '__reduce__'):
469             reduce = data.__reduce__()
470         else:
471             raise RepresenterError("cannot represent object: %r" % data)
472         reduce = (list(reduce)+[None]*5)[:5]
473         function, args, state, listitems, dictitems = reduce
474         args = list(args)
475         if state is None:
476             state = {}
477         if listitems is not None:
478             listitems = list(listitems)
479         if dictitems is not None:
480             dictitems = dict(dictitems)
481         if function.__name__ == '__newobj__':
482             function = args[0]
483             args = args[1:]
484             tag = u'tag:yaml.org,2002:python/object/new:'
485             newobj = True
486         else:
487             tag = u'tag:yaml.org,2002:python/object/apply:'
488             newobj = False
489         function_name = u'%s.%s' % (function.__module__, function.__name__)
490         if not args and not listitems and not dictitems \
491                 and isinstance(state, dict) and newobj:
492             state = state.items()
493             state.sort()
494             return self.represent_mapping(
495                     u'tag:yaml.org,2002:python/object:'+function_name, state)
496         if not listitems and not dictitems  \
497                 and isinstance(state, dict) and not state:
498             return self.represent_sequence(tag+function_name, args)
499         value = {}
500         if args:
501             value['args'] = args
502         if state or not isinstance(state, dict):
503             value['state'] = state
504         if listitems:
505             value['listitems'] = listitems
506         if dictitems:
507             value['dictitems'] = dictitems
508         return self.represent_mapping(tag+function_name, value)
509
510 Representer.add_representer(str,
511         Representer.represent_str)
512
513 Representer.add_representer(unicode,
514         Representer.represent_unicode)
515
516 Representer.add_representer(long,
517         Representer.represent_long)
518
519 Representer.add_representer(complex,
520         Representer.represent_complex)
521
522 Representer.add_representer(tuple,
523         Representer.represent_tuple)
524
525 Representer.add_representer(type,
526         Representer.represent_name)
527
528 Representer.add_representer(Representer.classobj_type,
529         Representer.represent_name)
530
531 Representer.add_representer(Representer.function_type,
532         Representer.represent_name)
533
534 Representer.add_representer(Representer.builtin_function_type,
535         Representer.represent_name)
536
537 Representer.add_representer(Representer.module_type,
538         Representer.represent_module)
539
540 Representer.add_multi_representer(Representer.instance_type,
541         Representer.represent_instance)
542
543 Representer.add_multi_representer(object,
544         Representer.represent_object)
545