scalarstring.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. # coding: utf-8
  2. from __future__ import print_function, absolute_import, division, unicode_literals
  3. from ruamel.yaml.compat import text_type
  4. from ruamel.yaml.anchor import Anchor
  5. if False: # MYPY
  6. from typing import Text, Any, Dict, List # NOQA
  7. __all__ = [
  8. 'ScalarString',
  9. 'LiteralScalarString',
  10. 'FoldedScalarString',
  11. 'SingleQuotedScalarString',
  12. 'DoubleQuotedScalarString',
  13. 'PlainScalarString',
  14. # PreservedScalarString is the old name, as it was the first to be preserved on rt,
  15. # use LiteralScalarString instead
  16. 'PreservedScalarString',
  17. ]
  18. class ScalarString(text_type):
  19. __slots__ = Anchor.attrib
  20. def __new__(cls, *args, **kw):
  21. # type: (Any, Any) -> Any
  22. anchor = kw.pop('anchor', None) # type: ignore
  23. ret_val = text_type.__new__(cls, *args, **kw) # type: ignore
  24. if anchor is not None:
  25. ret_val.yaml_set_anchor(anchor, always_dump=True)
  26. return ret_val
  27. def replace(self, old, new, maxreplace=-1):
  28. # type: (Any, Any, int) -> Any
  29. return type(self)((text_type.replace(self, old, new, maxreplace)))
  30. @property
  31. def anchor(self):
  32. # type: () -> Any
  33. if not hasattr(self, Anchor.attrib):
  34. setattr(self, Anchor.attrib, Anchor())
  35. return getattr(self, Anchor.attrib)
  36. def yaml_anchor(self, any=False):
  37. # type: (bool) -> Any
  38. if not hasattr(self, Anchor.attrib):
  39. return None
  40. if any or self.anchor.always_dump:
  41. return self.anchor
  42. return None
  43. def yaml_set_anchor(self, value, always_dump=False):
  44. # type: (Any, bool) -> None
  45. self.anchor.value = value
  46. self.anchor.always_dump = always_dump
  47. class LiteralScalarString(ScalarString):
  48. __slots__ = 'comment' # the comment after the | on the first line
  49. style = '|'
  50. def __new__(cls, value, anchor=None):
  51. # type: (Text, Any) -> Any
  52. return ScalarString.__new__(cls, value, anchor=anchor)
  53. PreservedScalarString = LiteralScalarString
  54. class FoldedScalarString(ScalarString):
  55. __slots__ = ('fold_pos', 'comment') # the comment after the > on the first line
  56. style = '>'
  57. def __new__(cls, value, anchor=None):
  58. # type: (Text, Any) -> Any
  59. return ScalarString.__new__(cls, value, anchor=anchor)
  60. class SingleQuotedScalarString(ScalarString):
  61. __slots__ = ()
  62. style = "'"
  63. def __new__(cls, value, anchor=None):
  64. # type: (Text, Any) -> Any
  65. return ScalarString.__new__(cls, value, anchor=anchor)
  66. class DoubleQuotedScalarString(ScalarString):
  67. __slots__ = ()
  68. style = '"'
  69. def __new__(cls, value, anchor=None):
  70. # type: (Text, Any) -> Any
  71. return ScalarString.__new__(cls, value, anchor=anchor)
  72. class PlainScalarString(ScalarString):
  73. __slots__ = ()
  74. style = ''
  75. def __new__(cls, value, anchor=None):
  76. # type: (Text, Any) -> Any
  77. return ScalarString.__new__(cls, value, anchor=anchor)
  78. def preserve_literal(s):
  79. # type: (Text) -> Text
  80. return LiteralScalarString(s.replace('\r\n', '\n').replace('\r', '\n'))
  81. def walk_tree(base, map=None):
  82. # type: (Any, Any) -> None
  83. """
  84. the routine here walks over a simple yaml tree (recursing in
  85. dict values and list items) and converts strings that
  86. have multiple lines to literal scalars
  87. You can also provide an explicit (ordered) mapping for multiple transforms
  88. (first of which is executed):
  89. map = ruamel.yaml.compat.ordereddict
  90. map['\n'] = preserve_literal
  91. map[':'] = SingleQuotedScalarString
  92. walk_tree(data, map=map)
  93. """
  94. from ruamel.yaml.compat import string_types
  95. from ruamel.yaml.compat import MutableMapping, MutableSequence # type: ignore
  96. if map is None:
  97. map = {'\n': preserve_literal}
  98. if isinstance(base, MutableMapping):
  99. for k in base:
  100. v = base[k] # type: Text
  101. if isinstance(v, string_types):
  102. for ch in map:
  103. if ch in v:
  104. base[k] = map[ch](v)
  105. break
  106. else:
  107. walk_tree(v, map=map)
  108. elif isinstance(base, MutableSequence):
  109. for idx, elem in enumerate(base):
  110. if isinstance(elem, string_types):
  111. for ch in map:
  112. if ch in elem: # type: ignore
  113. base[idx] = map[ch](elem)
  114. break
  115. else:
  116. walk_tree(elem, map=map)