codekingpro/portable-devtools
114k
1 2from __future__ import annotations3 4from ruamel.yaml.error import * # NOQA5from ruamel.yaml.nodes import * # NOQA6from ruamel.yaml.compat import ordereddict7from ruamel.yaml.compat import nprint, nprintf # NOQA8from ruamel.yaml.scalarstring import (9 LiteralScalarString,10 FoldedScalarString,11 SingleQuotedScalarString,12 DoubleQuotedScalarString,13 PlainScalarString,14)15from ruamel.yaml.comments import (16 CommentedMap,17 CommentedOrderedMap,18 CommentedSeq,19 CommentedKeySeq,20 CommentedKeyMap,21 CommentedSet,22 comment_attrib,23 merge_attrib,24 TaggedScalar,25)26from ruamel.yaml.scalarint import ScalarInt, BinaryInt, OctalInt, HexInt, HexCapsInt27from ruamel.yaml.scalarfloat import ScalarFloat28from ruamel.yaml.scalarbool import ScalarBoolean29from ruamel.yaml.timestamp import TimeStamp30from ruamel.yaml.anchor import Anchor31 32import collections33import datetime34import types35 36import copyreg37import base6438 39if False: # MYPY40 from typing import Dict, List, Any, Union, Text, Optional # NOQA41 42# fmt: off43__all__ = ['BaseRepresenter', 'SafeRepresenter', 'Representer',44 'RepresenterError', 'RoundTripRepresenter']45# fmt: on46 47 48class RepresenterError(YAMLError):49 pass50 51 52class BaseRepresenter:53 54 yaml_representers: Dict[Any, Any] = {}55 yaml_multi_representers: Dict[Any, Any] = {}56 57 def __init__(58 self: Any,59 default_style: Any = None,60 default_flow_style: Any = None,61 dumper: Any = None,62 ) -> None:63 self.dumper = dumper64 if self.dumper is not None:65 self.dumper._representer = self66 self.default_style = default_style67 self.default_flow_style = default_flow_style68 self.represented_objects: Dict[Any, Any] = {}69 self.object_keeper: List[Any] = []70 self.alias_key: Optional[int] = None71 self.sort_base_mapping_type_on_output = True72 73 @property74 def serializer(self) -> Any:75 try:76 if hasattr(self.dumper, 'typ'):77 return self.dumper.serializer78 return self.dumper._serializer79 except AttributeError:80 return self # cyaml81 82 def represent(self, data: Any) -> None:83 node = self.represent_data(data)84 self.serializer.serialize(node)85 self.represented_objects = {}86 self.object_keeper = []87 self.alias_key = None88 89 def represent_data(self, data: Any) -> Any:90 if self.ignore_aliases(data):91 self.alias_key = None92 else:93 self.alias_key = id(data)94 if self.alias_key is not None:95 if self.alias_key in self.represented_objects:96 node = self.represented_objects[self.alias_key]97 # if node is None:98 # raise RepresenterError(99 # f"recursive objects are not allowed: {data!r}")100 return node101 # self.represented_objects[alias_key] = None102 self.object_keeper.append(data)103 data_types = type(data).__mro__104 if data_types[0] in self.yaml_representers:105 node = self.yaml_representers[data_types[0]](self, data)106 else:107 for data_type in data_types:108 if data_type in self.yaml_multi_representers:109 node = self.yaml_multi_representers[data_type](self, data)110 break111 else:112 if None in self.yaml_multi_representers:113 node = self.yaml_multi_representers[None](self, data)114 elif None in self.yaml_representers:115 node = self.yaml_representers[None](self, data)116 else:117 node = ScalarNode(None, str(data))118 # if alias_key is not None:119 # self.represented_objects[alias_key] = node120 return node121 122 def represent_key(self, data: Any) -> Any:123 """124 David Fraser: Extract a method to represent keys in mappings, so that125 a subclass can choose not to quote them (for example)126 used in represent_mapping127 https://bitbucket.org/davidfraser/pyyaml/commits/d81df6eb95f20cac4a79eed95ae553b5c6f77b8c128 """129 return self.represent_data(data)130 131 @classmethod132 def add_representer(cls, data_type: Any, representer: Any) -> None:133 if 'yaml_representers' not in cls.__dict__:134 cls.yaml_representers = cls.yaml_representers.copy()135 cls.yaml_representers[data_type] = representer136 137 @classmethod138 def add_multi_representer(cls, data_type: Any, representer: Any) -> None:139 if 'yaml_multi_representers' not in cls.__dict__:140 cls.yaml_multi_representers = cls.yaml_multi_representers.copy()141 cls.yaml_multi_representers[data_type] = representer142 143 def represent_scalar(144 self, tag: Any, value: Any, style: Any = None, anchor: Any = None,145 ) -> ScalarNode:146 if style is None:147 style = self.default_style148 comment = None149 if style and style[0] in '|>':150 comment = getattr(value, 'comment', None)151 if comment:152 comment = [None, [comment]]153 if isinstance(tag, str):154 tag = Tag(suffix=tag)155 node = ScalarNode(tag, value, style=style, comment=comment, anchor=anchor)156 if self.alias_key is not None:157 self.represented_objects[self.alias_key] = node158 return node159 160 def represent_sequence(161 self, tag: Any, sequence: Any, flow_style: Any = None,162 ) -> SequenceNode:163 value: List[Any] = []164 if isinstance(tag, str):165 tag = Tag(suffix=tag)166 node = SequenceNode(tag, value, flow_style=flow_style)167 if self.alias_key is not None:168 self.represented_objects[self.alias_key] = node169 best_style = True170 for item in sequence:171 node_item = self.represent_data(item)172 if not (isinstance(node_item, ScalarNode) and not node_item.style):173 best_style = False174 value.append(node_item)175 if flow_style is None:176 if self.default_flow_style is not None:177 node.flow_style = self.default_flow_style178 else:179 node.flow_style = best_style180 return node181 182 def represent_omap(self, tag: Any, omap: Any, flow_style: Any = None) -> SequenceNode:183 value: List[Any] = []184 if isinstance(tag, str):185 tag = Tag(suffix=tag)186 node = SequenceNode(tag, value, flow_style=flow_style)187 if self.alias_key is not None:188 self.represented_objects[self.alias_key] = node189 best_style = True190 for item_key in omap:191 item_val = omap[item_key]192 node_item = self.represent_data({item_key: item_val})193 # if not (isinstance(node_item, ScalarNode) \194 # and not node_item.style):195 # best_style = False196 value.append(node_item)197 if flow_style is None:198 if self.default_flow_style is not None:199 node.flow_style = self.default_flow_style200 else:201 node.flow_style = best_style202 return node203 204 def represent_mapping(self, tag: Any, mapping: Any, flow_style: Any = None) -> MappingNode:205 value: List[Any] = []206 if isinstance(tag, str):207 tag = Tag(suffix=tag)208 node = MappingNode(tag, value, flow_style=flow_style)209 if self.alias_key is not None:210 self.represented_objects[self.alias_key] = node211 best_style = True212 if hasattr(mapping, 'items'):213 mapping = list(mapping.items())214 if self.sort_base_mapping_type_on_output:215 try:216 mapping = sorted(mapping)217 except TypeError:218 pass219 for item_key, item_value in mapping:220 node_key = self.represent_key(item_key)221 node_value = self.represent_data(item_value)222 if not (isinstance(node_key, ScalarNode) and not node_key.style):223 best_style = False224 if not (isinstance(node_value, ScalarNode) and not node_value.style):225 best_style = False226 value.append((node_key, node_value))227 if flow_style is None:228 if self.default_flow_style is not None:229 node.flow_style = self.default_flow_style230 else:231 node.flow_style = best_style232 return node233 234 def ignore_aliases(self, data: Any) -> bool:235 return False236 237 238class SafeRepresenter(BaseRepresenter):239 def ignore_aliases(self, data: Any) -> bool:240 # https://docs.python.org/3/reference/expressions.html#parenthesized-forms :241 # "i.e. two occurrences of the empty tuple may or may not yield the same object"242 # so "data is ()" should not be used243 if data is None or (isinstance(data, tuple) and data == ()):244 return True245 if isinstance(data, (bytes, str, bool, int, float)):246 return True247 return False248 249 def represent_none(self, data: Any) -> ScalarNode:250 return self.represent_scalar('tag:yaml.org,2002:null', 'null')251 252 def represent_str(self, data: Any) -> Any:253 return self.represent_scalar('tag:yaml.org,2002:str', data)254 255 def represent_binary(self, data: Any) -> ScalarNode:256 if hasattr(base64, 'encodebytes'):257 data = base64.encodebytes(data).decode('ascii')258 else:259 # check py2 only?260 data = base64.encodestring(data).decode('ascii') # type: ignore261 return self.represent_scalar('tag:yaml.org,2002:binary', data, style='|')262 263 def represent_bool(self, data: Any, anchor: Optional[Any] = None) -> ScalarNode:264 try:265 value = self.dumper.boolean_representation[bool(data)]266 except AttributeError:267 if data:268 value = 'true'269 else:270 value = 'false'271 return self.represent_scalar('tag:yaml.org,2002:bool', value, anchor=anchor)272 273 def represent_int(self, data: Any) -> ScalarNode:274 return self.represent_scalar('tag:yaml.org,2002:int', str(data))275 276 inf_value = 1e300277 while repr(inf_value) != repr(inf_value * inf_value):278 inf_value *= inf_value279 280 def represent_float(self, data: Any) -> ScalarNode:281 if data != data or (data == 0.0 and data == 1.0):282 value = '.nan'283 elif data == self.inf_value:284 value = '.inf'285 elif data == -self.inf_value:286 value = '-.inf'287 else:288 value = repr(data).lower()289 if getattr(self.serializer, 'use_version', None) == (1, 1):290 if '.' not in value and 'e' in value:291 # Note that in some cases `repr(data)` represents a float number292 # without the decimal parts. For instance:293 # >>> repr(1e17)294 # '1e17'295 # Unfortunately, this is not a valid float representation according296 # to the definition of the `!!float` tag in YAML 1.1. We fix297 # this by adding '.0' before the 'e' symbol.298 value = value.replace('e', '.0e', 1)299 return self.represent_scalar('tag:yaml.org,2002:float', value)300 301 def represent_list(self, data: Any) -> SequenceNode:302 # pairs = (len(data) > 0 and isinstance(data, list))303 # if pairs:304 # for item in data:305 # if not isinstance(item, tuple) or len(item) != 2:306 # pairs = False307 # break308 # if not pairs:309 return self.represent_sequence('tag:yaml.org,2002:seq', data)310 311 # value = []312 # for item_key, item_value in data:313 # value.append(self.represent_mapping('tag:yaml.org,2002:map',314 # [(item_key, item_value)]))315 # return SequenceNode('tag:yaml.org,2002:pairs', value)316 317 def represent_dict(self, data: Any) -> MappingNode:318 return self.represent_mapping('tag:yaml.org,2002:map', data)319 320 def represent_ordereddict(self, data: Any) -> SequenceNode:321 return self.represent_omap('tag:yaml.org,2002:omap', data)322 323 def represent_set(self, data: Any) -> MappingNode:324 value: Dict[Any, None] = {}325 for key in data:326 value[key] = None327 return self.represent_mapping('tag:yaml.org,2002:set', value)328 329 def represent_date(self, data: Any) -> ScalarNode:330 value = data.isoformat()331 return self.represent_scalar('tag:yaml.org,2002:timestamp', value)332 333 def represent_datetime(self, data: Any) -> ScalarNode:334 value = data.isoformat(' ')335 return self.represent_scalar('tag:yaml.org,2002:timestamp', value)336 337 def represent_yaml_object(338 self, tag: Any, data: Any, cls: Any, flow_style: Any = None,339 ) -> MappingNode:340 if hasattr(data, '__getstate__'):341 state = data.__getstate__()342 else:343 state = data.__dict__.copy()344 return self.represent_mapping(tag, state, flow_style=flow_style)345 346 def represent_undefined(self, data: Any) -> None:347 raise RepresenterError(f'cannot represent an object: {data!r}')348 349 350SafeRepresenter.add_representer(type(None), SafeRepresenter.represent_none)351 352SafeRepresenter.add_representer(str, SafeRepresenter.represent_str)353 354SafeRepresenter.add_representer(bytes, SafeRepresenter.represent_binary)355 356SafeRepresenter.add_representer(bool, SafeRepresenter.represent_bool)357 358SafeRepresenter.add_representer(int, SafeRepresenter.represent_int)359 360SafeRepresenter.add_representer(float, SafeRepresenter.represent_float)361 362SafeRepresenter.add_representer(list, SafeRepresenter.represent_list)363 364SafeRepresenter.add_representer(tuple, SafeRepresenter.represent_list)365 366SafeRepresenter.add_representer(dict, SafeRepresenter.represent_dict)367 368SafeRepresenter.add_representer(set, SafeRepresenter.represent_set)369 370SafeRepresenter.add_representer(ordereddict, SafeRepresenter.represent_ordereddict)371 372SafeRepresenter.add_representer(373 collections.OrderedDict, SafeRepresenter.represent_ordereddict,374)375 376SafeRepresenter.add_representer(datetime.date, SafeRepresenter.represent_date)377 378SafeRepresenter.add_representer(datetime.datetime, SafeRepresenter.represent_datetime)379 380SafeRepresenter.add_representer(None, SafeRepresenter.represent_undefined)381 382 383class Representer(SafeRepresenter):384 def represent_complex(self, data: Any) -> Any:385 if data.imag == 0.0:386 data = repr(data.real)387 elif data.real == 0.0:388 data = f'{data.imag!r}j'389 elif data.imag > 0:390 data = f'{data.real!r}+{data.imag!r}j'391 else:392 data = f'{data.real!r}{data.imag!r}j'393 return self.represent_scalar('tag:yaml.org,2002:python/complex', data)394 395 def represent_tuple(self, data: Any) -> SequenceNode:396 return self.represent_sequence('tag:yaml.org,2002:python/tuple', data)397 398 def represent_name(self, data: Any) -> ScalarNode:399 try:400 name = f'{data.__module__!s}.{data.__qualname__!s}'401 except AttributeError:402 # ToDo: check if this can be reached in Py3403 name = f'{data.__module__!s}.{data.__name__!s}'404 return self.represent_scalar('tag:yaml.org,2002:python/name:' + name, "")405 406 def represent_module(self, data: Any) -> ScalarNode:407 return self.represent_scalar('tag:yaml.org,2002:python/module:' + data.__name__, "")408 409 def represent_object(self, data: Any) -> Union[SequenceNode, MappingNode]:410 # We use __reduce__ API to save the data. data.__reduce__ returns411 # a tuple of length 2-5:412 # (function, args, state, listitems, dictitems)413 414 # For reconstructing, we calls function(*args), then set its state,415 # listitems, and dictitems if they are not None.416 417 # A special case is when function.__name__ == '__newobj__'. In this418 # case we create the object with args[0].__new__(*args).419 420 # Another special case is when __reduce__ returns a string - we don't421 # support it.422 423 # We produce a !!python/object, !!python/object/new or424 # !!python/object/apply node.425 426 cls = type(data)427 if cls in copyreg.dispatch_table:428 reduce: Any = copyreg.dispatch_table[cls](data)429 elif hasattr(data, '__reduce_ex__'):430 reduce = data.__reduce_ex__(2)431 elif hasattr(data, '__reduce__'):432 reduce = data.__reduce__()433 else:434 raise RepresenterError(f'cannot represent object: {data!r}')435 reduce = (list(reduce) + [None] * 5)[:5]436 function, args, state, listitems, dictitems = reduce437 args = list(args)438 if state is None:439 state = {}440 if listitems is not None:441 listitems = list(listitems)442 if dictitems is not None:443 dictitems = dict(dictitems)444 if function.__name__ == '__newobj__':445 function = args[0]446 args = args[1:]447 tag = 'tag:yaml.org,2002:python/object/new:'448 newobj = True449 else:450 tag = 'tag:yaml.org,2002:python/object/apply:'451 newobj = False452 try:453 function_name = f'{function.__module__!s}.{function.__qualname__!s}'454 except AttributeError:455 # ToDo: check if this can be reached in Py3456 function_name = f'{function.__module__!s}.{function.__name__!s}'457 if not args and not listitems and not dictitems and isinstance(state, dict) and newobj:458 return self.represent_mapping(459 'tag:yaml.org,2002:python/object:' + function_name, state,460 )461 if not listitems and not dictitems and isinstance(state, dict) and not state:462 return self.represent_sequence(tag + function_name, args)463 value = {}464 if args:465 value['args'] = args466 if state or not isinstance(state, dict):467 value['state'] = state468 if listitems:469 value['listitems'] = listitems470 if dictitems:471 value['dictitems'] = dictitems472 return self.represent_mapping(tag + function_name, value)473 474 475Representer.add_representer(complex, Representer.represent_complex)476 477Representer.add_representer(tuple, Representer.represent_tuple)478 479Representer.add_representer(type, Representer.represent_name)480 481Representer.add_representer(types.FunctionType, Representer.represent_name)482 483Representer.add_representer(types.BuiltinFunctionType, Representer.represent_name)484 485Representer.add_representer(types.ModuleType, Representer.represent_module)486 487Representer.add_multi_representer(object, Representer.represent_object)488 489Representer.add_multi_representer(type, Representer.represent_name)490 491 492class RoundTripRepresenter(SafeRepresenter):493 # need to add type here and write out the .comment494 # in serializer and emitter495 496 def __init__(497 self, default_style: Any = None, default_flow_style: Any = None, dumper: Any = None,498 ) -> None:499 if not hasattr(dumper, 'typ') and default_flow_style is None:500 default_flow_style = False501 SafeRepresenter.__init__(502 self,503 default_style=default_style,504 default_flow_style=default_flow_style,505 dumper=dumper,506 )507 508 def ignore_aliases(self, data: Any) -> bool:509 try:510 if data.anchor is not None and data.anchor.value is not None:511 return False512 except AttributeError:513 pass514 return SafeRepresenter.ignore_aliases(self, data)515 516 def represent_none(self, data: Any) -> ScalarNode:517 if len(self.represented_objects) == 0 and not self.serializer.use_explicit_start:518 # this will be open ended (although it is not yet)519 return self.represent_scalar('tag:yaml.org,2002:null', 'null')520 return self.represent_scalar('tag:yaml.org,2002:null', "")521 522 def represent_literal_scalarstring(self, data: Any) -> ScalarNode:523 tag = None524 style = '|'525 anchor = data.yaml_anchor(any=True)526 tag = 'tag:yaml.org,2002:str'527 return self.represent_scalar(tag, data, style=style, anchor=anchor)528 529 represent_preserved_scalarstring = represent_literal_scalarstring530 531 def represent_folded_scalarstring(self, data: Any) -> ScalarNode:532 tag = None533 style = '>'534 anchor = data.yaml_anchor(any=True)535 for fold_pos in reversed(getattr(data, 'fold_pos', [])):536 if (537 data[fold_pos] == ' '538 and (fold_pos > 0 and not data[fold_pos - 1].isspace())539 and (fold_pos < len(data) and not data[fold_pos + 1].isspace())540 ):541 data = data[:fold_pos] + '\a' + data[fold_pos:]542 tag = 'tag:yaml.org,2002:str'543 return self.represent_scalar(tag, data, style=style, anchor=anchor)544 545 def represent_single_quoted_scalarstring(self, data: Any) -> ScalarNode:546 tag = None547 style = "'"548 anchor = data.yaml_anchor(any=True)549 tag = 'tag:yaml.org,2002:str'550 return self.represent_scalar(tag, data, style=style, anchor=anchor)551 552 def represent_double_quoted_scalarstring(self, data: Any) -> ScalarNode:553 tag = None554 style = '"'555 anchor = data.yaml_anchor(any=True)556 tag = 'tag:yaml.org,2002:str'557 return self.represent_scalar(tag, data, style=style, anchor=anchor)558 559 def represent_plain_scalarstring(self, data: Any) -> ScalarNode:560 tag = None561 style = ''562 anchor = data.yaml_anchor(any=True)563 tag = 'tag:yaml.org,2002:str'564 return self.represent_scalar(tag, data, style=style, anchor=anchor)565 566 def insert_underscore(567 self, prefix: Any, s: Any, underscore: Any, anchor: Any = None,568 ) -> ScalarNode:569 if underscore is None:570 return self.represent_scalar('tag:yaml.org,2002:int', prefix + s, anchor=anchor)571 if underscore[0]:572 sl = list(s)573 pos = len(s) - underscore[0]574 while pos > 0:575 sl.insert(pos, '_')576 pos -= underscore[0]577 s = "".join(sl)578 if underscore[1]:579 s = '_' + s580 if underscore[2]:581 s += '_'582 return self.represent_scalar('tag:yaml.org,2002:int', prefix + s, anchor=anchor)583 584 def represent_scalar_int(self, data: Any) -> ScalarNode:585 if data._width is not None:586 s = f'{data:0{data._width}d}'587 else:588 s = format(data, 'd')589 anchor = data.yaml_anchor(any=True)590 return self.insert_underscore("", s, data._underscore, anchor=anchor)591 592 def represent_binary_int(self, data: Any) -> ScalarNode:593 if data._width is not None:594 # cannot use '{:#0{}b}', that strips the zeros595 s = f'{data:0{data._width}b}'596 else:597 s = format(data, 'b')598 anchor = data.yaml_anchor(any=True)599 return self.insert_underscore('0b', s, data._underscore, anchor=anchor)600 601 def represent_octal_int(self, data: Any) -> ScalarNode:602 if data._width is not None:603 # cannot use '{:#0{}o}', that strips the zeros604 s = f'{data:0{data._width}o}'605 else:606 s = format(data, 'o')607 anchor = data.yaml_anchor(any=True)608 prefix = '0o'609 if getattr(self.serializer, 'use_version', None) == (1, 1):610 prefix = '0'611 return self.insert_underscore(prefix, s, data._underscore, anchor=anchor)612 613 def represent_hex_int(self, data: Any) -> ScalarNode:614 if data._width is not None:615 # cannot use '{:#0{}x}', that strips the zeros616 s = f'{data:0{data._width}x}'617 else:618 s = format(data, 'x')619 anchor = data.yaml_anchor(any=True)620 return self.insert_underscore('0x', s, data._underscore, anchor=anchor)621 622 def represent_hex_caps_int(self, data: Any) -> ScalarNode:623 if data._width is not None:624 # cannot use '{:#0{}X}', that strips the zeros625 s = f'{data:0{data._width}X}'626 else:627 s = format(data, 'X')628 anchor = data.yaml_anchor(any=True)629 return self.insert_underscore('0x', s, data._underscore, anchor=anchor)630 631 def represent_scalar_float(self, data: Any) -> ScalarNode:632 """ this is way more complicated """633 value = None634 anchor = data.yaml_anchor(any=True)635 if data != data or (data == 0.0 and data == 1.0):636 value = '.nan'637 elif data == self.inf_value:638 value = '.inf'639 elif data == -self.inf_value:640 value = '-.inf'641 if value:642 return self.represent_scalar('tag:yaml.org,2002:float', value, anchor=anchor)643 if data._exp is None and data._prec > 0 and data._prec == data._width - 1:644 # no exponent, but trailing dot645 value = f'{data._m_sign if data._m_sign else ""}{abs(int(data)):d}.'646 elif data._exp is None:647 # no exponent, "normal" dot648 prec = data._prec649 ms = data._m_sign if data._m_sign else ""650 if prec < 0:651 value = f'{ms}{abs(int(data)):0{data._width - len(ms)}d}'652 else:653 # -1 for the dot654 value = f'{ms}{abs(data):0{data._width - len(ms)}.{data._width - prec - 1}f}'655 if prec == 0 or (prec == 1 and ms != ""):656 value = value.replace('0.', '.')657 while len(value) < data._width:658 value += '0'659 else:660 # exponent661 (662 m,663 es,664 ) = f'{data:{data._width}.{data._width + (1 if data._m_sign else 0)}e}'.split('e')665 w = data._width if data._prec > 0 else (data._width + 1)666 if data < 0:667 w += 1668 m = m[:w]669 e = int(es)670 m1, m2 = m.split('.') # always second?671 while len(m1) + len(m2) < data._width - (1 if data._prec >= 0 else 0):672 m2 += '0'673 if data._m_sign and data > 0:674 m1 = '+' + m1675 esgn = '+' if data._e_sign else ""676 if data._prec < 0: # mantissa without dot677 if m2 != '0':678 e -= len(m2)679 else:680 m2 = ""681 while (len(m1) + len(m2) - (1 if data._m_sign else 0)) < data._width:682 m2 += '0'683 e -= 1684 value = m1 + m2 + data._exp + f'{e:{esgn}0{data._e_width}d}'685 elif data._prec == 0: # mantissa with trailing dot686 e -= len(m2)687 value = m1 + m2 + '.' + data._exp + f'{e:{esgn}0{data._e_width}d}'688 else:689 if data._m_lead0 > 0:690 m2 = '0' * (data._m_lead0 - 1) + m1 + m2691 m1 = '0'692 m2 = m2[: -data._m_lead0] # these should be zeros693 e += data._m_lead0694 while len(m1) < data._prec:695 m1 += m2[0]696 m2 = m2[1:]697 e -= 1698 value = m1 + '.' + m2 + data._exp + f'{e:{esgn}0{data._e_width}d}'699 700 if value is None:701 value = repr(data).lower()702 return self.represent_scalar('tag:yaml.org,2002:float', value, anchor=anchor)703 704 def represent_sequence(705 self, tag: Any, sequence: Any, flow_style: Any = None,706 ) -> SequenceNode:707 value: List[Any] = []708 # if the flow_style is None, the flow style tacked on to the object709 # explicitly will be taken. If that is None as well the default flow710 # style rules711 try:712 flow_style = sequence.fa.flow_style(flow_style)713 except AttributeError:714 flow_style = flow_style715 try:716 anchor = sequence.yaml_anchor()717 except AttributeError:718 anchor = None719 if isinstance(tag, str):720 tag = Tag(suffix=tag)721 node = SequenceNode(tag, value, flow_style=flow_style, anchor=anchor)722 if self.alias_key is not None:723 self.represented_objects[self.alias_key] = node724 best_style = True725 try:726 comment = getattr(sequence, comment_attrib)727 node.comment = comment.comment728 # reset any comment already printed information729 if node.comment and node.comment[1]:730 for ct in node.comment[1]:731 ct.reset()732 item_comments = comment.items733 for v in item_comments.values():734 if v and v[1]:735 for ct in v[1]:736 ct.reset()737 item_comments = comment.items738 if node.comment is None:739 node.comment = comment.comment740 else:741 # as we are potentially going to extend this, make a new list742 node.comment = comment.comment[:]743 try:744 node.comment.append(comment.end)745 except AttributeError:746 pass747 except AttributeError:748 item_comments = {}749 for idx, item in enumerate(sequence):750 node_item = self.represent_data(item)751 self.merge_comments(node_item, item_comments.get(idx))752 if not (isinstance(node_item, ScalarNode) and not node_item.style):753 best_style = False754 value.append(node_item)755 if flow_style is None:756 if len(sequence) != 0 and self.default_flow_style is not None:757 node.flow_style = self.default_flow_style758 else:759 node.flow_style = best_style760 return node761 762 def merge_comments(self, node: Any, comments: Any) -> Any:763 if comments is None:764 assert hasattr(node, 'comment')765 return node766 if getattr(node, 'comment', None) is not None:767 for idx, val in enumerate(comments):768 if idx >= len(node.comment):769 continue770 nc = node.comment[idx]771 if nc is not None:772 assert val is None or val == nc773 comments[idx] = nc774 node.comment = comments775 return node776 777 def represent_key(self, data: Any) -> Any:778 if isinstance(data, CommentedKeySeq):779 self.alias_key = None780 return self.represent_sequence('tag:yaml.org,2002:seq', data, flow_style=True)781 if isinstance(data, CommentedKeyMap):782 self.alias_key = None783 return self.represent_mapping('tag:yaml.org,2002:map', data, flow_style=True)784 return SafeRepresenter.represent_key(self, data)785 786 def represent_mapping(self, tag: Any, mapping: Any, flow_style: Any = None) -> MappingNode:787 value: List[Any] = []788 try:789 flow_style = mapping.fa.flow_style(flow_style)790 except AttributeError:791 flow_style = flow_style792 try:793 anchor = mapping.yaml_anchor()794 except AttributeError:795 anchor = None796 if isinstance(tag, str):797 tag = Tag(suffix=tag)798 node = MappingNode(tag, value, flow_style=flow_style, anchor=anchor)799 if self.alias_key is not None:800 self.represented_objects[self.alias_key] = node801 best_style = True802 # no sorting! !!803 try:804 comment = getattr(mapping, comment_attrib)805 if node.comment is None:806 node.comment = comment.comment807 else:808 # as we are potentially going to extend this, make a new list809 node.comment = comment.comment[:]810 if node.comment and node.comment[1]:811 for ct in node.comment[1]:812 ct.reset()813 item_comments = comment.items814 if self.dumper.comment_handling is None:815 for v in item_comments.values():816 if v and v[1]:817 for ct in v[1]:818 ct.reset()819 try:820 node.comment.append(comment.end)821 except AttributeError:822 pass823 else:824 # NEWCMNT825 pass826 except AttributeError:827 item_comments = {}828 merge_value = getattr(mapping, merge_attrib, [])829 # merge_list = [m[1] for m in merge_value]830 # merge_list = [m for m in merge_value]831 try:832 # merge_pos = getattr(mapping, merge_attrib, [[0]])[0][0]833 # print('merge_pos', merge_pos, merge_value.merge_pos)834 merge_pos = merge_value.merge_pos # type: ignore835 except (AttributeError, IndexError):836 merge_pos = 0837 item_count = 0838 # if bool(merge_list):839 if len(merge_value) > 0:840 items = mapping.non_merged_items()841 else:842 items = mapping.items()843 for item_key, item_value in items:844 item_count += 1845 node_key = self.represent_key(item_key)846 node_value = self.represent_data(item_value)847 item_comment = item_comments.get(item_key)848 if item_comment:849 # assert getattr(node_key, 'comment', None) is None850 # issue 351 did throw this because the comment from the list item was851 # moved to the dict852 node_key.comment = item_comment[:2]853 nvc = getattr(node_value, 'comment', None)854 if nvc is not None: # end comment already there855 nvc[0] = item_comment[2]856 nvc[1] = item_comment[3]857 else:858 node_value.comment = item_comment[2:]859 if not (isinstance(node_key, ScalarNode) and not node_key.style):860 best_style = False861 if not (isinstance(node_value, ScalarNode) and not node_value.style):862 best_style = False863 value.append((node_key, node_value))864 if flow_style is None:865 if ((item_count != 0) or (len(merge_value) > 0)) and self.default_flow_style is not None: # NOQA866 node.flow_style = self.default_flow_style867 else:868 node.flow_style = best_style869 if len(merge_value) > 0:870 # because of the call to represent_data here, the anchors871 # are marked as being used and thereby created872 # if len(merge_list) == 1:873 if merge_value.sequence is None: # type: ignore874 arg = self.represent_data(merge_value[0])875 else:876 # arg = self.represent_data(merge_list)877 # arg.flow_style = True878 arg = self.represent_data(merge_value.sequence) # type: ignore879 value.insert(880 merge_pos, (ScalarNode(Tag(suffix='tag:yaml.org,2002:merge'), '<<'), arg),881 )882 return node883 884 def represent_omap(self, tag: Any, omap: Any, flow_style: Any = None) -> SequenceNode:885 value: List[Any] = []886 try:887 flow_style = omap.fa.flow_style(flow_style)888 except AttributeError:889 flow_style = flow_style890 try:891 anchor = omap.yaml_anchor()892 except AttributeError:893 anchor = None894 if isinstance(tag, str):895 tag = Tag(suffix=tag)896 node = SequenceNode(tag, value, flow_style=flow_style, anchor=anchor)897 if self.alias_key is not None:898 self.represented_objects[self.alias_key] = node899 best_style = True900 try:901 comment = getattr(omap, comment_attrib)902 if node.comment is None:903 node.comment = comment.comment904 else:905 # as we are potentially going to extend this, make a new list906 node.comment = comment.comment[:]907 if node.comment and node.comment[1]:908 for ct in node.comment[1]:909 ct.reset()910 item_comments = comment.items911 for v in item_comments.values():912 if v and v[1]:913 for ct in v[1]:914 ct.reset()915 try:916 node.comment.append(comment.end)917 except AttributeError:918 pass919 except AttributeError:920 item_comments = {}921 for item_key in omap:922 item_val = omap[item_key]923 node_item = self.represent_data({item_key: item_val})924 # node_item.flow_style = False925 # node item has two scalars in value: node_key and node_value926 item_comment = item_comments.get(item_key)927 if item_comment:928 if item_comment[1]:929 node_item.comment = [None, item_comment[1]]930 assert getattr(node_item.value[0][0], 'comment', None) is None931 node_item.value[0][0].comment = [item_comment[0], None]932 nvc = getattr(node_item.value[0][1], 'comment', None)933 if nvc is not None: # end comment already there934 nvc[0] = item_comment[2]935 nvc[1] = item_comment[3]936 else:937 node_item.value[0][1].comment = item_comment[2:]938 # if not (isinstance(node_item, ScalarNode) \939 # and not node_item.style):940 # best_style = False941 value.append(node_item)942 if flow_style is None:943 if self.default_flow_style is not None:944 node.flow_style = self.default_flow_style945 else:946 node.flow_style = best_style947 return node948 949 def represent_set(self, setting: Any) -> MappingNode:950 flow_style = False951 tag = Tag(suffix='tag:yaml.org,2002:set')952 # return self.represent_mapping(tag, value)953 value: List[Any] = []954 flow_style = setting.fa.flow_style(flow_style)955 try:956 anchor = setting.yaml_anchor()957 except AttributeError:958 anchor = None959 node = MappingNode(tag, value, flow_style=flow_style, anchor=anchor)960 if self.alias_key is not None:961 self.represented_objects[self.alias_key] = node962 best_style = True963 # no sorting! !!964 try:965 comment = getattr(setting, comment_attrib)966 if node.comment is None:967 node.comment = comment.comment968 else:969 # as we are potentially going to extend this, make a new list970 node.comment = comment.comment[:]971 if node.comment and node.comment[1]:972 for ct in node.comment[1]:973 ct.reset()974 item_comments = comment.items975 for v in item_comments.values():976 if v and v[1]:977 for ct in v[1]:978 ct.reset()979 try:980 node.comment.append(comment.end)981 except AttributeError:982 pass983 except AttributeError:984 item_comments = {}985 for item_key in setting.odict:986 node_key = self.represent_key(item_key)987 node_value = self.represent_data(None)988 item_comment = item_comments.get(item_key)989 if item_comment:990 assert getattr(node_key, 'comment', None) is None991 node_key.comment = item_comment[:2]992 node_key.style = '?'993 node_value.style = '-' if flow_style else '?'994 if not (isinstance(node_key, ScalarNode) and not node_key.style):995 best_style = False996 if not (isinstance(node_value, ScalarNode) and not node_value.style):997 best_style = False998 value.append((node_key, node_value))999 best_style = best_style1000 return node1001 1002 def represent_dict(self, data: Any) -> MappingNode:1003 """write out tag if saved on loading"""1004 try:1005 _ = data.tag1006 except AttributeError:1007 tag = Tag(suffix='tag:yaml.org,2002:map')1008 else:1009 if data.tag.trval:1010 if data.tag.startswith('!!'):1011 tag = Tag(suffix='tag:yaml.org,2002:' + data.tag.trval[2:])1012 else:1013 tag = data.tag1014 else:1015 tag = Tag(suffix='tag:yaml.org,2002:map')1016 return self.represent_mapping(tag, data)1017 1018 def represent_list(self, data: Any) -> SequenceNode:1019 try:1020 _ = data.tag1021 except AttributeError:1022 tag = Tag(suffix='tag:yaml.org,2002:seq')1023 else:1024 if data.tag.trval:1025 if data.tag.startswith('!!'):1026 tag = Tag(suffix='tag:yaml.org,2002:' + data.tag.trval[2:])1027 else:1028 tag = data.tag1029 else:1030 tag = Tag(suffix='tag:yaml.org,2002:seq')1031 return self.represent_sequence(tag, data)1032 1033 def represent_datetime(self, data: Any) -> ScalarNode:1034 inter = 'T' if data._yaml['t'] else ' '1035 _yaml = data._yaml1036 if False and _yaml['delta']:1037 data += _yaml['delta']1038 value = data.isoformat(inter)1039 else:1040 value = data.isoformat(inter).strip()1041 if False and _yaml['tz']:1042 value += _yaml['tz']1043 if data.tzinfo and str(data.tzinfo):1044 if value[-6] in '+-':1045 value = value[:-6] + str(data.tzinfo)1046 return self.represent_scalar('tag:yaml.org,2002:timestamp', value)1047 1048 def represent_tagged_scalar(self, data: Any) -> ScalarNode:1049 try:1050 if data.tag.handle == '!!':1051 tag = f'{data.tag.handle} {data.tag.suffix}'1052 else:1053 tag = data.tag1054 except AttributeError:1055 tag = None1056 try:1057 anchor = data.yaml_anchor()1058 except AttributeError:1059 anchor = None1060 return self.represent_scalar(tag, data.value, style=data.style, anchor=anchor)1061 1062 def represent_scalar_bool(self, data: Any) -> ScalarNode:1063 try:1064 anchor = data.yaml_anchor()1065 except AttributeError:1066 anchor = None1067 return SafeRepresenter.represent_bool(self, data, anchor=anchor)1068 1069 def represent_yaml_object(1070 self, tag: Any, data: Any, cls: Any, flow_style: Optional[Any] = None,1071 ) -> MappingNode:1072 if hasattr(data, '__getstate__'):1073 state = data.__getstate__()1074 else:1075 state = data.__dict__.copy()1076 anchor = state.pop(Anchor.attrib, None)1077 res = self.represent_mapping(tag, state, flow_style=flow_style)1078 if anchor is not None:1079 res.anchor = anchor1080 return res1081 1082 1083RoundTripRepresenter.add_representer(type(None), RoundTripRepresenter.represent_none)1084 1085RoundTripRepresenter.add_representer(1086 LiteralScalarString, RoundTripRepresenter.represent_literal_scalarstring,1087)1088 1089RoundTripRepresenter.add_representer(1090 FoldedScalarString, RoundTripRepresenter.represent_folded_scalarstring,1091)1092 1093RoundTripRepresenter.add_representer(1094 SingleQuotedScalarString, RoundTripRepresenter.represent_single_quoted_scalarstring,1095)1096 1097RoundTripRepresenter.add_representer(1098 DoubleQuotedScalarString, RoundTripRepresenter.represent_double_quoted_scalarstring,1099)1100 1101RoundTripRepresenter.add_representer(1102 PlainScalarString, RoundTripRepresenter.represent_plain_scalarstring,1103)1104 1105# RoundTripRepresenter.add_representer(tuple, Representer.represent_tuple)1106 1107RoundTripRepresenter.add_representer(ScalarInt, RoundTripRepresenter.represent_scalar_int)1108 1109RoundTripRepresenter.add_representer(BinaryInt, RoundTripRepresenter.represent_binary_int)1110 1111RoundTripRepresenter.add_representer(OctalInt, RoundTripRepresenter.represent_octal_int)1112 1113RoundTripRepresenter.add_representer(HexInt, RoundTripRepresenter.represent_hex_int)1114 1115RoundTripRepresenter.add_representer(HexCapsInt, RoundTripRepresenter.represent_hex_caps_int)1116 1117RoundTripRepresenter.add_representer(ScalarFloat, RoundTripRepresenter.represent_scalar_float)1118 1119RoundTripRepresenter.add_representer(ScalarBoolean, RoundTripRepresenter.represent_scalar_bool)1120 1121RoundTripRepresenter.add_representer(CommentedSeq, RoundTripRepresenter.represent_list)1122 1123RoundTripRepresenter.add_representer(CommentedMap, RoundTripRepresenter.represent_dict)1124 1125RoundTripRepresenter.add_representer(1126 CommentedOrderedMap, RoundTripRepresenter.represent_ordereddict,1127)1128 1129RoundTripRepresenter.add_representer(1130 collections.OrderedDict, RoundTripRepresenter.represent_ordereddict,1131)1132 1133RoundTripRepresenter.add_representer(CommentedSet, RoundTripRepresenter.represent_set)1134 1135RoundTripRepresenter.add_representer(1136 TaggedScalar, RoundTripRepresenter.represent_tagged_scalar,1137)1138 1139RoundTripRepresenter.add_representer(TimeStamp, RoundTripRepresenter.represent_datetime)1140 