source: pyyaml/tags/3.10/lib/yaml/representer.py @ 381

Revision 381, 17.2 KB checked in by xi, 3 years ago (diff)

Dropped support for Python 2.3 and 2.4.

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