source: trunk/packages/pyyaml/lib/yaml/representer.py @ 898

Last change on this file since 898 was 898, checked in by hartmans, 16 years ago

Add pyyaml and libyaml packages
backported from lenny.
There is discussion about how these should go in the repository; these are added in this form
in order to make forward progress.

File size: 17.3 KB
Line 
1
2__all__ = ['BaseRepresenter', 'SafeRepresenter', 'Representer',
3    'RepresenterError']
4
5from error import *
6from nodes import *
7
8import datetime
9
10try:
11    set
12except NameError:
13    from sets import Set as set
14
15import sys, copy_reg, types
16
17class RepresenterError(YAMLError):
18    pass
19
20class BaseRepresenter(object):
21
22    yaml_representers = {}
23    yaml_multi_representers = {}
24
25    def __init__(self, default_style=None, default_flow_style=None):
26        self.default_style = default_style
27        self.default_flow_style = default_flow_style
28        self.represented_objects = {}
29        self.object_keeper = []
30        self.alias_key = None
31
32    def represent(self, data):
33        node = self.represent_data(data)
34        self.serialize(node)
35        self.represented_objects = {}
36        self.object_keeper = []
37        self.alias_key = None
38
39    def get_classobj_bases(self, cls):
40        bases = [cls]
41        for base in cls.__bases__:
42            bases.extend(self.get_classobj_bases(base))
43        return bases
44
45    def represent_data(self, data):
46        if self.ignore_aliases(data):
47            self.alias_key = None
48        else:
49            self.alias_key = id(data)
50        if self.alias_key is not None:
51            if self.alias_key in self.represented_objects:
52                node = self.represented_objects[self.alias_key]
53                #if node is None:
54                #    raise RepresenterError("recursive objects are not allowed: %r" % data)
55                return node
56            #self.represented_objects[alias_key] = None
57            self.object_keeper.append(data)
58        data_types = type(data).__mro__
59        if type(data) is types.InstanceType:
60            data_types = self.get_classobj_bases(data.__class__)+list(data_types)
61        if data_types[0] in self.yaml_representers:
62            node = self.yaml_representers[data_types[0]](self, data)
63        else:
64            for data_type in data_types:
65                if data_type in self.yaml_multi_representers:
66                    node = self.yaml_multi_representers[data_type](self, data)
67                    break
68            else:
69                if None in self.yaml_multi_representers:
70                    node = self.yaml_multi_representers[None](self, data)
71                elif None in self.yaml_representers:
72                    node = self.yaml_representers[None](self, data)
73                else:
74                    node = ScalarNode(None, unicode(data))
75        #if alias_key is not None:
76        #    self.represented_objects[alias_key] = node
77        return node
78
79    def add_representer(cls, data_type, representer):
80        if not 'yaml_representers' in cls.__dict__:
81            cls.yaml_representers = cls.yaml_representers.copy()
82        cls.yaml_representers[data_type] = representer
83    add_representer = classmethod(add_representer)
84
85    def add_multi_representer(cls, data_type, representer):
86        if not 'yaml_multi_representers' in cls.__dict__:
87            cls.yaml_multi_representers = cls.yaml_multi_representers.copy()
88        cls.yaml_multi_representers[data_type] = representer
89    add_multi_representer = classmethod(add_multi_representer)
90
91    def represent_scalar(self, tag, value, style=None):
92        if style is None:
93            style = self.default_style
94        node = ScalarNode(tag, value, style=style)
95        if self.alias_key is not None:
96            self.represented_objects[self.alias_key] = node
97        return node
98
99    def represent_sequence(self, tag, sequence, flow_style=None):
100        value = []
101        node = SequenceNode(tag, value, flow_style=flow_style)
102        if self.alias_key is not None:
103            self.represented_objects[self.alias_key] = node
104        best_style = True
105        for item in sequence:
106            node_item = self.represent_data(item)
107            if not (isinstance(node_item, ScalarNode) and not node_item.style):
108                best_style = False
109            value.append(node_item)
110        if flow_style is None:
111            if self.default_flow_style is not None:
112                node.flow_style = self.default_flow_style
113            else:
114                node.flow_style = best_style
115        return node
116
117    def represent_mapping(self, tag, mapping, flow_style=None):
118        value = []
119        node = MappingNode(tag, value, flow_style=flow_style)
120        if self.alias_key is not None:
121            self.represented_objects[self.alias_key] = node
122        best_style = True
123        if hasattr(mapping, 'items'):
124            mapping = mapping.items()
125            mapping.sort()
126        for item_key, item_value in mapping:
127            node_key = self.represent_data(item_key)
128            node_value = self.represent_data(item_value)
129            if not (isinstance(node_key, ScalarNode) and not node_key.style):
130                best_style = False
131            if not (isinstance(node_value, ScalarNode) and not node_value.style):
132                best_style = False
133            value.append((node_key, node_value))
134        if flow_style is None:
135            if self.default_flow_style is not None:
136                node.flow_style = self.default_flow_style
137            else:
138                node.flow_style = best_style
139        return node
140
141    def ignore_aliases(self, data):
142        return False
143
144class SafeRepresenter(BaseRepresenter):
145
146    def ignore_aliases(self, data):
147        if data in [None, ()]:
148            return True
149        if isinstance(data, (str, unicode, bool, int, float)):
150            return True
151
152    def represent_none(self, data):
153        return self.represent_scalar(u'tag:yaml.org,2002:null',
154                u'null')
155
156    def represent_str(self, data):
157        tag = None
158        style = None
159        try:
160            data = unicode(data, 'ascii')
161            tag = u'tag:yaml.org,2002:str'
162        except UnicodeDecodeError:
163            try:
164                data = unicode(data, 'utf-8')
165                tag = u'tag:yaml.org,2002:str'
166            except UnicodeDecodeError:
167                data = data.encode('base64')
168                tag = u'tag:yaml.org,2002:binary'
169                style = '|'
170        return self.represent_scalar(tag, data, style=style)
171
172    def represent_unicode(self, data):
173        return self.represent_scalar(u'tag:yaml.org,2002:str', data)
174
175    def represent_bool(self, data):
176        if data:
177            value = u'true'
178        else:
179            value = u'false'
180        return self.represent_scalar(u'tag:yaml.org,2002:bool', value)
181
182    def represent_int(self, data):
183        return self.represent_scalar(u'tag:yaml.org,2002:int', unicode(data))
184
185    def represent_long(self, data):
186        return self.represent_scalar(u'tag:yaml.org,2002:int', unicode(data))
187
188    inf_value = 1e300
189    while repr(inf_value) != repr(inf_value*inf_value):
190        inf_value *= inf_value
191
192    def represent_float(self, data):
193        if data != data or (data == 0.0 and data == 1.0):
194            value = u'.nan'
195        elif data == self.inf_value:
196            value = u'.inf'
197        elif data == -self.inf_value:
198            value = u'-.inf'
199        else:
200            value = unicode(repr(data)).lower()
201            # Note that in some cases `repr(data)` represents a float number
202            # without the decimal parts.  For instance:
203            #   >>> repr(1e17)
204            #   '1e17'
205            # Unfortunately, this is not a valid float representation according
206            # to the definition of the `!!float` tag.  We fix this by adding
207            # '.0' before the 'e' symbol.
208            if u'.' not in value and u'e' in value:
209                value = value.replace(u'e', u'.0e', 1)
210        return self.represent_scalar(u'tag:yaml.org,2002:float', value)
211
212    def represent_list(self, data):
213        #pairs = (len(data) > 0 and isinstance(data, list))
214        #if pairs:
215        #    for item in data:
216        #        if not isinstance(item, tuple) or len(item) != 2:
217        #            pairs = False
218        #            break
219        #if not pairs:
220            return self.represent_sequence(u'tag:yaml.org,2002:seq', data)
221        #value = []
222        #for item_key, item_value in data:
223        #    value.append(self.represent_mapping(u'tag:yaml.org,2002:map',
224        #        [(item_key, item_value)]))
225        #return SequenceNode(u'tag:yaml.org,2002:pairs', value)
226
227    def represent_dict(self, data):
228        return self.represent_mapping(u'tag:yaml.org,2002:map', data)
229
230    def represent_set(self, data):
231        value = {}
232        for key in data:
233            value[key] = None
234        return self.represent_mapping(u'tag:yaml.org,2002:set', value)
235
236    def represent_date(self, data):
237        value = unicode(data.isoformat())
238        return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value)
239
240    def represent_datetime(self, data):
241        value = unicode(data.isoformat(' '))
242        return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value)
243
244    def represent_yaml_object(self, tag, data, cls, flow_style=None):
245        if hasattr(data, '__getstate__'):
246            state = data.__getstate__()
247        else:
248            state = data.__dict__.copy()
249        return self.represent_mapping(tag, state, flow_style=flow_style)
250
251    def represent_undefined(self, data):
252        raise RepresenterError("cannot represent an object: %s" % data)
253
254SafeRepresenter.add_representer(type(None),
255        SafeRepresenter.represent_none)
256
257SafeRepresenter.add_representer(str,
258        SafeRepresenter.represent_str)
259
260SafeRepresenter.add_representer(unicode,
261        SafeRepresenter.represent_unicode)
262
263SafeRepresenter.add_representer(bool,
264        SafeRepresenter.represent_bool)
265
266SafeRepresenter.add_representer(int,
267        SafeRepresenter.represent_int)
268
269SafeRepresenter.add_representer(long,
270        SafeRepresenter.represent_long)
271
272SafeRepresenter.add_representer(float,
273        SafeRepresenter.represent_float)
274
275SafeRepresenter.add_representer(list,
276        SafeRepresenter.represent_list)
277
278SafeRepresenter.add_representer(tuple,
279        SafeRepresenter.represent_list)
280
281SafeRepresenter.add_representer(dict,
282        SafeRepresenter.represent_dict)
283
284SafeRepresenter.add_representer(set,
285        SafeRepresenter.represent_set)
286
287SafeRepresenter.add_representer(datetime.date,
288        SafeRepresenter.represent_date)
289SafeRepresenter.add_representer(datetime.datetime,
290        SafeRepresenter.represent_datetime)
291
292SafeRepresenter.add_representer(None,
293        SafeRepresenter.represent_undefined)
294
295class Representer(SafeRepresenter):
296
297    def represent_str(self, data):
298        tag = None
299        style = None
300        try:
301            data = unicode(data, 'ascii')
302            tag = u'tag:yaml.org,2002:str'
303        except UnicodeDecodeError:
304            try:
305                data = unicode(data, 'utf-8')
306                tag = u'tag:yaml.org,2002:python/str'
307            except UnicodeDecodeError:
308                data = data.encode('base64')
309                tag = u'tag:yaml.org,2002:binary'
310                style = '|'
311        return self.represent_scalar(tag, data, style=style)
312
313    def represent_unicode(self, data):
314        tag = None
315        try:
316            data.encode('ascii')
317            tag = u'tag:yaml.org,2002:python/unicode'
318        except UnicodeEncodeError:
319            tag = u'tag:yaml.org,2002:str'
320        return self.represent_scalar(tag, data)
321
322    def represent_long(self, data):
323        tag = u'tag:yaml.org,2002:int'
324        if int(data) is not data:
325            tag = u'tag:yaml.org,2002:python/long'
326        return self.represent_scalar(tag, unicode(data))
327
328    def represent_complex(self, data):
329        if data.imag == 0.0:
330            data = u'%r' % data.real
331        elif data.real == 0.0:
332            data = u'%rj' % data.imag
333        elif data.imag > 0:
334            data = u'%r+%rj' % (data.real, data.imag)
335        else:
336            data = u'%r%rj' % (data.real, data.imag)
337        return self.represent_scalar(u'tag:yaml.org,2002:python/complex', data)
338
339    def represent_tuple(self, data):
340        return self.represent_sequence(u'tag:yaml.org,2002:python/tuple', data)
341
342    def represent_name(self, data):
343        name = u'%s.%s' % (data.__module__, data.__name__)
344        return self.represent_scalar(u'tag:yaml.org,2002:python/name:'+name, u'')
345
346    def represent_module(self, data):
347        return self.represent_scalar(
348                u'tag:yaml.org,2002:python/module:'+data.__name__, u'')
349
350    def represent_instance(self, data):
351        # For instances of classic classes, we use __getinitargs__ and
352        # __getstate__ to serialize the data.
353
354        # If data.__getinitargs__ exists, the object must be reconstructed by
355        # calling cls(**args), where args is a tuple returned by
356        # __getinitargs__. Otherwise, the cls.__init__ method should never be
357        # called and the class instance is created by instantiating a trivial
358        # class and assigning to the instance's __class__ variable.
359
360        # If data.__getstate__ exists, it returns the state of the object.
361        # Otherwise, the state of the object is data.__dict__.
362
363        # We produce either a !!python/object or !!python/object/new node.
364        # If data.__getinitargs__ does not exist and state is a dictionary, we
365        # produce a !!python/object node . Otherwise we produce a
366        # !!python/object/new node.
367
368        cls = data.__class__
369        class_name = u'%s.%s' % (cls.__module__, cls.__name__)
370        args = None
371        state = None
372        if hasattr(data, '__getinitargs__'):
373            args = list(data.__getinitargs__())
374        if hasattr(data, '__getstate__'):
375            state = data.__getstate__()
376        else:
377            state = data.__dict__
378        if args is None and isinstance(state, dict):
379            return self.represent_mapping(
380                    u'tag:yaml.org,2002:python/object:'+class_name, state)
381        if isinstance(state, dict) and not state:
382            return self.represent_sequence(
383                    u'tag:yaml.org,2002:python/object/new:'+class_name, args)
384        value = {}
385        if args:
386            value['args'] = args
387        value['state'] = state
388        return self.represent_mapping(
389                u'tag:yaml.org,2002:python/object/new:'+class_name, value)
390
391    def represent_object(self, data):
392        # We use __reduce__ API to save the data. data.__reduce__ returns
393        # a tuple of length 2-5:
394        #   (function, args, state, listitems, dictitems)
395
396        # For reconstructing, we calls function(*args), then set its state,
397        # listitems, and dictitems if they are not None.
398
399        # A special case is when function.__name__ == '__newobj__'. In this
400        # case we create the object with args[0].__new__(*args).
401
402        # Another special case is when __reduce__ returns a string - we don't
403        # support it.
404
405        # We produce a !!python/object, !!python/object/new or
406        # !!python/object/apply node.
407
408        cls = type(data)
409        if cls in copy_reg.dispatch_table:
410            reduce = copy_reg.dispatch_table[cls](data)
411        elif hasattr(data, '__reduce_ex__'):
412            reduce = data.__reduce_ex__(2)
413        elif hasattr(data, '__reduce__'):
414            reduce = data.__reduce__()
415        else:
416            raise RepresenterError("cannot represent object: %r" % data)
417        reduce = (list(reduce)+[None]*5)[:5]
418        function, args, state, listitems, dictitems = reduce
419        args = list(args)
420        if state is None:
421            state = {}
422        if listitems is not None:
423            listitems = list(listitems)
424        if dictitems is not None:
425            dictitems = dict(dictitems)
426        if function.__name__ == '__newobj__':
427            function = args[0]
428            args = args[1:]
429            tag = u'tag:yaml.org,2002:python/object/new:'
430            newobj = True
431        else:
432            tag = u'tag:yaml.org,2002:python/object/apply:'
433            newobj = False
434        function_name = u'%s.%s' % (function.__module__, function.__name__)
435        if not args and not listitems and not dictitems \
436                and isinstance(state, dict) and newobj:
437            return self.represent_mapping(
438                    u'tag:yaml.org,2002:python/object:'+function_name, state)
439        if not listitems and not dictitems  \
440                and isinstance(state, dict) and not state:
441            return self.represent_sequence(tag+function_name, args)
442        value = {}
443        if args:
444            value['args'] = args
445        if state or not isinstance(state, dict):
446            value['state'] = state
447        if listitems:
448            value['listitems'] = listitems
449        if dictitems:
450            value['dictitems'] = dictitems
451        return self.represent_mapping(tag+function_name, value)
452
453Representer.add_representer(str,
454        Representer.represent_str)
455
456Representer.add_representer(unicode,
457        Representer.represent_unicode)
458
459Representer.add_representer(long,
460        Representer.represent_long)
461
462Representer.add_representer(complex,
463        Representer.represent_complex)
464
465Representer.add_representer(tuple,
466        Representer.represent_tuple)
467
468Representer.add_representer(type,
469        Representer.represent_name)
470
471Representer.add_representer(types.ClassType,
472        Representer.represent_name)
473
474Representer.add_representer(types.FunctionType,
475        Representer.represent_name)
476
477Representer.add_representer(types.BuiltinFunctionType,
478        Representer.represent_name)
479
480Representer.add_representer(types.ModuleType,
481        Representer.represent_module)
482
483Representer.add_multi_representer(types.InstanceType,
484        Representer.represent_instance)
485
486Representer.add_multi_representer(object,
487        Representer.represent_object)
488
Note: See TracBrowser for help on using the repository browser.