codekingpro/portable-devtools
114k
1 2from __future__ import annotations3 4from ruamel.yaml.anchor import Anchor5 6if False: # MYPY7 from typing import Text, Any, Dict, List # NOQA8 from ruamel.yaml.compat import SupportsIndex9 10__all__ = [11 'ScalarString',12 'LiteralScalarString',13 'FoldedScalarString',14 'SingleQuotedScalarString',15 'DoubleQuotedScalarString',16 'PlainScalarString',17 # PreservedScalarString is the old name, as it was the first to be preserved on rt,18 # use LiteralScalarString instead19 'PreservedScalarString',20]21 22 23class ScalarString(str):24 __slots__ = Anchor.attrib25 26 def __new__(cls, *args: Any, **kw: Any) -> Any:27 anchor = kw.pop('anchor', None)28 ret_val = str.__new__(cls, *args, **kw)29 if anchor is not None:30 ret_val.yaml_set_anchor(anchor, always_dump=True)31 return ret_val32 33 def replace(self, old: Any, new: Any, maxreplace: SupportsIndex = -1) -> Any:34 return type(self)((str.replace(self, old, new, maxreplace)))35 36 @property37 def anchor(self) -> Any:38 if not hasattr(self, Anchor.attrib):39 setattr(self, Anchor.attrib, Anchor())40 return getattr(self, Anchor.attrib)41 42 def yaml_anchor(self, any: bool = False) -> Any:43 if not hasattr(self, Anchor.attrib):44 return None45 if any or self.anchor.always_dump:46 return self.anchor47 return None48 49 def yaml_set_anchor(self, value: Any, always_dump: bool = False) -> None:50 self.anchor.value = value51 self.anchor.always_dump = always_dump52 53 54class LiteralScalarString(ScalarString):55 __slots__ = 'comment' # the comment after the | on the first line56 57 style = '|'58 59 def __new__(cls, value: Text, anchor: Any = None) -> Any:60 return ScalarString.__new__(cls, value, anchor=anchor)61 62 63PreservedScalarString = LiteralScalarString64 65 66class FoldedScalarString(ScalarString):67 __slots__ = ('fold_pos', 'comment') # the comment after the > on the first line68 69 style = '>'70 71 def __new__(cls, value: Text, anchor: Any = None) -> Any:72 return ScalarString.__new__(cls, value, anchor=anchor)73 74 75class SingleQuotedScalarString(ScalarString):76 __slots__ = ()77 78 style = "'"79 80 def __new__(cls, value: Text, anchor: Any = None) -> Any:81 return ScalarString.__new__(cls, value, anchor=anchor)82 83 84class DoubleQuotedScalarString(ScalarString):85 __slots__ = ()86 87 style = '"'88 89 def __new__(cls, value: Text, anchor: Any = None) -> Any:90 return ScalarString.__new__(cls, value, anchor=anchor)91 92 93class PlainScalarString(ScalarString):94 __slots__ = ()95 96 style = ''97 98 def __new__(cls, value: Text, anchor: Any = None) -> Any:99 return ScalarString.__new__(cls, value, anchor=anchor)100 101 102def preserve_literal(s: Text) -> Text:103 return LiteralScalarString(s.replace('\r\n', '\n').replace('\r', '\n'))104 105 106def walk_tree(base: Any, map: Any = None) -> None:107 """108 the routine here walks over a simple yaml tree (recursing in109 dict values and list items) and converts strings that110 have multiple lines to literal scalars111 112 You can also provide an explicit (ordered) mapping for multiple transforms113 (first of which is executed):114 map = ruamel.yaml.compat.ordereddict115 map['\n'] = preserve_literal116 map[':'] = SingleQuotedScalarString117 walk_tree(data, map=map)118 """119 from collections.abc import MutableMapping, MutableSequence120 121 if map is None:122 map = {'\n': preserve_literal}123 124 if isinstance(base, MutableMapping):125 for k in base:126 v: Text = base[k]127 if isinstance(v, str):128 for ch in map:129 if ch in v:130 base[k] = map[ch](v)131 break132 else:133 walk_tree(v, map=map)134 elif isinstance(base, MutableSequence):135 for idx, elem in enumerate(base):136 if isinstance(elem, str):137 for ch in map:138 if ch in elem:139 base[idx] = map[ch](elem)140 break141 else:142 walk_tree(elem, map=map)143 