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

Revision 168, 17.7 KB checked in by xi, 8 years ago (diff)

Dynamically determine the inf and nan values. Should fix #14.
Thanks to Scott Daniels for the report and the 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)
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        for item in sequence:
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:
114            flow_style = self.default_flow_style
115        if flow_style is None:
116            flow_style = best_style
117        return SequenceNode(tag, value, flow_style=flow_style)
118
119    def represent_mapping(self, tag, mapping, flow_style=None):
120        best_style = True
121        if hasattr(mapping, 'keys'):
122            value = {}
123            for item_key in mapping.keys():
124                item_value = mapping[item_key]
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
132        else:
133            value = []
134            for item_key, item_value in mapping:
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:
143            flow_style = self.default_flow_style
144        if flow_style is None:
145            flow_style = best_style
146        return MappingNode(tag, value, flow_style=flow_style)
147
148    def ignore_aliases(self, data):
149        return False
150
151class SafeRepresenter(BaseRepresenter):
152
153    def ignore_aliases(self, data):
154        if data in [None, ()]:
155            return True
156        if isinstance(data, (str, unicode, bool, int, float)):
157            return True
158
159    def represent_none(self, data):
160        return self.represent_scalar(u'tag:yaml.org,2002:null',
161                u'null')
162
163    def represent_str(self, data):
164        tag = None
165        style = None
166        try:
167            data = unicode(data, 'ascii')
168            tag = u'tag:yaml.org,2002:str'
169        except UnicodeDecodeError:
170            try:
171                data = unicode(data, 'utf-8')
172                tag = u'tag:yaml.org,2002:str'
173            except UnicodeDecodeError:
174                data = data.encode('base64')
175                tag = u'tag:yaml.org,2002:binary'
176                style = '|'
177        return self.represent_scalar(tag, data, style=style)
178
179    def represent_unicode(self, data):
180        return self.represent_scalar(u'tag:yaml.org,2002:str', data)
181
182    def represent_bool(self, data):
183        if data:
184            value = u'true'
185        else:
186            value = u'false'
187        return self.represent_scalar(u'tag:yaml.org,2002:bool', value)
188
189    def represent_int(self, data):
190        return self.represent_scalar(u'tag:yaml.org,2002:int', unicode(data))
191
192    def represent_long(self, data):
193        return self.represent_scalar(u'tag:yaml.org,2002:int', unicode(data))
194
195    inf_value = 1e300
196    while repr(inf_value) != repr(inf_value*inf_value):
197        inf_value *= inf_value
198    nan_value = inf_value/inf_value
199
200    repr_pos_inf = repr(inf_value)
201    repr_neg_inf = repr(-inf_value)
202    repr_nan = repr(inf_value/inf_value)
203
204    def represent_float(self, data):
205        repr_data = repr(data)
206        if repr_data == self.repr_pos_inf:
207            value = u'.inf'
208        elif repr_data == self.repr_neg_inf:
209            value = u'-.inf'
210        elif repr_data == self.repr_nan:
211            value = u'.nan'
212        else:
213            value = unicode(repr_data)
214        return self.represent_scalar(u'tag:yaml.org,2002:float', value)
215
216    def represent_list(self, data):
217        pairs = (len(data) > 0 and isinstance(data, list))
218        if pairs:
219            for item in data:
220                if not isinstance(item, tuple) or len(item) != 2:
221                    pairs = False
222                    break
223        if not pairs:
224            return self.represent_sequence(u'tag:yaml.org,2002:seq', data)
225        value = []
226        for item_key, item_value in data:
227            value.append(self.represent_mapping(u'tag:yaml.org,2002:map',
228                [(item_key, item_value)]))
229        return SequenceNode(u'tag:yaml.org,2002:pairs', value)
230
231    def represent_dict(self, data):
232        return self.represent_mapping(u'tag:yaml.org,2002:map', data)
233
234    def represent_set(self, data):
235        value = {}
236        for key in data:
237            value[key] = None
238        return self.represent_mapping(u'tag:yaml.org,2002:set', value)
239
240    def represent_date(self, data):
241        value = u'%04d-%02d-%02d' % (data.year, data.month, data.day)
242        return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value)
243
244    def represent_datetime(self, data):
245        value = u'%04d-%02d-%02d %02d:%02d:%02d' \
246                % (data.year, data.month, data.day,
247                    data.hour, data.minute, data.second)
248        if data.microsecond:
249            value += u'.' + unicode(data.microsecond/1000000.0).split(u'.')[1]
250        if data.utcoffset():
251            value += unicode(data.utcoffset())
252        return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value)
253
254    def represent_yaml_object(self, tag, data, cls, flow_style=None):
255        if hasattr(data, '__getstate__'):
256            state = data.__getstate__()
257        else:
258            state = data.__dict__.copy()
259        if isinstance(state, dict):
260            state = state.items()
261            state.sort()
262        return self.represent_mapping(tag, state, flow_style=flow_style)
263
264    def represent_undefined(self, data):
265        raise RepresenterError("cannot represent an object: %s" % data)
266
267SafeRepresenter.add_representer(type(None),
268        SafeRepresenter.represent_none)
269
270SafeRepresenter.add_representer(str,
271        SafeRepresenter.represent_str)
272
273SafeRepresenter.add_representer(unicode,
274        SafeRepresenter.represent_unicode)
275
276SafeRepresenter.add_representer(bool,
277        SafeRepresenter.represent_bool)
278
279SafeRepresenter.add_representer(int,
280        SafeRepresenter.represent_int)
281
282SafeRepresenter.add_representer(long,
283        SafeRepresenter.represent_long)
284
285SafeRepresenter.add_representer(float,
286        SafeRepresenter.represent_float)
287
288SafeRepresenter.add_representer(list,
289        SafeRepresenter.represent_list)
290
291SafeRepresenter.add_representer(tuple,
292        SafeRepresenter.represent_list)
293
294SafeRepresenter.add_representer(dict,
295        SafeRepresenter.represent_dict)
296
297SafeRepresenter.add_representer(set,
298        SafeRepresenter.represent_set)
299
300if datetime_available:
301    SafeRepresenter.add_representer(datetime.date,
302            SafeRepresenter.represent_date)
303    SafeRepresenter.add_representer(datetime.datetime,
304            SafeRepresenter.represent_datetime)
305
306SafeRepresenter.add_representer(None,
307        SafeRepresenter.represent_undefined)
308
309class Representer(SafeRepresenter):
310
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)
326
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)
335
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))
341
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)
352
353    def represent_tuple(self, data):
354        return self.represent_sequence(u'tag:yaml.org,2002:python/tuple', data)
355
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'')
359
360    def represent_module(self, data):
361        return self.represent_scalar(
362                u'tag:yaml.org,2002:python/module:'+data.__name__, u'')
363
364    def represent_instance(self, data):
365        # For instances of classic classes, we use __getinitargs__ and
366        # __getstate__ to serialize the data.
367
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.
373
374        # If data.__getstate__ exists, it returns the state of the object.
375        # Otherwise, the state of the object is data.__dict__.
376
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.
381
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            state = state.items()
394            state.sort()
395            return self.represent_mapping(
396                    u'tag:yaml.org,2002:python/object:'+class_name, state)
397        if isinstance(state, dict) and not state:
398            return self.represent_sequence(
399                    u'tag:yaml.org,2002:python/object/new:'+class_name, args)
400        value = {}
401        if args:
402            value['args'] = args
403        value['state'] = state
404        return self.represent_mapping(
405                u'tag:yaml.org,2002:python/object/new:'+class_name, value)
406
407    def represent_object(self, data):
408        # We use __reduce__ API to save the data. data.__reduce__ returns
409        # a tuple of length 2-5:
410        #   (function, args, state, listitems, dictitems)
411
412        # For reconstructing, we calls function(*args), then set its state,
413        # listitems, and dictitems if they are not None.
414
415        # A special case is when function.__name__ == '__newobj__'. In this
416        # case we create the object with args[0].__new__(*args).
417
418        # Another special case is when __reduce__ returns a string - we don't
419        # support it.
420
421        # We produce a !!python/object, !!python/object/new or
422        # !!python/object/apply node.
423
424        cls = type(data)
425        if cls in copy_reg.dispatch_table:
426            reduce = copy_reg.dispatch_table[cls]
427        elif hasattr(data, '__reduce_ex__'):
428            reduce = data.__reduce_ex__(2)
429        elif hasattr(data, '__reduce__'):
430            reduce = data.__reduce__()
431        else:
432            raise RepresenterError("cannot represent object: %r" % data)
433        reduce = (list(reduce)+[None]*5)[:5]
434        function, args, state, listitems, dictitems = reduce
435        args = list(args)
436        if state is None:
437            state = {}
438        if listitems is not None:
439            listitems = list(listitems)
440        if dictitems is not None:
441            dictitems = dict(dictitems)
442        if function.__name__ == '__newobj__':
443            function = args[0]
444            args = args[1:]
445            tag = u'tag:yaml.org,2002:python/object/new:'
446            newobj = True
447        else:
448            tag = u'tag:yaml.org,2002:python/object/apply:'
449            newobj = False
450        function_name = u'%s.%s' % (function.__module__, function.__name__)
451        if not args and not listitems and not dictitems \
452                and isinstance(state, dict) and newobj:
453            state = state.items()
454            state.sort()
455            return self.represent_mapping(
456                    u'tag:yaml.org,2002:python/object:'+function_name, state)
457        if not listitems and not dictitems  \
458                and isinstance(state, dict) and not state:
459            return self.represent_sequence(tag+function_name, args)
460        value = {}
461        if args:
462            value['args'] = args
463        if state or not isinstance(state, dict):
464            value['state'] = state
465        if listitems:
466            value['listitems'] = listitems
467        if dictitems:
468            value['dictitems'] = dictitems
469        return self.represent_mapping(tag+function_name, value)
470
471Representer.add_representer(str,
472        Representer.represent_str)
473
474Representer.add_representer(unicode,
475        Representer.represent_unicode)
476
477Representer.add_representer(long,
478        Representer.represent_long)
479
480Representer.add_representer(complex,
481        Representer.represent_complex)
482
483Representer.add_representer(tuple,
484        Representer.represent_tuple)
485
486Representer.add_representer(type,
487        Representer.represent_name)
488
489Representer.add_representer(Representer.classobj_type,
490        Representer.represent_name)
491
492Representer.add_representer(Representer.function_type,
493        Representer.represent_name)
494
495Representer.add_representer(Representer.builtin_function_type,
496        Representer.represent_name)
497
498Representer.add_representer(Representer.module_type,
499        Representer.represent_module)
500
501Representer.add_multi_representer(Representer.instance_type,
502        Representer.represent_instance)
503
504Representer.add_multi_representer(object,
505        Representer.represent_object)
506
Note: See TracBrowser for help on using the repository browser.