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

Revision 222, 17.5 KB checked in by xi, 8 years ago (diff)

Subclass all base classes from object.

Hold references to the objects being represented (should fix #22).

The value of a mapping node is represented as a list of pairs (key, value)
now.

Sort dictionary items (fix #23).

Recursive structures are now loaded and dumped correctly, including complex
structures like recursive tuples (fix #5). Thanks Peter Murphy for the patches.
To make it possible, representer functions are allowed to be generators.
In this case, the first generated value is an object. Other values produced
by the representer are ignored.

Make Representer not try to guess !!pairs when a list is represented.
You need to construct a !!pairs node explicitly now.

Do not check for duplicate mapping keys as it didn't work correctly anyway.

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
[222]24class BaseRepresenter(object):
[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 = {}
[222]33        self.object_keeper = []
34        self.alias_key = None
[133]35
[136]36    def represent(self, data):
[147]37        node = self.represent_data(data)
[136]38        self.serialize(node)
[133]39        self.represented_objects = {}
[222]40        self.object_keeper = []
41        self.alias_key = None
[133]42
[139]43    class C: pass
44    c = C()
45    def f(): pass
[222]46    def g(): yield None
[139]47    classobj_type = type(C)
48    instance_type = type(c)
49    function_type = type(f)
[222]50    generator_type = type(g())
[139]51    builtin_function_type = type(abs)
52    module_type = type(sys)
[222]53    del C, c, f, g
[139]54
55    def get_classobj_bases(self, cls):
56        bases = [cls]
57        for base in cls.__bases__:
58            bases.extend(self.get_classobj_bases(base))
59        return bases
60
[147]61    def represent_data(self, data):
[136]62        if self.ignore_aliases(data):
[222]63            self.alias_key = None
[133]64        else:
[222]65            self.alias_key = id(data)
66        if self.alias_key is not None:
67            if self.alias_key in self.represented_objects:
68                node = self.represented_objects[self.alias_key]
69                #if node is None:
70                #    raise RepresenterError("recursive objects are not allowed: %r" % data)
[133]71                return node
[222]72            #self.represented_objects[alias_key] = None
73            self.object_keeper.append(data)
[139]74        data_types = type(data).__mro__
75        if type(data) is self.instance_type:
[143]76            data_types = self.get_classobj_bases(data.__class__)+list(data_types)
[147]77        if data_types[0] in self.yaml_representers:
78            node = self.yaml_representers[data_types[0]](self, data)
[133]79        else:
[147]80            for data_type in data_types:
81                if data_type in self.yaml_multi_representers:
82                    node = self.yaml_multi_representers[data_type](self, data)
83                    break
[133]84            else:
[147]85                if None in self.yaml_multi_representers:
86                    node = self.yaml_multi_representers[None](self, data)
87                elif None in self.yaml_representers:
88                    node = self.yaml_representers[None](self, data)
89                else:
90                    node = ScalarNode(None, unicode(data))
[222]91        #if alias_key is not None:
92        #    self.represented_objects[alias_key] = node
[133]93        return node
94
[136]95    def add_representer(cls, data_type, representer):
[133]96        if not 'yaml_representers' in cls.__dict__:
97            cls.yaml_representers = cls.yaml_representers.copy()
[136]98        cls.yaml_representers[data_type] = representer
[133]99    add_representer = classmethod(add_representer)
100
[147]101    def add_multi_representer(cls, data_type, representer):
102        if not 'yaml_multi_representers' in cls.__dict__:
103            cls.yaml_multi_representers = cls.yaml_multi_representers.copy()
104        cls.yaml_multi_representers[data_type] = representer
105    add_multi_representer = classmethod(add_multi_representer)
106
[133]107    def represent_scalar(self, tag, value, style=None):
[152]108        if style is None:
109            style = self.default_style
[222]110        node = ScalarNode(tag, value, style=style)
111        if self.alias_key is not None:
112            self.represented_objects[self.alias_key] = node
113        return node
[133]114
115    def represent_sequence(self, tag, sequence, flow_style=None):
[222]116        value = []
117        node = SequenceNode(tag, value, flow_style=flow_style)
118        if self.alias_key is not None:
119            self.represented_objects[self.alias_key] = node
[147]120        best_style = True
[133]121        for item in sequence:
[147]122            node_item = self.represent_data(item)
123            if not (isinstance(node_item, ScalarNode) and not node_item.style):
124                best_style = False
[222]125            value.append(node_item)
[147]126        if flow_style is None:
[222]127            if self.default_flow_style is not None:
128                node.flow_style = self.default_flow_style
129            else:
130                node.flow_style = best_style
131        return node
[133]132
133    def represent_mapping(self, tag, mapping, flow_style=None):
[222]134        value = []
135        node = MappingNode(tag, value, flow_style=flow_style)
136        if self.alias_key is not None:
137            self.represented_objects[self.alias_key] = node
[147]138        best_style = True
[222]139        if hasattr(mapping, 'items'):
140            mapping = mapping.items()
141            mapping.sort()
142        for item_key, item_value in mapping:
143            node_key = self.represent_data(item_key)
144            node_value = self.represent_data(item_value)
145            if not (isinstance(node_key, ScalarNode) and not node_key.style):
146                best_style = False
147            if not (isinstance(node_value, ScalarNode) and not node_value.style):
148                best_style = False
149            value.append((node_key, node_value))
[147]150        if flow_style is None:
[222]151            if self.default_flow_style is not None:
152                node.flow_style = self.default_flow_style
153            else:
154                node.flow_style = best_style
155        return node
[133]156
[136]157    def ignore_aliases(self, data):
[133]158        return False
159
[136]160class SafeRepresenter(BaseRepresenter):
[133]161
[136]162    def ignore_aliases(self, data):
163        if data in [None, ()]:
[133]164            return True
[136]165        if isinstance(data, (str, unicode, bool, int, float)):
[133]166            return True
167
[136]168    def represent_none(self, data):
[133]169        return self.represent_scalar(u'tag:yaml.org,2002:null',
170                u'null')
171
[136]172    def represent_str(self, data):
[139]173        tag = None
174        style = None
[133]175        try:
[139]176            data = unicode(data, 'ascii')
177            tag = u'tag:yaml.org,2002:str'
[135]178        except UnicodeDecodeError:
179            try:
[139]180                data = unicode(data, 'utf-8')
181                tag = u'tag:yaml.org,2002:str'
[135]182            except UnicodeDecodeError:
[139]183                data = data.encode('base64')
184                tag = u'tag:yaml.org,2002:binary'
185                style = '|'
186        return self.represent_scalar(tag, data, style=style)
[133]187
[136]188    def represent_unicode(self, data):
189        return self.represent_scalar(u'tag:yaml.org,2002:str', data)
[133]190
[136]191    def represent_bool(self, data):
192        if data:
[133]193            value = u'true'
194        else:
195            value = u'false'
196        return self.represent_scalar(u'tag:yaml.org,2002:bool', value)
197
[136]198    def represent_int(self, data):
199        return self.represent_scalar(u'tag:yaml.org,2002:int', unicode(data))
[133]200
[136]201    def represent_long(self, data):
202        return self.represent_scalar(u'tag:yaml.org,2002:int', unicode(data))
[133]203
[168]204    inf_value = 1e300
205    while repr(inf_value) != repr(inf_value*inf_value):
206        inf_value *= inf_value
[133]207
[136]208    def represent_float(self, data):
[173]209        if data != data or (data == 0.0 and data == 1.0):
210            value = u'.nan'
211        elif data == self.inf_value:
[133]212            value = u'.inf'
[173]213        elif data == -self.inf_value:
[133]214            value = u'-.inf'
215        else:
[173]216            value = unicode(repr(data))
[133]217        return self.represent_scalar(u'tag:yaml.org,2002:float', value)
218
[136]219    def represent_list(self, data):
[222]220        #pairs = (len(data) > 0 and isinstance(data, list))
221        #if pairs:
222        #    for item in data:
223        #        if not isinstance(item, tuple) or len(item) != 2:
224        #            pairs = False
225        #            break
226        #if not pairs:
[136]227            return self.represent_sequence(u'tag:yaml.org,2002:seq', data)
[222]228        #value = []
229        #for item_key, item_value in data:
230        #    value.append(self.represent_mapping(u'tag:yaml.org,2002:map',
231        #        [(item_key, item_value)]))
232        #return SequenceNode(u'tag:yaml.org,2002:pairs', value)
[133]233
[136]234    def represent_dict(self, data):
235        return self.represent_mapping(u'tag:yaml.org,2002:map', data)
[133]236
[136]237    def represent_set(self, data):
[133]238        value = {}
[136]239        for key in data:
[133]240            value[key] = None
241        return self.represent_mapping(u'tag:yaml.org,2002:set', value)
242
[136]243    def represent_date(self, data):
244        value = u'%04d-%02d-%02d' % (data.year, data.month, data.day)
[133]245        return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value)
246
[136]247    def represent_datetime(self, data):
[133]248        value = u'%04d-%02d-%02d %02d:%02d:%02d' \
[136]249                % (data.year, data.month, data.day,
250                    data.hour, data.minute, data.second)
251        if data.microsecond:
252            value += u'.' + unicode(data.microsecond/1000000.0).split(u'.')[1]
253        if data.utcoffset():
254            value += unicode(data.utcoffset())
[133]255        return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value)
256
[136]257    def represent_yaml_object(self, tag, data, cls, flow_style=None):
258        if hasattr(data, '__getstate__'):
259            state = data.__getstate__()
260        else:
261            state = data.__dict__.copy()
[139]262        return self.represent_mapping(tag, state, flow_style=flow_style)
[133]263
[136]264    def represent_undefined(self, data):
265        raise RepresenterError("cannot represent an object: %s" % data)
266
[133]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
[139]291SafeRepresenter.add_representer(tuple,
292        SafeRepresenter.represent_list)
293
[133]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):
[147]310
[139]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)
[133]326
[139]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):
[143]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:
[139]348            data = u'%r+%rj' % (data.real, data.imag)
349        else:
[143]350            data = u'%r%rj' % (data.real, data.imag)
[139]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
[147]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            return self.represent_mapping(
394                    u'tag:yaml.org,2002:python/object:'+class_name, state)
395        if isinstance(state, dict) and not state:
396            return self.represent_sequence(
397                    u'tag:yaml.org,2002:python/object/new:'+class_name, args)
398        value = {}
399        if args:
400            value['args'] = args
401        value['state'] = state
402        return self.represent_mapping(
403                u'tag:yaml.org,2002:python/object/new:'+class_name, value)
404
405    def represent_object(self, data):
406        # We use __reduce__ API to save the data. data.__reduce__ returns
407        # a tuple of length 2-5:
408        #   (function, args, state, listitems, dictitems)
409
410        # For reconstructing, we calls function(*args), then set its state,
411        # listitems, and dictitems if they are not None.
412
413        # A special case is when function.__name__ == '__newobj__'. In this
414        # case we create the object with args[0].__new__(*args).
415
416        # Another special case is when __reduce__ returns a string - we don't
417        # support it.
418
419        # We produce a !!python/object, !!python/object/new or
420        # !!python/object/apply node.
421
422        cls = type(data)
423        if cls in copy_reg.dispatch_table:
[206]424            reduce = copy_reg.dispatch_table[cls](data)
[147]425        elif hasattr(data, '__reduce_ex__'):
426            reduce = data.__reduce_ex__(2)
427        elif hasattr(data, '__reduce__'):
428            reduce = data.__reduce__()
429        else:
430            raise RepresenterError("cannot represent object: %r" % data)
431        reduce = (list(reduce)+[None]*5)[:5]
432        function, args, state, listitems, dictitems = reduce
433        args = list(args)
434        if state is None:
435            state = {}
436        if listitems is not None:
437            listitems = list(listitems)
438        if dictitems is not None:
439            dictitems = dict(dictitems)
440        if function.__name__ == '__newobj__':
441            function = args[0]
442            args = args[1:]
443            tag = u'tag:yaml.org,2002:python/object/new:'
444            newobj = True
445        else:
446            tag = u'tag:yaml.org,2002:python/object/apply:'
447            newobj = False
448        function_name = u'%s.%s' % (function.__module__, function.__name__)
449        if not args and not listitems and not dictitems \
450                and isinstance(state, dict) and newobj:
451            return self.represent_mapping(
452                    u'tag:yaml.org,2002:python/object:'+function_name, state)
453        if not listitems and not dictitems  \
454                and isinstance(state, dict) and not state:
455            return self.represent_sequence(tag+function_name, args)
456        value = {}
457        if args:
458            value['args'] = args
459        if state or not isinstance(state, dict):
460            value['state'] = state
461        if listitems:
462            value['listitems'] = listitems
463        if dictitems:
464            value['dictitems'] = dictitems
465        return self.represent_mapping(tag+function_name, value)
466
[139]467Representer.add_representer(str,
468        Representer.represent_str)
469
470Representer.add_representer(unicode,
471        Representer.represent_unicode)
472
473Representer.add_representer(long,
474        Representer.represent_long)
475
476Representer.add_representer(complex,
477        Representer.represent_complex)
478
479Representer.add_representer(tuple,
480        Representer.represent_tuple)
481
482Representer.add_representer(type,
483        Representer.represent_name)
484
485Representer.add_representer(Representer.classobj_type,
486        Representer.represent_name)
487
488Representer.add_representer(Representer.function_type,
489        Representer.represent_name)
490
491Representer.add_representer(Representer.builtin_function_type,
492        Representer.represent_name)
493
494Representer.add_representer(Representer.module_type,
495        Representer.represent_module)
496
[147]497Representer.add_multi_representer(Representer.instance_type,
498        Representer.represent_instance)
499
500Representer.add_multi_representer(object,
501        Representer.represent_object)
502
Note: See TracBrowser for help on using the repository browser.