| 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 | import copy_reg |
|---|
| 14 | |
|---|
| 15 | __all__ = ['GenericDumper', 'Dumper', |
|---|
| 16 | 'emit', 'dump', 'emit_documents', 'dump_documents'] |
|---|
| 17 | |
|---|
| 18 | class 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 | |
|---|
| 67 | class 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 | |
|---|
| 260 | def 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 | |
|---|
| 274 | def 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 | |
|---|
| 288 | def 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 | |
|---|
| 303 | def 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 | |
|---|