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