source: pyyaml/trunk/lib/yaml/representer.py @ 152

Revision 152, 17.6 KB checked in by xi, 8 years ago (diff)

Add a way to override default style chosen by Representer: fix #9

RevLine 
[133]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
[147]19import sys, copy_reg
[139]20
[133]21class RepresenterError(YAMLError):
22    pass
23
[136]24class BaseRepresenter:
[133]25
[136]26    yaml_representers = {}
[147]27    yaml_multi_representers = {}
[133]28
[152]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
[133]32        self.represented_objects = {}
33
[136]34    def represent(self, data):
[147]35        node = self.represent_data(data)
[136]36        self.serialize(node)
[133]37        self.represented_objects = {}
38
[139]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
[147]55    def represent_data(self, data):
[136]56        if self.ignore_aliases(data):
[133]57            alias_key = None
58        else:
[136]59            alias_key = id(data)
[133]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:
[136]64                    raise RepresenterError("recursive objects are not allowed: %r" % data)
[133]65                return node
66            self.represented_objects[alias_key] = None
[139]67        data_types = type(data).__mro__
68        if type(data) is self.instance_type:
[143]69            data_types = self.get_classobj_bases(data.__class__)+list(data_types)
[147]70        if data_types[0] in self.yaml_representers:
71            node = self.yaml_representers[data_types[0]](self, data)
[133]72        else:
[147]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
[133]77            else:
[147]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))
[133]84        if alias_key is not None:
85            self.represented_objects[alias_key] = node
86        return node
87
[136]88    def add_representer(cls, data_type, representer):
[133]89        if not 'yaml_representers' in cls.__dict__:
90            cls.yaml_representers = cls.yaml_representers.copy()
[136]91        cls.yaml_representers[data_type] = representer
[133]92    add_representer = classmethod(add_representer)
93
[147]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
[133]100    def represent_scalar(self, tag, value, style=None):
[152]101        if style is None:
102            style = self.default_style
[136]103        return ScalarNode(tag, value, style=style)
[133]104
105    def represent_sequence(self, tag, sequence, flow_style=None):
[147]106        best_style = True
[133]107        value = []
108        for item in sequence:
[147]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:
[152]114            flow_style = self.default_flow_style
115        if flow_style is None:
[147]116            flow_style = best_style
[133]117        return SequenceNode(tag, value, flow_style=flow_style)
118
119    def represent_mapping(self, tag, mapping, flow_style=None):
[147]120        best_style = True
[133]121        if hasattr(mapping, 'keys'):
[139]122            value = {}
[133]123            for item_key in mapping.keys():
124                item_value = mapping[item_key]
[147]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
[133]132        else:
[139]133            value = []
[133]134            for item_key, item_value in mapping:
[147]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:
[152]143            flow_style = self.default_flow_style
144        if flow_style is None:
[147]145            flow_style = best_style
[133]146        return MappingNode(tag, value, flow_style=flow_style)
147
[136]148    def ignore_aliases(self, data):
[133]149        return False
150
[136]151class SafeRepresenter(BaseRepresenter):
[133]152
[136]153    def ignore_aliases(self, data):
154        if data in [None, ()]:
[133]155            return True
[136]156        if isinstance(data, (str, unicode, bool, int, float)):
[133]157            return True
158
[136]159    def represent_none(self, data):
[133]160        return self.represent_scalar(u'tag:yaml.org,2002:null',
161                u'null')
162
[136]163    def represent_str(self, data):
[139]164        tag = None
165        style = None
[133]166        try:
[139]167            data = unicode(data, 'ascii')
168            tag = u'tag:yaml.org,2002:str'
[135]169        except UnicodeDecodeError:
170            try:
[139]171                data = unicode(data, 'utf-8')
172                tag = u'tag:yaml.org,2002:str'
[135]173            except UnicodeDecodeError:
[139]174                data = data.encode('base64')
175                tag = u'tag:yaml.org,2002:binary'
176                style = '|'
177        return self.represent_scalar(tag, data, style=style)
[133]178
[136]179    def represent_unicode(self, data):
180        return self.represent_scalar(u'tag:yaml.org,2002:str', data)
[133]181
[136]182    def represent_bool(self, data):
183        if data:
[133]184            value = u'true'
185        else:
186            value = u'false'
187        return self.represent_scalar(u'tag:yaml.org,2002:bool', value)
188
[136]189    def represent_int(self, data):
190        return self.represent_scalar(u'tag:yaml.org,2002:int', unicode(data))
[133]191
[136]192    def represent_long(self, data):
193        return self.represent_scalar(u'tag:yaml.org,2002:int', unicode(data))
[133]194
[150]195    repr_pos_inf = repr(1e300000)
[151]196    repr_neg_inf = repr(-1e300000)
[150]197    repr_nan = repr(1e300000/1e300000)
[133]198
[136]199    def represent_float(self, data):
[150]200        repr_data = repr(data)
201        if repr_data == self.repr_pos_inf:
[133]202            value = u'.inf'
[150]203        elif repr_data == self.repr_neg_inf:
[133]204            value = u'-.inf'
[150]205        elif repr_data == self.repr_nan:
[133]206            value = u'.nan'
207        else:
[150]208            value = unicode(repr_data)
[133]209        return self.represent_scalar(u'tag:yaml.org,2002:float', value)
210
[136]211    def represent_list(self, data):
[139]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
[133]218        if not pairs:
[136]219            return self.represent_sequence(u'tag:yaml.org,2002:seq', data)
[133]220        value = []
[136]221        for item_key, item_value in data:
[133]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)
225
[136]226    def represent_dict(self, data):
227        return self.represent_mapping(u'tag:yaml.org,2002:map', data)
[133]228
[136]229    def represent_set(self, data):
[133]230        value = {}
[136]231        for key in data:
[133]232            value[key] = None
233        return self.represent_mapping(u'tag:yaml.org,2002:set', value)
234
[136]235    def represent_date(self, data):
236        value = u'%04d-%02d-%02d' % (data.year, data.month, data.day)
[133]237        return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value)
238
[136]239    def represent_datetime(self, data):
[133]240        value = u'%04d-%02d-%02d %02d:%02d:%02d' \
[136]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())
[133]247        return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value)
248
[136]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()
[148]254        if isinstance(state, dict):
255            state = state.items()
256            state.sort()
[139]257        return self.represent_mapping(tag, state, flow_style=flow_style)
[133]258
[136]259    def represent_undefined(self, data):
260        raise RepresenterError("cannot represent an object: %s" % data)
261
[133]262SafeRepresenter.add_representer(type(None),
263        SafeRepresenter.represent_none)
264
265SafeRepresenter.add_representer(str,
266        SafeRepresenter.represent_str)
267
268SafeRepresenter.add_representer(unicode,
269        SafeRepresenter.represent_unicode)
270
271SafeRepresenter.add_representer(bool,
272        SafeRepresenter.represent_bool)
273
274SafeRepresenter.add_representer(int,
275        SafeRepresenter.represent_int)
276
277SafeRepresenter.add_representer(long,
278        SafeRepresenter.represent_long)
279
280SafeRepresenter.add_representer(float,
281        SafeRepresenter.represent_float)
282
283SafeRepresenter.add_representer(list,
284        SafeRepresenter.represent_list)
285
[139]286SafeRepresenter.add_representer(tuple,
287        SafeRepresenter.represent_list)
288
[133]289SafeRepresenter.add_representer(dict,
290        SafeRepresenter.represent_dict)
291
292SafeRepresenter.add_representer(set,
293        SafeRepresenter.represent_set)
294
295if datetime_available:
296    SafeRepresenter.add_representer(datetime.date,
297            SafeRepresenter.represent_date)
298    SafeRepresenter.add_representer(datetime.datetime,
299            SafeRepresenter.represent_datetime)
300
301SafeRepresenter.add_representer(None,
302        SafeRepresenter.represent_undefined)
303
304class Representer(SafeRepresenter):
[147]305
[139]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)
[133]321
[139]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)
330
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))
336
337    def represent_complex(self, data):
[143]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:
[139]343            data = u'%r+%rj' % (data.real, data.imag)
344        else:
[143]345            data = u'%r%rj' % (data.real, data.imag)
[139]346        return self.represent_scalar(u'tag:yaml.org,2002:python/complex', data)
347
348    def represent_tuple(self, data):
349        return self.represent_sequence(u'tag:yaml.org,2002:python/tuple', data)
350
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'')
354
355    def represent_module(self, data):
356        return self.represent_scalar(
357                u'tag:yaml.org,2002:python/module:'+data.__name__, u'')
358
[147]359    def represent_instance(self, data):
360        # For instances of classic classes, we use __getinitargs__ and
361        # __getstate__ to serialize the data.
362
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.
368
369        # If data.__getstate__ exists, it returns the state of the object.
370        # Otherwise, the state of the object is data.__dict__.
371
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.
376
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):
[148]388            state = state.items()
389            state.sort()
[147]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)
401
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)
406
407        # For reconstructing, we calls function(*args), then set its state,
408        # listitems, and dictitems if they are not None.
409
410        # A special case is when function.__name__ == '__newobj__'. In this
411        # case we create the object with args[0].__new__(*args).
412
413        # Another special case is when __reduce__ returns a string - we don't
414        # support it.
415
416        # We produce a !!python/object, !!python/object/new or
417        # !!python/object/apply node.
418
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:
[148]448            state = state.items()
449            state.sort()
[147]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)
465
[139]466Representer.add_representer(str,
467        Representer.represent_str)
468
469Representer.add_representer(unicode,
470        Representer.represent_unicode)
471
472Representer.add_representer(long,
473        Representer.represent_long)
474
475Representer.add_representer(complex,
476        Representer.represent_complex)
477
478Representer.add_representer(tuple,
479        Representer.represent_tuple)
480
481Representer.add_representer(type,
482        Representer.represent_name)
483
484Representer.add_representer(Representer.classobj_type,
485        Representer.represent_name)
486
487Representer.add_representer(Representer.function_type,
488        Representer.represent_name)
489
490Representer.add_representer(Representer.builtin_function_type,
491        Representer.represent_name)
492
493Representer.add_representer(Representer.module_type,
494        Representer.represent_module)
495
[147]496Representer.add_multi_representer(Representer.instance_type,
497        Representer.represent_instance)
498
499Representer.add_multi_representer(object,
500        Representer.represent_object)
501
Note: See TracBrowser for help on using the repository browser.