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

Revision 173, 17.5 KB checked in by xi, 9 years ago (diff)

Revamp the inf/nan handling again.

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