source: pysyck/trunk/lib/syck/dumpers.py @ 207

Revision 207, 11.4 KB checked in by xi, 8 years ago (diff)

Port represent_object from PyYAML: fix Numeric.array dumping.

Line 
1"""
2syck.dumpers is a high-level wrapper for the Syck YAML emitter.
3Do not use it directly, use the module 'syck' instead.
4"""
5
6import _syck
7
8try:
9    import cStringIO as StringIO
10except ImportError:
11    import StringIO
12
13import copy_reg
14
15__all__ = ['GenericDumper', 'Dumper',
16    'emit', 'dump', 'emit_documents', 'dump_documents']
17
18class GenericDumper(_syck.Emitter):
19    """
20    GenericDumper dumps native Python objects into YAML documents.
21    """
22
23    def dump(self, object):
24        """Dumps the given Python object as a YAML document."""
25        self.emit(self._convert(object, {}))
26
27    def _convert(self, object, object_to_node):
28        if id(object) in object_to_node and self.allow_aliases(object):
29            return object_to_node[id(object)][1]
30        node = self.represent(object)
31        object_to_node[id(object)] = object, node
32        if node.kind == 'seq':
33            for index in range(len(node.value)):
34                item = node.value[index]
35                node.value[index] = self._convert(item, object_to_node)
36        elif node.kind == 'map':
37            if isinstance(node.value, dict):
38                for key in node.value.keys():
39                    value = node.value[key]
40                    del node.value[key]
41                    node.value[self._convert(key, object_to_node)] =    \
42                            self._convert(value, object_to_node)
43            elif isinstance(node.value, list):
44                for index in range(len(node.value)):
45                    key, value = node.value[index]
46                    node.value[index] = (self._convert(key, object_to_node),
47                            self._convert(value, object_to_node))
48#        # Workaround against a Syck bug:
49#        if node.kind == 'scalar' and node.style not in ['1quote', '2quote'] \
50#                and node.value and node.value[-1] in [' ', '\t']:
51#            node.style = '2quote'
52        return node
53
54    def represent(self, object):
55        """Represents the given Python object as a 'Node'."""
56        if isinstance(object, dict):
57            return _syck.Map(object.copy(), tag="tag:yaml.org,2002:map")
58        elif isinstance(object, list):
59            return _syck.Seq(object[:], tag="tag:yaml.org,2002:seq")
60        else:
61            return _syck.Scalar(str(object), tag="tag:yaml.org,2002:str")
62
63    def allow_aliases(self, object):
64        """Checks whether the given object can be aliased."""
65        return True
66
67class Dumper(GenericDumper):
68    """
69    Dumper dumps native Python objects into YAML documents.
70    """
71
72    INF = 1e300000
73    inf_value = repr(INF)
74    neginf_value = repr(-INF)
75    nan_value = repr(INF/INF)
76
77    def find_representer(self, object):
78        """
79        For the given object, find a method that can represent it as a 'Node'
80        object.
81
82        If the type of the object has the form 'package.module.type',
83        find_representer() returns the method 'represent_package_module_type'.
84        If this method does not exist, it checks the base types.
85        """
86        for object_type in type(object).__mro__:
87            if object_type.__module__ == '__builtin__':
88                name = object_type.__name__
89            else:
90                name = '%s.%s' % (object_type.__module__, object_type.__name__)
91            method = 'represent_' + name.replace('.', '_')
92            if hasattr(self, method):
93                return getattr(self, method)
94
95    def represent(self, object):
96        """Represents the given Python object as a 'Node'."""
97        representer = self.find_representer(object)
98        if representer:
99            return representer(object)
100        else:
101            return super(Dumper, self).represent(object)
102
103    def represent_object(self, object):
104        return _syck.Scalar(repr(object), tag="tag:yaml.org,2002:str")
105
106    def represent_NoneType(self, object):
107        return _syck.Scalar('~', tag="tag:yaml.org,2002:null")
108
109    def represent_bool(self, object):
110        return _syck.Scalar(repr(object), tag="tag:yaml.org,2002:bool")
111
112    def represent_str(self, object):
113        try:
114            return _syck.Scalar(object.encode('ascii'), tag="tag:yaml.org,2002:str")
115        except UnicodeDecodeError:
116            try:
117                return _syck.Scalar(unicode(object, 'utf-8').encode('utf-8'),
118                        tag="tag:python.yaml.org,2002:str")
119            except UnicodeDecodeError:
120                return _syck.Scalar(object.encode('base64'),
121                        tag="tag:yaml.org,2002:binary")
122
123    def represent_unicode(self, object):
124        try:
125            return _syck.Scalar(object.encode('ascii'), tag="tag:python.yaml.org,2002:unicode")
126        except UnicodeEncodeError:
127            return _syck.Scalar(object.encode('utf-8'), tag="tag:yaml.org,2002:str")
128
129    def represent_list(self, object):
130        return _syck.Seq(object[:], tag="tag:yaml.org,2002:seq")
131
132    def represent_dict(self, object):
133        return _syck.Map(object.copy(), tag="tag:yaml.org,2002:map")
134
135    def represent_int(self, object):
136        return _syck.Scalar(repr(object), tag="tag:yaml.org,2002:int")
137
138    def represent_float(self, object):
139        value = repr(object)
140        if value == self.inf_value:
141            value = '.inf'
142        elif value == self.neginf_value:
143            value = '-.inf'
144        elif value == self.nan_value:
145            value = '.nan'
146        return _syck.Scalar(value, tag="tag:yaml.org,2002:float")
147
148    def represent_complex(self, object):
149        if object.real != 0.0:
150            value = '%s+%sj' % (repr(object.real), repr(object.imag))
151        else:
152            value = '%sj' % repr(object.imag)
153        return _syck.Scalar(value, tag="tag:python.yaml.org,2002:complex")
154
155    def represent_sets_Set(self, object):
156        return _syck.Seq(list(object), tag="tag:yaml.org,2002:set")
157    represent_set = represent_sets_Set
158
159    def represent_datetime_datetime(self, object):
160        return _syck.Scalar(object.isoformat(), tag="tag:yaml.org,2002:timestamp")
161
162    def represent_long(self, object):
163        return _syck.Scalar(repr(object), tag="tag:python.yaml.org,2002:long")
164
165    def represent_tuple(self, object):
166        return _syck.Seq(list(object), tag="tag:python.yaml.org,2002:tuple")
167
168    def represent_type(self, object):
169        name = '%s.%s' % (object.__module__, object.__name__)
170        return _syck.Scalar('', tag="tag:python.yaml.org,2002:name:"+name)
171    represent_classobj = represent_type
172    represent_class = represent_type
173    # TODO: Python 2.2 does not provide the module name of a function
174    represent_function = represent_type
175    represent_builtin_function_or_method = represent_type
176
177    def represent_module(self, object):
178        return _syck.Scalar('', tag="tag:python.yaml.org,2002:module:"+object.__name__)
179
180    def represent_instance(self, object):
181        cls = object.__class__
182        class_name = '%s.%s' % (cls.__module__, cls.__name__)
183        args = ()
184        state = {}
185        if hasattr(object, '__getinitargs__'):
186            args = object.__getinitargs__()
187        if hasattr(object, '__getstate__'):
188            state = object.__getstate__()
189        elif not hasattr(object, '__getinitargs__'):
190            state = object.__dict__.copy()
191        if not args and isinstance(state, dict):
192            return _syck.Map(state.copy(),
193                    tag="tag:python.yaml.org,2002:object:"+class_name)
194        value = {}
195        if args:
196            value['args'] = list(args)
197        if state or not isinstance(state, dict):
198            value['state'] = state
199        return _syck.Map(value,
200                tag="tag:python.yaml.org,2002:new:"+class_name)
201
202    def represent_object(self, object): # Borrowed from PyYAML.
203        cls = type(object)
204        if cls in copy_reg.dispatch_table:
205            reduce = copy_reg.dispatch_table[cls](object)
206        elif hasattr(object, '__reduce_ex__'):
207            reduce = object.__reduce_ex__(2)
208        elif hasattr(object, '__reduce__'):
209            reduce = object.__reduce__()
210        else:
211            raise RuntimeError("cannot dump object: %r" % object)
212        reduce = (list(reduce)+[None]*3)[:3]
213        function, args, state = reduce
214        args = list(args)
215        if state is None:
216            state = {}
217        if function.__name__ == '__newobj__':
218            function = args[0]
219            args = args[1:]
220            tag = 'tag:python.yaml.org,2002:new:'
221            newobj = True
222        else:
223            tag = 'tag:python.yaml.org,2002:apply:'
224            newobj = False
225        function_name = '%s.%s' % (function.__module__, function.__name__)
226        if not args and isinstance(state, dict) and newobj:
227            return _syck.Map(state.copy(),
228                    'tag:python.yaml.org,2002:object:'+function_name)
229        if isinstance(state, dict) and not state:
230            return _syck.Seq(args, tag+function_name)
231        value = {}
232        if args:
233            value['args'] = args
234        if state or not isinstance(state, dict):
235            value['state'] = state
236        return _syck.Map(value, tag+function_name)
237
238    def represent__syck_Node(self, object):
239        object_type = type(object)
240        type_name = '%s.%s' % (object_type.__module__, object_type.__name__)
241        state = []
242        if hasattr(object_type, '__slotnames__'):
243            for name in object_type.__slotnames__:
244                value = getattr(object, name)
245                if value:
246                    state.append((name, value))
247        return _syck.Map(state,
248                tag="tag:python.yaml.org,2002:object:"+type_name)
249
250    def allow_aliases(self, object):
251        """Checks whether the given object can be aliased."""
252        if object is None or type(object) in [int, bool, float]:
253            return False
254        if type(object) is str and (not object or object.isalnum()):
255            return False
256        if type(object) is tuple and not object:
257            return False
258        return True
259
260def emit(node, output=None, Dumper=Dumper, **parameters):
261    """
262    Emits the given node to the output.
263
264    If output is None, returns the produced YAML document.
265    """
266    if output is None:
267        dumper = Dumper(StringIO.StringIO(), **parameters)
268    else:
269        dumper = Dumper(output, **parameters)
270    dumper.emit(node)
271    if output is None:
272        return dumper.output.getvalue()
273
274def dump(object, output=None, Dumper=Dumper, **parameters):
275    """
276    Dumps the given object to the output.
277
278    If output is None, returns the produced YAML document.
279    """
280    if output is None:
281        dumper = Dumper(StringIO.StringIO(), **parameters)
282    else:
283        dumper = Dumper(output, **parameters)
284    dumper.dump(object)
285    if output is None:
286        return dumper.output.getvalue()
287
288def emit_documents(nodes, output=None, Dumper=Dumper, **parameters):
289    """
290    Emits the list of nodes to the output.
291   
292    If output is None, returns the produced YAML document.
293    """
294    if output is None:
295        dumper = Dumper(StringIO.StringIO(), **parameters)
296    else:
297        dumper = Dumper(output, **parameters)
298    for node in nodes:
299        dumper.emit(node)
300    if output is None:
301        return dumper.output.getvalue()
302
303def dump_documents(objects, output=None, Dumper=Dumper, **parameters):
304    """
305    Dumps the list of objects to the output.
306   
307    If output is None, returns the produced YAML document.
308    """
309    if output is None:
310        dumper = Dumper(StringIO.StringIO(), **parameters)
311    else:
312        dumper = Dumper(output, **parameters)
313    for object in objects:
314        dumper.dump(object)
315    if output is None:
316        return dumper.output.getvalue()
317
318
Note: See TracBrowser for help on using the repository browser.