Ticket #5: representer.py

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

Possible patch

Line 
1
2__all__ = ['BaseRepresenter', 'SafeRepresenter', 'Representer',
3    'RepresenterError']
4
5from error import *
6from nodes import *
7
8try:
9    import datetime
10    datetime_available = True
11except ImportError:
12    datetime_available = False
13
14try:
15    set
16except NameError:
17    from sets import Set as set
18
19import sys, copy_reg
20
21class RepresenterError(YAMLError):
22    pass
23
24class 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
186class 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
306SafeRepresenter.add_representer(type(None),
307        SafeRepresenter.represent_none)
308
309SafeRepresenter.add_representer(str,
310        SafeRepresenter.represent_str)
311
312SafeRepresenter.add_representer(unicode,
313        SafeRepresenter.represent_unicode)
314
315SafeRepresenter.add_representer(bool,
316        SafeRepresenter.represent_bool)
317
318SafeRepresenter.add_representer(int,
319        SafeRepresenter.represent_int)
320
321SafeRepresenter.add_representer(long,
322        SafeRepresenter.represent_long)
323
324SafeRepresenter.add_representer(float,
325        SafeRepresenter.represent_float)
326
327SafeRepresenter.add_representer(list,
328        SafeRepresenter.represent_list)
329
330SafeRepresenter.add_representer(tuple,
331        SafeRepresenter.represent_list)
332
333SafeRepresenter.add_representer(dict,
334        SafeRepresenter.represent_dict)
335
336SafeRepresenter.add_representer(set,
337        SafeRepresenter.represent_set)
338
339if datetime_available:
340    SafeRepresenter.add_representer(datetime.date,
341            SafeRepresenter.represent_date)
342    SafeRepresenter.add_representer(datetime.datetime,
343            SafeRepresenter.represent_datetime)
344
345SafeRepresenter.add_representer(None,
346        SafeRepresenter.represent_undefined)
347
348class 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
510Representer.add_representer(str,
511        Representer.represent_str)
512
513Representer.add_representer(unicode,
514        Representer.represent_unicode)
515
516Representer.add_representer(long,
517        Representer.represent_long)
518
519Representer.add_representer(complex,
520        Representer.represent_complex)
521
522Representer.add_representer(tuple,
523        Representer.represent_tuple)
524
525Representer.add_representer(type,
526        Representer.represent_name)
527
528Representer.add_representer(Representer.classobj_type,
529        Representer.represent_name)
530
531Representer.add_representer(Representer.function_type,
532        Representer.represent_name)
533
534Representer.add_representer(Representer.builtin_function_type,
535        Representer.represent_name)
536
537Representer.add_representer(Representer.module_type,
538        Representer.represent_module)
539
540Representer.add_multi_representer(Representer.instance_type,
541        Representer.represent_instance)
542
543Representer.add_multi_representer(object,
544        Representer.represent_object)
545