comparison env/lib/python3.7/site-packages/ruamel/yaml/representer.py @ 0:26e78fe6e8c4 draft

"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
author shellac
date Sat, 02 May 2020 07:14:21 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:26e78fe6e8c4
1 # coding: utf-8
2
3 from __future__ import print_function, absolute_import, division
4
5
6 from ruamel.yaml.error import * # NOQA
7 from ruamel.yaml.nodes import * # NOQA
8 from ruamel.yaml.compat import text_type, binary_type, to_unicode, PY2, PY3, ordereddict
9 from ruamel.yaml.compat import nprint, nprintf # NOQA
10 from ruamel.yaml.scalarstring import (
11 LiteralScalarString,
12 FoldedScalarString,
13 SingleQuotedScalarString,
14 DoubleQuotedScalarString,
15 PlainScalarString,
16 )
17 from ruamel.yaml.scalarint import ScalarInt, BinaryInt, OctalInt, HexInt, HexCapsInt
18 from ruamel.yaml.scalarfloat import ScalarFloat
19 from ruamel.yaml.scalarbool import ScalarBoolean
20 from ruamel.yaml.timestamp import TimeStamp
21
22 import datetime
23 import sys
24 import types
25
26 if PY3:
27 import copyreg
28 import base64
29 else:
30 import copy_reg as copyreg # type: ignore
31
32 if False: # MYPY
33 from typing import Dict, List, Any, Union, Text, Optional # NOQA
34
35 # fmt: off
36 __all__ = ['BaseRepresenter', 'SafeRepresenter', 'Representer',
37 'RepresenterError', 'RoundTripRepresenter']
38 # fmt: on
39
40
41 class RepresenterError(YAMLError):
42 pass
43
44
45 if PY2:
46
47 def get_classobj_bases(cls):
48 # type: (Any) -> Any
49 bases = [cls]
50 for base in cls.__bases__:
51 bases.extend(get_classobj_bases(base))
52 return bases
53
54
55 class BaseRepresenter(object):
56
57 yaml_representers = {} # type: Dict[Any, Any]
58 yaml_multi_representers = {} # type: Dict[Any, Any]
59
60 def __init__(self, default_style=None, default_flow_style=None, dumper=None):
61 # type: (Any, Any, Any, Any) -> None
62 self.dumper = dumper
63 if self.dumper is not None:
64 self.dumper._representer = self
65 self.default_style = default_style
66 self.default_flow_style = default_flow_style
67 self.represented_objects = {} # type: Dict[Any, Any]
68 self.object_keeper = [] # type: List[Any]
69 self.alias_key = None # type: Optional[int]
70 self.sort_base_mapping_type_on_output = True
71
72 @property
73 def serializer(self):
74 # type: () -> Any
75 try:
76 if hasattr(self.dumper, 'typ'):
77 return self.dumper.serializer
78 return self.dumper._serializer
79 except AttributeError:
80 return self # cyaml
81
82 def represent(self, data):
83 # type: (Any) -> None
84 node = self.represent_data(data)
85 self.serializer.serialize(node)
86 self.represented_objects = {} # type: Dict[Any, Any]
87 self.object_keeper = [] # type: List[Any]
88 self.alias_key = None
89
90 def represent_data(self, data):
91 # type: (Any) -> Any
92 if self.ignore_aliases(data):
93 self.alias_key = None
94 else:
95 self.alias_key = id(data)
96 if self.alias_key is not None:
97 if self.alias_key in self.represented_objects:
98 node = self.represented_objects[self.alias_key]
99 # if node is None:
100 # raise RepresenterError(
101 # "recursive objects are not allowed: %r" % data)
102 return node
103 # self.represented_objects[alias_key] = None
104 self.object_keeper.append(data)
105 data_types = type(data).__mro__
106 if PY2:
107 # if type(data) is types.InstanceType:
108 if isinstance(data, types.InstanceType):
109 data_types = get_classobj_bases(data.__class__) + list(data_types)
110 if data_types[0] in self.yaml_representers:
111 node = self.yaml_representers[data_types[0]](self, data)
112 else:
113 for data_type in data_types:
114 if data_type in self.yaml_multi_representers:
115 node = self.yaml_multi_representers[data_type](self, data)
116 break
117 else:
118 if None in self.yaml_multi_representers:
119 node = self.yaml_multi_representers[None](self, data)
120 elif None in self.yaml_representers:
121 node = self.yaml_representers[None](self, data)
122 else:
123 node = ScalarNode(None, text_type(data))
124 # if alias_key is not None:
125 # self.represented_objects[alias_key] = node
126 return node
127
128 def represent_key(self, data):
129 # type: (Any) -> Any
130 """
131 David Fraser: Extract a method to represent keys in mappings, so that
132 a subclass can choose not to quote them (for example)
133 used in represent_mapping
134 https://bitbucket.org/davidfraser/pyyaml/commits/d81df6eb95f20cac4a79eed95ae553b5c6f77b8c
135 """
136 return self.represent_data(data)
137
138 @classmethod
139 def add_representer(cls, data_type, representer):
140 # type: (Any, Any) -> None
141 if 'yaml_representers' not in cls.__dict__:
142 cls.yaml_representers = cls.yaml_representers.copy()
143 cls.yaml_representers[data_type] = representer
144
145 @classmethod
146 def add_multi_representer(cls, data_type, representer):
147 # type: (Any, Any) -> None
148 if 'yaml_multi_representers' not in cls.__dict__:
149 cls.yaml_multi_representers = cls.yaml_multi_representers.copy()
150 cls.yaml_multi_representers[data_type] = representer
151
152 def represent_scalar(self, tag, value, style=None, anchor=None):
153 # type: (Any, Any, Any, Any) -> Any
154 if style is None:
155 style = self.default_style
156 comment = None
157 if style and style[0] in '|>':
158 comment = getattr(value, 'comment', None)
159 if comment:
160 comment = [None, [comment]]
161 node = ScalarNode(tag, value, style=style, comment=comment, anchor=anchor)
162 if self.alias_key is not None:
163 self.represented_objects[self.alias_key] = node
164 return node
165
166 def represent_sequence(self, tag, sequence, flow_style=None):
167 # type: (Any, Any, Any) -> Any
168 value = [] # type: List[Any]
169 node = SequenceNode(tag, value, flow_style=flow_style)
170 if self.alias_key is not None:
171 self.represented_objects[self.alias_key] = node
172 best_style = True
173 for item in sequence:
174 node_item = self.represent_data(item)
175 if not (isinstance(node_item, ScalarNode) and not node_item.style):
176 best_style = False
177 value.append(node_item)
178 if flow_style is None:
179 if self.default_flow_style is not None:
180 node.flow_style = self.default_flow_style
181 else:
182 node.flow_style = best_style
183 return node
184
185 def represent_omap(self, tag, omap, flow_style=None):
186 # type: (Any, Any, Any) -> Any
187 value = [] # type: List[Any]
188 node = SequenceNode(tag, value, flow_style=flow_style)
189 if self.alias_key is not None:
190 self.represented_objects[self.alias_key] = node
191 best_style = True
192 for item_key in omap:
193 item_val = omap[item_key]
194 node_item = self.represent_data({item_key: item_val})
195 # if not (isinstance(node_item, ScalarNode) \
196 # and not node_item.style):
197 # best_style = False
198 value.append(node_item)
199 if flow_style is None:
200 if self.default_flow_style is not None:
201 node.flow_style = self.default_flow_style
202 else:
203 node.flow_style = best_style
204 return node
205
206 def represent_mapping(self, tag, mapping, flow_style=None):
207 # type: (Any, Any, Any) -> Any
208 value = [] # type: List[Any]
209 node = MappingNode(tag, value, flow_style=flow_style)
210 if self.alias_key is not None:
211 self.represented_objects[self.alias_key] = node
212 best_style = True
213 if hasattr(mapping, 'items'):
214 mapping = list(mapping.items())
215 if self.sort_base_mapping_type_on_output:
216 try:
217 mapping = sorted(mapping)
218 except TypeError:
219 pass
220 for item_key, item_value in mapping:
221 node_key = self.represent_key(item_key)
222 node_value = self.represent_data(item_value)
223 if not (isinstance(node_key, ScalarNode) and not node_key.style):
224 best_style = False
225 if not (isinstance(node_value, ScalarNode) and not node_value.style):
226 best_style = False
227 value.append((node_key, node_value))
228 if flow_style is None:
229 if self.default_flow_style is not None:
230 node.flow_style = self.default_flow_style
231 else:
232 node.flow_style = best_style
233 return node
234
235 def ignore_aliases(self, data):
236 # type: (Any) -> bool
237 return False
238
239
240 class SafeRepresenter(BaseRepresenter):
241 def ignore_aliases(self, data):
242 # type: (Any) -> bool
243 # https://docs.python.org/3/reference/expressions.html#parenthesized-forms :
244 # "i.e. two occurrences of the empty tuple may or may not yield the same object"
245 # so "data is ()" should not be used
246 if data is None or (isinstance(data, tuple) and data == ()):
247 return True
248 if isinstance(data, (binary_type, text_type, bool, int, float)):
249 return True
250 return False
251
252 def represent_none(self, data):
253 # type: (Any) -> Any
254 return self.represent_scalar(u'tag:yaml.org,2002:null', u'null')
255
256 if PY3:
257
258 def represent_str(self, data):
259 # type: (Any) -> Any
260 return self.represent_scalar(u'tag:yaml.org,2002:str', data)
261
262 def represent_binary(self, data):
263 # type: (Any) -> Any
264 if hasattr(base64, 'encodebytes'):
265 data = base64.encodebytes(data).decode('ascii')
266 else:
267 data = base64.encodestring(data).decode('ascii')
268 return self.represent_scalar(u'tag:yaml.org,2002:binary', data, style='|')
269
270 else:
271
272 def represent_str(self, data):
273 # type: (Any) -> Any
274 tag = None
275 style = None
276 try:
277 data = unicode(data, 'ascii')
278 tag = u'tag:yaml.org,2002:str'
279 except UnicodeDecodeError:
280 try:
281 data = unicode(data, 'utf-8')
282 tag = u'tag:yaml.org,2002:str'
283 except UnicodeDecodeError:
284 data = data.encode('base64')
285 tag = u'tag:yaml.org,2002:binary'
286 style = '|'
287 return self.represent_scalar(tag, data, style=style)
288
289 def represent_unicode(self, data):
290 # type: (Any) -> Any
291 return self.represent_scalar(u'tag:yaml.org,2002:str', data)
292
293 def represent_bool(self, data, anchor=None):
294 # type: (Any, Optional[Any]) -> Any
295 try:
296 value = self.dumper.boolean_representation[bool(data)]
297 except AttributeError:
298 if data:
299 value = u'true'
300 else:
301 value = u'false'
302 return self.represent_scalar(u'tag:yaml.org,2002:bool', value, anchor=anchor)
303
304 def represent_int(self, data):
305 # type: (Any) -> Any
306 return self.represent_scalar(u'tag:yaml.org,2002:int', text_type(data))
307
308 if PY2:
309
310 def represent_long(self, data):
311 # type: (Any) -> Any
312 return self.represent_scalar(u'tag:yaml.org,2002:int', text_type(data))
313
314 inf_value = 1e300
315 while repr(inf_value) != repr(inf_value * inf_value):
316 inf_value *= inf_value
317
318 def represent_float(self, data):
319 # type: (Any) -> Any
320 if data != data or (data == 0.0 and data == 1.0):
321 value = u'.nan'
322 elif data == self.inf_value:
323 value = u'.inf'
324 elif data == -self.inf_value:
325 value = u'-.inf'
326 else:
327 value = to_unicode(repr(data)).lower()
328 if getattr(self.serializer, 'use_version', None) == (1, 1):
329 if u'.' not in value and u'e' in value:
330 # Note that in some cases `repr(data)` represents a float number
331 # without the decimal parts. For instance:
332 # >>> repr(1e17)
333 # '1e17'
334 # Unfortunately, this is not a valid float representation according
335 # to the definition of the `!!float` tag in YAML 1.1. We fix
336 # this by adding '.0' before the 'e' symbol.
337 value = value.replace(u'e', u'.0e', 1)
338 return self.represent_scalar(u'tag:yaml.org,2002:float', value)
339
340 def represent_list(self, data):
341 # type: (Any) -> Any
342 # pairs = (len(data) > 0 and isinstance(data, list))
343 # if pairs:
344 # for item in data:
345 # if not isinstance(item, tuple) or len(item) != 2:
346 # pairs = False
347 # break
348 # if not pairs:
349 return self.represent_sequence(u'tag:yaml.org,2002:seq', data)
350
351 # value = []
352 # for item_key, item_value in data:
353 # value.append(self.represent_mapping(u'tag:yaml.org,2002:map',
354 # [(item_key, item_value)]))
355 # return SequenceNode(u'tag:yaml.org,2002:pairs', value)
356
357 def represent_dict(self, data):
358 # type: (Any) -> Any
359 return self.represent_mapping(u'tag:yaml.org,2002:map', data)
360
361 def represent_ordereddict(self, data):
362 # type: (Any) -> Any
363 return self.represent_omap(u'tag:yaml.org,2002:omap', data)
364
365 def represent_set(self, data):
366 # type: (Any) -> Any
367 value = {} # type: Dict[Any, None]
368 for key in data:
369 value[key] = None
370 return self.represent_mapping(u'tag:yaml.org,2002:set', value)
371
372 def represent_date(self, data):
373 # type: (Any) -> Any
374 value = to_unicode(data.isoformat())
375 return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value)
376
377 def represent_datetime(self, data):
378 # type: (Any) -> Any
379 value = to_unicode(data.isoformat(' '))
380 return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value)
381
382 def represent_yaml_object(self, tag, data, cls, flow_style=None):
383 # type: (Any, Any, Any, Any) -> Any
384 if hasattr(data, '__getstate__'):
385 state = data.__getstate__()
386 else:
387 state = data.__dict__.copy()
388 return self.represent_mapping(tag, state, flow_style=flow_style)
389
390 def represent_undefined(self, data):
391 # type: (Any) -> None
392 raise RepresenterError('cannot represent an object: %s' % (data,))
393
394
395 SafeRepresenter.add_representer(type(None), SafeRepresenter.represent_none)
396
397 SafeRepresenter.add_representer(str, SafeRepresenter.represent_str)
398
399 if PY2:
400 SafeRepresenter.add_representer(unicode, SafeRepresenter.represent_unicode)
401 else:
402 SafeRepresenter.add_representer(bytes, SafeRepresenter.represent_binary)
403
404 SafeRepresenter.add_representer(bool, SafeRepresenter.represent_bool)
405
406 SafeRepresenter.add_representer(int, SafeRepresenter.represent_int)
407
408 if PY2:
409 SafeRepresenter.add_representer(long, SafeRepresenter.represent_long)
410
411 SafeRepresenter.add_representer(float, SafeRepresenter.represent_float)
412
413 SafeRepresenter.add_representer(list, SafeRepresenter.represent_list)
414
415 SafeRepresenter.add_representer(tuple, SafeRepresenter.represent_list)
416
417 SafeRepresenter.add_representer(dict, SafeRepresenter.represent_dict)
418
419 SafeRepresenter.add_representer(set, SafeRepresenter.represent_set)
420
421 SafeRepresenter.add_representer(ordereddict, SafeRepresenter.represent_ordereddict)
422
423 if sys.version_info >= (2, 7):
424 import collections
425
426 SafeRepresenter.add_representer(
427 collections.OrderedDict, SafeRepresenter.represent_ordereddict
428 )
429
430 SafeRepresenter.add_representer(datetime.date, SafeRepresenter.represent_date)
431
432 SafeRepresenter.add_representer(datetime.datetime, SafeRepresenter.represent_datetime)
433
434 SafeRepresenter.add_representer(None, SafeRepresenter.represent_undefined)
435
436
437 class Representer(SafeRepresenter):
438 if PY2:
439
440 def represent_str(self, data):
441 # type: (Any) -> Any
442 tag = None
443 style = None
444 try:
445 data = unicode(data, 'ascii')
446 tag = u'tag:yaml.org,2002:str'
447 except UnicodeDecodeError:
448 try:
449 data = unicode(data, 'utf-8')
450 tag = u'tag:yaml.org,2002:python/str'
451 except UnicodeDecodeError:
452 data = data.encode('base64')
453 tag = u'tag:yaml.org,2002:binary'
454 style = '|'
455 return self.represent_scalar(tag, data, style=style)
456
457 def represent_unicode(self, data):
458 # type: (Any) -> Any
459 tag = None
460 try:
461 data.encode('ascii')
462 tag = u'tag:yaml.org,2002:python/unicode'
463 except UnicodeEncodeError:
464 tag = u'tag:yaml.org,2002:str'
465 return self.represent_scalar(tag, data)
466
467 def represent_long(self, data):
468 # type: (Any) -> Any
469 tag = u'tag:yaml.org,2002:int'
470 if int(data) is not data:
471 tag = u'tag:yaml.org,2002:python/long'
472 return self.represent_scalar(tag, to_unicode(data))
473
474 def represent_complex(self, data):
475 # type: (Any) -> Any
476 if data.imag == 0.0:
477 data = u'%r' % data.real
478 elif data.real == 0.0:
479 data = u'%rj' % data.imag
480 elif data.imag > 0:
481 data = u'%r+%rj' % (data.real, data.imag)
482 else:
483 data = u'%r%rj' % (data.real, data.imag)
484 return self.represent_scalar(u'tag:yaml.org,2002:python/complex', data)
485
486 def represent_tuple(self, data):
487 # type: (Any) -> Any
488 return self.represent_sequence(u'tag:yaml.org,2002:python/tuple', data)
489
490 def represent_name(self, data):
491 # type: (Any) -> Any
492 try:
493 name = u'%s.%s' % (data.__module__, data.__qualname__)
494 except AttributeError:
495 # probably PY2
496 name = u'%s.%s' % (data.__module__, data.__name__)
497 return self.represent_scalar(u'tag:yaml.org,2002:python/name:' + name, "")
498
499 def represent_module(self, data):
500 # type: (Any) -> Any
501 return self.represent_scalar(u'tag:yaml.org,2002:python/module:' + data.__name__, "")
502
503 if PY2:
504
505 def represent_instance(self, data):
506 # type: (Any) -> Any
507 # For instances of classic classes, we use __getinitargs__ and
508 # __getstate__ to serialize the data.
509
510 # If data.__getinitargs__ exists, the object must be reconstructed
511 # by calling cls(**args), where args is a tuple returned by
512 # __getinitargs__. Otherwise, the cls.__init__ method should never
513 # be called and the class instance is created by instantiating a
514 # trivial class and assigning to the instance's __class__ variable.
515
516 # If data.__getstate__ exists, it returns the state of the object.
517 # Otherwise, the state of the object is data.__dict__.
518
519 # We produce either a !!python/object or !!python/object/new node.
520 # If data.__getinitargs__ does not exist and state is a dictionary,
521 # we produce a !!python/object node . Otherwise we produce a
522 # !!python/object/new node.
523
524 cls = data.__class__
525 class_name = u'%s.%s' % (cls.__module__, cls.__name__)
526 args = None
527 state = None
528 if hasattr(data, '__getinitargs__'):
529 args = list(data.__getinitargs__())
530 if hasattr(data, '__getstate__'):
531 state = data.__getstate__()
532 else:
533 state = data.__dict__
534 if args is None and isinstance(state, dict):
535 return self.represent_mapping(
536 u'tag:yaml.org,2002:python/object:' + class_name, state
537 )
538 if isinstance(state, dict) and not state:
539 return self.represent_sequence(
540 u'tag:yaml.org,2002:python/object/new:' + class_name, args
541 )
542 value = {}
543 if bool(args):
544 value['args'] = args
545 value['state'] = state # type: ignore
546 return self.represent_mapping(
547 u'tag:yaml.org,2002:python/object/new:' + class_name, value
548 )
549
550 def represent_object(self, data):
551 # type: (Any) -> Any
552 # We use __reduce__ API to save the data. data.__reduce__ returns
553 # a tuple of length 2-5:
554 # (function, args, state, listitems, dictitems)
555
556 # For reconstructing, we calls function(*args), then set its state,
557 # listitems, and dictitems if they are not None.
558
559 # A special case is when function.__name__ == '__newobj__'. In this
560 # case we create the object with args[0].__new__(*args).
561
562 # Another special case is when __reduce__ returns a string - we don't
563 # support it.
564
565 # We produce a !!python/object, !!python/object/new or
566 # !!python/object/apply node.
567
568 cls = type(data)
569 if cls in copyreg.dispatch_table:
570 reduce = copyreg.dispatch_table[cls](data)
571 elif hasattr(data, '__reduce_ex__'):
572 reduce = data.__reduce_ex__(2)
573 elif hasattr(data, '__reduce__'):
574 reduce = data.__reduce__()
575 else:
576 raise RepresenterError('cannot represent object: %r' % (data,))
577 reduce = (list(reduce) + [None] * 5)[:5]
578 function, args, state, listitems, dictitems = reduce
579 args = list(args)
580 if state is None:
581 state = {}
582 if listitems is not None:
583 listitems = list(listitems)
584 if dictitems is not None:
585 dictitems = dict(dictitems)
586 if function.__name__ == '__newobj__':
587 function = args[0]
588 args = args[1:]
589 tag = u'tag:yaml.org,2002:python/object/new:'
590 newobj = True
591 else:
592 tag = u'tag:yaml.org,2002:python/object/apply:'
593 newobj = False
594 try:
595 function_name = u'%s.%s' % (function.__module__, function.__qualname__)
596 except AttributeError:
597 # probably PY2
598 function_name = u'%s.%s' % (function.__module__, function.__name__)
599 if not args and not listitems and not dictitems and isinstance(state, dict) and newobj:
600 return self.represent_mapping(
601 u'tag:yaml.org,2002:python/object:' + function_name, state
602 )
603 if not listitems and not dictitems and isinstance(state, dict) and not state:
604 return self.represent_sequence(tag + function_name, args)
605 value = {}
606 if args:
607 value['args'] = args
608 if state or not isinstance(state, dict):
609 value['state'] = state
610 if listitems:
611 value['listitems'] = listitems
612 if dictitems:
613 value['dictitems'] = dictitems
614 return self.represent_mapping(tag + function_name, value)
615
616
617 if PY2:
618 Representer.add_representer(str, Representer.represent_str)
619
620 Representer.add_representer(unicode, Representer.represent_unicode)
621
622 Representer.add_representer(long, Representer.represent_long)
623
624 Representer.add_representer(complex, Representer.represent_complex)
625
626 Representer.add_representer(tuple, Representer.represent_tuple)
627
628 Representer.add_representer(type, Representer.represent_name)
629
630 if PY2:
631 Representer.add_representer(types.ClassType, Representer.represent_name)
632
633 Representer.add_representer(types.FunctionType, Representer.represent_name)
634
635 Representer.add_representer(types.BuiltinFunctionType, Representer.represent_name)
636
637 Representer.add_representer(types.ModuleType, Representer.represent_module)
638
639 if PY2:
640 Representer.add_multi_representer(types.InstanceType, Representer.represent_instance)
641
642 Representer.add_multi_representer(object, Representer.represent_object)
643
644 Representer.add_multi_representer(type, Representer.represent_name)
645
646 from ruamel.yaml.comments import (
647 CommentedMap,
648 CommentedOrderedMap,
649 CommentedSeq,
650 CommentedKeySeq,
651 CommentedKeyMap,
652 CommentedSet,
653 comment_attrib,
654 merge_attrib,
655 TaggedScalar,
656 ) # NOQA
657
658
659 class RoundTripRepresenter(SafeRepresenter):
660 # need to add type here and write out the .comment
661 # in serializer and emitter
662
663 def __init__(self, default_style=None, default_flow_style=None, dumper=None):
664 # type: (Any, Any, Any) -> None
665 if not hasattr(dumper, 'typ') and default_flow_style is None:
666 default_flow_style = False
667 SafeRepresenter.__init__(
668 self,
669 default_style=default_style,
670 default_flow_style=default_flow_style,
671 dumper=dumper,
672 )
673
674 def ignore_aliases(self, data):
675 # type: (Any) -> bool
676 try:
677 if data.anchor is not None and data.anchor.value is not None:
678 return False
679 except AttributeError:
680 pass
681 return SafeRepresenter.ignore_aliases(self, data)
682
683 def represent_none(self, data):
684 # type: (Any) -> Any
685 if len(self.represented_objects) == 0 and not self.serializer.use_explicit_start:
686 # this will be open ended (although it is not yet)
687 return self.represent_scalar(u'tag:yaml.org,2002:null', u'null')
688 return self.represent_scalar(u'tag:yaml.org,2002:null', "")
689
690 def represent_literal_scalarstring(self, data):
691 # type: (Any) -> Any
692 tag = None
693 style = '|'
694 anchor = data.yaml_anchor(any=True)
695 if PY2 and not isinstance(data, unicode):
696 data = unicode(data, 'ascii')
697 tag = u'tag:yaml.org,2002:str'
698 return self.represent_scalar(tag, data, style=style, anchor=anchor)
699
700 represent_preserved_scalarstring = represent_literal_scalarstring
701
702 def represent_folded_scalarstring(self, data):
703 # type: (Any) -> Any
704 tag = None
705 style = '>'
706 anchor = data.yaml_anchor(any=True)
707 for fold_pos in reversed(getattr(data, 'fold_pos', [])):
708 if (
709 data[fold_pos] == ' '
710 and (fold_pos > 0 and not data[fold_pos - 1].isspace())
711 and (fold_pos < len(data) and not data[fold_pos + 1].isspace())
712 ):
713 data = data[:fold_pos] + '\a' + data[fold_pos:]
714 if PY2 and not isinstance(data, unicode):
715 data = unicode(data, 'ascii')
716 tag = u'tag:yaml.org,2002:str'
717 return self.represent_scalar(tag, data, style=style, anchor=anchor)
718
719 def represent_single_quoted_scalarstring(self, data):
720 # type: (Any) -> Any
721 tag = None
722 style = "'"
723 anchor = data.yaml_anchor(any=True)
724 if PY2 and not isinstance(data, unicode):
725 data = unicode(data, 'ascii')
726 tag = u'tag:yaml.org,2002:str'
727 return self.represent_scalar(tag, data, style=style, anchor=anchor)
728
729 def represent_double_quoted_scalarstring(self, data):
730 # type: (Any) -> Any
731 tag = None
732 style = '"'
733 anchor = data.yaml_anchor(any=True)
734 if PY2 and not isinstance(data, unicode):
735 data = unicode(data, 'ascii')
736 tag = u'tag:yaml.org,2002:str'
737 return self.represent_scalar(tag, data, style=style, anchor=anchor)
738
739 def represent_plain_scalarstring(self, data):
740 # type: (Any) -> Any
741 tag = None
742 style = ''
743 anchor = data.yaml_anchor(any=True)
744 if PY2 and not isinstance(data, unicode):
745 data = unicode(data, 'ascii')
746 tag = u'tag:yaml.org,2002:str'
747 return self.represent_scalar(tag, data, style=style, anchor=anchor)
748
749 def insert_underscore(self, prefix, s, underscore, anchor=None):
750 # type: (Any, Any, Any, Any) -> Any
751 if underscore is None:
752 return self.represent_scalar(u'tag:yaml.org,2002:int', prefix + s, anchor=anchor)
753 if underscore[0]:
754 sl = list(s)
755 pos = len(s) - underscore[0]
756 while pos > 0:
757 sl.insert(pos, '_')
758 pos -= underscore[0]
759 s = "".join(sl)
760 if underscore[1]:
761 s = '_' + s
762 if underscore[2]:
763 s += '_'
764 return self.represent_scalar(u'tag:yaml.org,2002:int', prefix + s, anchor=anchor)
765
766 def represent_scalar_int(self, data):
767 # type: (Any) -> Any
768 if data._width is not None:
769 s = '{:0{}d}'.format(data, data._width)
770 else:
771 s = format(data, 'd')
772 anchor = data.yaml_anchor(any=True)
773 return self.insert_underscore("", s, data._underscore, anchor=anchor)
774
775 def represent_binary_int(self, data):
776 # type: (Any) -> Any
777 if data._width is not None:
778 # cannot use '{:#0{}b}', that strips the zeros
779 s = '{:0{}b}'.format(data, data._width)
780 else:
781 s = format(data, 'b')
782 anchor = data.yaml_anchor(any=True)
783 return self.insert_underscore('0b', s, data._underscore, anchor=anchor)
784
785 def represent_octal_int(self, data):
786 # type: (Any) -> Any
787 if data._width is not None:
788 # cannot use '{:#0{}o}', that strips the zeros
789 s = '{:0{}o}'.format(data, data._width)
790 else:
791 s = format(data, 'o')
792 anchor = data.yaml_anchor(any=True)
793 return self.insert_underscore('0o', s, data._underscore, anchor=anchor)
794
795 def represent_hex_int(self, data):
796 # type: (Any) -> Any
797 if data._width is not None:
798 # cannot use '{:#0{}x}', that strips the zeros
799 s = '{:0{}x}'.format(data, data._width)
800 else:
801 s = format(data, 'x')
802 anchor = data.yaml_anchor(any=True)
803 return self.insert_underscore('0x', s, data._underscore, anchor=anchor)
804
805 def represent_hex_caps_int(self, data):
806 # type: (Any) -> Any
807 if data._width is not None:
808 # cannot use '{:#0{}X}', that strips the zeros
809 s = '{:0{}X}'.format(data, data._width)
810 else:
811 s = format(data, 'X')
812 anchor = data.yaml_anchor(any=True)
813 return self.insert_underscore('0x', s, data._underscore, anchor=anchor)
814
815 def represent_scalar_float(self, data):
816 # type: (Any) -> Any
817 """ this is way more complicated """
818 value = None
819 anchor = data.yaml_anchor(any=True)
820 if data != data or (data == 0.0 and data == 1.0):
821 value = u'.nan'
822 elif data == self.inf_value:
823 value = u'.inf'
824 elif data == -self.inf_value:
825 value = u'-.inf'
826 if value:
827 return self.represent_scalar(u'tag:yaml.org,2002:float', value, anchor=anchor)
828 if data._exp is None and data._prec > 0 and data._prec == data._width - 1:
829 # no exponent, but trailing dot
830 value = u'{}{:d}.'.format(data._m_sign if data._m_sign else "", abs(int(data)))
831 elif data._exp is None:
832 # no exponent, "normal" dot
833 prec = data._prec
834 ms = data._m_sign if data._m_sign else ""
835 # -1 for the dot
836 value = u'{}{:0{}.{}f}'.format(
837 ms, abs(data), data._width - len(ms), data._width - prec - 1
838 )
839 if prec == 0 or (prec == 1 and ms != ""):
840 value = value.replace(u'0.', u'.')
841 while len(value) < data._width:
842 value += u'0'
843 else:
844 # exponent
845 m, es = u'{:{}.{}e}'.format(
846 # data, data._width, data._width - data._prec + (1 if data._m_sign else 0)
847 data,
848 data._width,
849 data._width + (1 if data._m_sign else 0),
850 ).split('e')
851 w = data._width if data._prec > 0 else (data._width + 1)
852 if data < 0:
853 w += 1
854 m = m[:w]
855 e = int(es)
856 m1, m2 = m.split('.') # always second?
857 while len(m1) + len(m2) < data._width - (1 if data._prec >= 0 else 0):
858 m2 += u'0'
859 if data._m_sign and data > 0:
860 m1 = '+' + m1
861 esgn = u'+' if data._e_sign else ""
862 if data._prec < 0: # mantissa without dot
863 if m2 != u'0':
864 e -= len(m2)
865 else:
866 m2 = ""
867 while (len(m1) + len(m2) - (1 if data._m_sign else 0)) < data._width:
868 m2 += u'0'
869 e -= 1
870 value = m1 + m2 + data._exp + u'{:{}0{}d}'.format(e, esgn, data._e_width)
871 elif data._prec == 0: # mantissa with trailing dot
872 e -= len(m2)
873 value = (
874 m1 + m2 + u'.' + data._exp + u'{:{}0{}d}'.format(e, esgn, data._e_width)
875 )
876 else:
877 if data._m_lead0 > 0:
878 m2 = u'0' * (data._m_lead0 - 1) + m1 + m2
879 m1 = u'0'
880 m2 = m2[: -data._m_lead0] # these should be zeros
881 e += data._m_lead0
882 while len(m1) < data._prec:
883 m1 += m2[0]
884 m2 = m2[1:]
885 e -= 1
886 value = (
887 m1 + u'.' + m2 + data._exp + u'{:{}0{}d}'.format(e, esgn, data._e_width)
888 )
889
890 if value is None:
891 value = to_unicode(repr(data)).lower()
892 return self.represent_scalar(u'tag:yaml.org,2002:float', value, anchor=anchor)
893
894 def represent_sequence(self, tag, sequence, flow_style=None):
895 # type: (Any, Any, Any) -> Any
896 value = [] # type: List[Any]
897 # if the flow_style is None, the flow style tacked on to the object
898 # explicitly will be taken. If that is None as well the default flow
899 # style rules
900 try:
901 flow_style = sequence.fa.flow_style(flow_style)
902 except AttributeError:
903 flow_style = flow_style
904 try:
905 anchor = sequence.yaml_anchor()
906 except AttributeError:
907 anchor = None
908 node = SequenceNode(tag, value, flow_style=flow_style, anchor=anchor)
909 if self.alias_key is not None:
910 self.represented_objects[self.alias_key] = node
911 best_style = True
912 try:
913 comment = getattr(sequence, comment_attrib)
914 node.comment = comment.comment
915 # reset any comment already printed information
916 if node.comment and node.comment[1]:
917 for ct in node.comment[1]:
918 ct.reset()
919 item_comments = comment.items
920 for v in item_comments.values():
921 if v and v[1]:
922 for ct in v[1]:
923 ct.reset()
924 item_comments = comment.items
925 node.comment = comment.comment
926 try:
927 node.comment.append(comment.end)
928 except AttributeError:
929 pass
930 except AttributeError:
931 item_comments = {}
932 for idx, item in enumerate(sequence):
933 node_item = self.represent_data(item)
934 self.merge_comments(node_item, item_comments.get(idx))
935 if not (isinstance(node_item, ScalarNode) and not node_item.style):
936 best_style = False
937 value.append(node_item)
938 if flow_style is None:
939 if len(sequence) != 0 and self.default_flow_style is not None:
940 node.flow_style = self.default_flow_style
941 else:
942 node.flow_style = best_style
943 return node
944
945 def merge_comments(self, node, comments):
946 # type: (Any, Any) -> Any
947 if comments is None:
948 assert hasattr(node, 'comment')
949 return node
950 if getattr(node, 'comment', None) is not None:
951 for idx, val in enumerate(comments):
952 if idx >= len(node.comment):
953 continue
954 nc = node.comment[idx]
955 if nc is not None:
956 assert val is None or val == nc
957 comments[idx] = nc
958 node.comment = comments
959 return node
960
961 def represent_key(self, data):
962 # type: (Any) -> Any
963 if isinstance(data, CommentedKeySeq):
964 self.alias_key = None
965 return self.represent_sequence(u'tag:yaml.org,2002:seq', data, flow_style=True)
966 if isinstance(data, CommentedKeyMap):
967 self.alias_key = None
968 return self.represent_mapping(u'tag:yaml.org,2002:map', data, flow_style=True)
969 return SafeRepresenter.represent_key(self, data)
970
971 def represent_mapping(self, tag, mapping, flow_style=None):
972 # type: (Any, Any, Any) -> Any
973 value = [] # type: List[Any]
974 try:
975 flow_style = mapping.fa.flow_style(flow_style)
976 except AttributeError:
977 flow_style = flow_style
978 try:
979 anchor = mapping.yaml_anchor()
980 except AttributeError:
981 anchor = None
982 node = MappingNode(tag, value, flow_style=flow_style, anchor=anchor)
983 if self.alias_key is not None:
984 self.represented_objects[self.alias_key] = node
985 best_style = True
986 # no sorting! !!
987 try:
988 comment = getattr(mapping, comment_attrib)
989 node.comment = comment.comment
990 if node.comment and node.comment[1]:
991 for ct in node.comment[1]:
992 ct.reset()
993 item_comments = comment.items
994 for v in item_comments.values():
995 if v and v[1]:
996 for ct in v[1]:
997 ct.reset()
998 try:
999 node.comment.append(comment.end)
1000 except AttributeError:
1001 pass
1002 except AttributeError:
1003 item_comments = {}
1004 merge_list = [m[1] for m in getattr(mapping, merge_attrib, [])]
1005 try:
1006 merge_pos = getattr(mapping, merge_attrib, [[0]])[0][0]
1007 except IndexError:
1008 merge_pos = 0
1009 item_count = 0
1010 if bool(merge_list):
1011 items = mapping.non_merged_items()
1012 else:
1013 items = mapping.items()
1014 for item_key, item_value in items:
1015 item_count += 1
1016 node_key = self.represent_key(item_key)
1017 node_value = self.represent_data(item_value)
1018 item_comment = item_comments.get(item_key)
1019 if item_comment:
1020 assert getattr(node_key, 'comment', None) is None
1021 node_key.comment = item_comment[:2]
1022 nvc = getattr(node_value, 'comment', None)
1023 if nvc is not None: # end comment already there
1024 nvc[0] = item_comment[2]
1025 nvc[1] = item_comment[3]
1026 else:
1027 node_value.comment = item_comment[2:]
1028 if not (isinstance(node_key, ScalarNode) and not node_key.style):
1029 best_style = False
1030 if not (isinstance(node_value, ScalarNode) and not node_value.style):
1031 best_style = False
1032 value.append((node_key, node_value))
1033 if flow_style is None:
1034 if ((item_count != 0) or bool(merge_list)) and self.default_flow_style is not None:
1035 node.flow_style = self.default_flow_style
1036 else:
1037 node.flow_style = best_style
1038 if bool(merge_list):
1039 # because of the call to represent_data here, the anchors
1040 # are marked as being used and thereby created
1041 if len(merge_list) == 1:
1042 arg = self.represent_data(merge_list[0])
1043 else:
1044 arg = self.represent_data(merge_list)
1045 arg.flow_style = True
1046 value.insert(merge_pos, (ScalarNode(u'tag:yaml.org,2002:merge', '<<'), arg))
1047 return node
1048
1049 def represent_omap(self, tag, omap, flow_style=None):
1050 # type: (Any, Any, Any) -> Any
1051 value = [] # type: List[Any]
1052 try:
1053 flow_style = omap.fa.flow_style(flow_style)
1054 except AttributeError:
1055 flow_style = flow_style
1056 try:
1057 anchor = omap.yaml_anchor()
1058 except AttributeError:
1059 anchor = None
1060 node = SequenceNode(tag, value, flow_style=flow_style, anchor=anchor)
1061 if self.alias_key is not None:
1062 self.represented_objects[self.alias_key] = node
1063 best_style = True
1064 try:
1065 comment = getattr(omap, comment_attrib)
1066 node.comment = comment.comment
1067 if node.comment and node.comment[1]:
1068 for ct in node.comment[1]:
1069 ct.reset()
1070 item_comments = comment.items
1071 for v in item_comments.values():
1072 if v and v[1]:
1073 for ct in v[1]:
1074 ct.reset()
1075 try:
1076 node.comment.append(comment.end)
1077 except AttributeError:
1078 pass
1079 except AttributeError:
1080 item_comments = {}
1081 for item_key in omap:
1082 item_val = omap[item_key]
1083 node_item = self.represent_data({item_key: item_val})
1084 # node_item.flow_style = False
1085 # node item has two scalars in value: node_key and node_value
1086 item_comment = item_comments.get(item_key)
1087 if item_comment:
1088 if item_comment[1]:
1089 node_item.comment = [None, item_comment[1]]
1090 assert getattr(node_item.value[0][0], 'comment', None) is None
1091 node_item.value[0][0].comment = [item_comment[0], None]
1092 nvc = getattr(node_item.value[0][1], 'comment', None)
1093 if nvc is not None: # end comment already there
1094 nvc[0] = item_comment[2]
1095 nvc[1] = item_comment[3]
1096 else:
1097 node_item.value[0][1].comment = item_comment[2:]
1098 # if not (isinstance(node_item, ScalarNode) \
1099 # and not node_item.style):
1100 # best_style = False
1101 value.append(node_item)
1102 if flow_style is None:
1103 if self.default_flow_style is not None:
1104 node.flow_style = self.default_flow_style
1105 else:
1106 node.flow_style = best_style
1107 return node
1108
1109 def represent_set(self, setting):
1110 # type: (Any) -> Any
1111 flow_style = False
1112 tag = u'tag:yaml.org,2002:set'
1113 # return self.represent_mapping(tag, value)
1114 value = [] # type: List[Any]
1115 flow_style = setting.fa.flow_style(flow_style)
1116 try:
1117 anchor = setting.yaml_anchor()
1118 except AttributeError:
1119 anchor = None
1120 node = MappingNode(tag, value, flow_style=flow_style, anchor=anchor)
1121 if self.alias_key is not None:
1122 self.represented_objects[self.alias_key] = node
1123 best_style = True
1124 # no sorting! !!
1125 try:
1126 comment = getattr(setting, comment_attrib)
1127 node.comment = comment.comment
1128 if node.comment and node.comment[1]:
1129 for ct in node.comment[1]:
1130 ct.reset()
1131 item_comments = comment.items
1132 for v in item_comments.values():
1133 if v and v[1]:
1134 for ct in v[1]:
1135 ct.reset()
1136 try:
1137 node.comment.append(comment.end)
1138 except AttributeError:
1139 pass
1140 except AttributeError:
1141 item_comments = {}
1142 for item_key in setting.odict:
1143 node_key = self.represent_key(item_key)
1144 node_value = self.represent_data(None)
1145 item_comment = item_comments.get(item_key)
1146 if item_comment:
1147 assert getattr(node_key, 'comment', None) is None
1148 node_key.comment = item_comment[:2]
1149 node_key.style = node_value.style = '?'
1150 if not (isinstance(node_key, ScalarNode) and not node_key.style):
1151 best_style = False
1152 if not (isinstance(node_value, ScalarNode) and not node_value.style):
1153 best_style = False
1154 value.append((node_key, node_value))
1155 best_style = best_style
1156 return node
1157
1158 def represent_dict(self, data):
1159 # type: (Any) -> Any
1160 """write out tag if saved on loading"""
1161 try:
1162 t = data.tag.value
1163 except AttributeError:
1164 t = None
1165 if t:
1166 if t.startswith('!!'):
1167 tag = 'tag:yaml.org,2002:' + t[2:]
1168 else:
1169 tag = t
1170 else:
1171 tag = u'tag:yaml.org,2002:map'
1172 return self.represent_mapping(tag, data)
1173
1174 def represent_list(self, data):
1175 # type: (Any) -> Any
1176 try:
1177 t = data.tag.value
1178 except AttributeError:
1179 t = None
1180 if t:
1181 if t.startswith('!!'):
1182 tag = 'tag:yaml.org,2002:' + t[2:]
1183 else:
1184 tag = t
1185 else:
1186 tag = u'tag:yaml.org,2002:seq'
1187 return self.represent_sequence(tag, data)
1188
1189 def represent_datetime(self, data):
1190 # type: (Any) -> Any
1191 inter = 'T' if data._yaml['t'] else ' '
1192 _yaml = data._yaml
1193 if _yaml['delta']:
1194 data += _yaml['delta']
1195 value = data.isoformat(inter)
1196 else:
1197 value = data.isoformat(inter)
1198 if _yaml['tz']:
1199 value += _yaml['tz']
1200 return self.represent_scalar(u'tag:yaml.org,2002:timestamp', to_unicode(value))
1201
1202 def represent_tagged_scalar(self, data):
1203 # type: (Any) -> Any
1204 try:
1205 tag = data.tag.value
1206 except AttributeError:
1207 tag = None
1208 try:
1209 anchor = data.yaml_anchor()
1210 except AttributeError:
1211 anchor = None
1212 return self.represent_scalar(tag, data.value, style=data.style, anchor=anchor)
1213
1214 def represent_scalar_bool(self, data):
1215 # type: (Any) -> Any
1216 try:
1217 anchor = data.yaml_anchor()
1218 except AttributeError:
1219 anchor = None
1220 return SafeRepresenter.represent_bool(self, data, anchor=anchor)
1221
1222
1223 RoundTripRepresenter.add_representer(type(None), RoundTripRepresenter.represent_none)
1224
1225 RoundTripRepresenter.add_representer(
1226 LiteralScalarString, RoundTripRepresenter.represent_literal_scalarstring
1227 )
1228
1229 RoundTripRepresenter.add_representer(
1230 FoldedScalarString, RoundTripRepresenter.represent_folded_scalarstring
1231 )
1232
1233 RoundTripRepresenter.add_representer(
1234 SingleQuotedScalarString, RoundTripRepresenter.represent_single_quoted_scalarstring
1235 )
1236
1237 RoundTripRepresenter.add_representer(
1238 DoubleQuotedScalarString, RoundTripRepresenter.represent_double_quoted_scalarstring
1239 )
1240
1241 RoundTripRepresenter.add_representer(
1242 PlainScalarString, RoundTripRepresenter.represent_plain_scalarstring
1243 )
1244
1245 RoundTripRepresenter.add_representer(ScalarInt, RoundTripRepresenter.represent_scalar_int)
1246
1247 RoundTripRepresenter.add_representer(BinaryInt, RoundTripRepresenter.represent_binary_int)
1248
1249 RoundTripRepresenter.add_representer(OctalInt, RoundTripRepresenter.represent_octal_int)
1250
1251 RoundTripRepresenter.add_representer(HexInt, RoundTripRepresenter.represent_hex_int)
1252
1253 RoundTripRepresenter.add_representer(HexCapsInt, RoundTripRepresenter.represent_hex_caps_int)
1254
1255 RoundTripRepresenter.add_representer(ScalarFloat, RoundTripRepresenter.represent_scalar_float)
1256
1257 RoundTripRepresenter.add_representer(ScalarBoolean, RoundTripRepresenter.represent_scalar_bool)
1258
1259 RoundTripRepresenter.add_representer(CommentedSeq, RoundTripRepresenter.represent_list)
1260
1261 RoundTripRepresenter.add_representer(CommentedMap, RoundTripRepresenter.represent_dict)
1262
1263 RoundTripRepresenter.add_representer(
1264 CommentedOrderedMap, RoundTripRepresenter.represent_ordereddict
1265 )
1266
1267 if sys.version_info >= (2, 7):
1268 import collections
1269
1270 RoundTripRepresenter.add_representer(
1271 collections.OrderedDict, RoundTripRepresenter.represent_ordereddict
1272 )
1273
1274 RoundTripRepresenter.add_representer(CommentedSet, RoundTripRepresenter.represent_set)
1275
1276 RoundTripRepresenter.add_representer(
1277 TaggedScalar, RoundTripRepresenter.represent_tagged_scalar
1278 )
1279
1280 RoundTripRepresenter.add_representer(TimeStamp, RoundTripRepresenter.represent_datetime)