serializer.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. # coding: utf-8
  2. from __future__ import absolute_import
  3. from ruamel.yaml.error import YAMLError
  4. from ruamel.yaml.compat import nprint, DBG_NODE, dbg, string_types, nprintf # NOQA
  5. from ruamel.yaml.util import RegExp
  6. from ruamel.yaml.events import (
  7. StreamStartEvent,
  8. StreamEndEvent,
  9. MappingStartEvent,
  10. MappingEndEvent,
  11. SequenceStartEvent,
  12. SequenceEndEvent,
  13. AliasEvent,
  14. ScalarEvent,
  15. DocumentStartEvent,
  16. DocumentEndEvent,
  17. )
  18. from ruamel.yaml.nodes import MappingNode, ScalarNode, SequenceNode
  19. if False: # MYPY
  20. from typing import Any, Dict, Union, Text, Optional # NOQA
  21. from ruamel.yaml.compat import VersionType # NOQA
  22. __all__ = ['Serializer', 'SerializerError']
  23. class SerializerError(YAMLError):
  24. pass
  25. class Serializer(object):
  26. # 'id' and 3+ numbers, but not 000
  27. ANCHOR_TEMPLATE = u'id%03d'
  28. ANCHOR_RE = RegExp(u'id(?!000$)\\d{3,}')
  29. def __init__(
  30. self,
  31. encoding=None,
  32. explicit_start=None,
  33. explicit_end=None,
  34. version=None,
  35. tags=None,
  36. dumper=None,
  37. ):
  38. # type: (Any, Optional[bool], Optional[bool], Optional[VersionType], Any, Any) -> None # NOQA
  39. self.dumper = dumper
  40. if self.dumper is not None:
  41. self.dumper._serializer = self
  42. self.use_encoding = encoding
  43. self.use_explicit_start = explicit_start
  44. self.use_explicit_end = explicit_end
  45. if isinstance(version, string_types):
  46. self.use_version = tuple(map(int, version.split('.')))
  47. else:
  48. self.use_version = version # type: ignore
  49. self.use_tags = tags
  50. self.serialized_nodes = {} # type: Dict[Any, Any]
  51. self.anchors = {} # type: Dict[Any, Any]
  52. self.last_anchor_id = 0
  53. self.closed = None # type: Optional[bool]
  54. self._templated_id = None
  55. @property
  56. def emitter(self):
  57. # type: () -> Any
  58. if hasattr(self.dumper, 'typ'):
  59. return self.dumper.emitter
  60. return self.dumper._emitter
  61. @property
  62. def resolver(self):
  63. # type: () -> Any
  64. if hasattr(self.dumper, 'typ'):
  65. self.dumper.resolver
  66. return self.dumper._resolver
  67. def open(self):
  68. # type: () -> None
  69. if self.closed is None:
  70. self.emitter.emit(StreamStartEvent(encoding=self.use_encoding))
  71. self.closed = False
  72. elif self.closed:
  73. raise SerializerError('serializer is closed')
  74. else:
  75. raise SerializerError('serializer is already opened')
  76. def close(self):
  77. # type: () -> None
  78. if self.closed is None:
  79. raise SerializerError('serializer is not opened')
  80. elif not self.closed:
  81. self.emitter.emit(StreamEndEvent())
  82. self.closed = True
  83. # def __del__(self):
  84. # self.close()
  85. def serialize(self, node):
  86. # type: (Any) -> None
  87. if dbg(DBG_NODE):
  88. nprint('Serializing nodes')
  89. node.dump()
  90. if self.closed is None:
  91. raise SerializerError('serializer is not opened')
  92. elif self.closed:
  93. raise SerializerError('serializer is closed')
  94. self.emitter.emit(
  95. DocumentStartEvent(
  96. explicit=self.use_explicit_start, version=self.use_version, tags=self.use_tags
  97. )
  98. )
  99. self.anchor_node(node)
  100. self.serialize_node(node, None, None)
  101. self.emitter.emit(DocumentEndEvent(explicit=self.use_explicit_end))
  102. self.serialized_nodes = {}
  103. self.anchors = {}
  104. self.last_anchor_id = 0
  105. def anchor_node(self, node):
  106. # type: (Any) -> None
  107. if node in self.anchors:
  108. if self.anchors[node] is None:
  109. self.anchors[node] = self.generate_anchor(node)
  110. else:
  111. anchor = None
  112. try:
  113. if node.anchor.always_dump:
  114. anchor = node.anchor.value
  115. except: # NOQA
  116. pass
  117. self.anchors[node] = anchor
  118. if isinstance(node, SequenceNode):
  119. for item in node.value:
  120. self.anchor_node(item)
  121. elif isinstance(node, MappingNode):
  122. for key, value in node.value:
  123. self.anchor_node(key)
  124. self.anchor_node(value)
  125. def generate_anchor(self, node):
  126. # type: (Any) -> Any
  127. try:
  128. anchor = node.anchor.value
  129. except: # NOQA
  130. anchor = None
  131. if anchor is None:
  132. self.last_anchor_id += 1
  133. return self.ANCHOR_TEMPLATE % self.last_anchor_id
  134. return anchor
  135. def serialize_node(self, node, parent, index):
  136. # type: (Any, Any, Any) -> None
  137. alias = self.anchors[node]
  138. if node in self.serialized_nodes:
  139. self.emitter.emit(AliasEvent(alias))
  140. else:
  141. self.serialized_nodes[node] = True
  142. self.resolver.descend_resolver(parent, index)
  143. if isinstance(node, ScalarNode):
  144. # here check if the node.tag equals the one that would result from parsing
  145. # if not equal quoting is necessary for strings
  146. detected_tag = self.resolver.resolve(ScalarNode, node.value, (True, False))
  147. default_tag = self.resolver.resolve(ScalarNode, node.value, (False, True))
  148. implicit = (
  149. (node.tag == detected_tag),
  150. (node.tag == default_tag),
  151. node.tag.startswith('tag:yaml.org,2002:'),
  152. )
  153. self.emitter.emit(
  154. ScalarEvent(
  155. alias,
  156. node.tag,
  157. implicit,
  158. node.value,
  159. style=node.style,
  160. comment=node.comment,
  161. )
  162. )
  163. elif isinstance(node, SequenceNode):
  164. implicit = node.tag == self.resolver.resolve(SequenceNode, node.value, True)
  165. comment = node.comment
  166. end_comment = None
  167. seq_comment = None
  168. if node.flow_style is True:
  169. if comment: # eol comment on flow style sequence
  170. seq_comment = comment[0]
  171. # comment[0] = None
  172. if comment and len(comment) > 2:
  173. end_comment = comment[2]
  174. else:
  175. end_comment = None
  176. self.emitter.emit(
  177. SequenceStartEvent(
  178. alias,
  179. node.tag,
  180. implicit,
  181. flow_style=node.flow_style,
  182. comment=node.comment,
  183. )
  184. )
  185. index = 0
  186. for item in node.value:
  187. self.serialize_node(item, node, index)
  188. index += 1
  189. self.emitter.emit(SequenceEndEvent(comment=[seq_comment, end_comment]))
  190. elif isinstance(node, MappingNode):
  191. implicit = node.tag == self.resolver.resolve(MappingNode, node.value, True)
  192. comment = node.comment
  193. end_comment = None
  194. map_comment = None
  195. if node.flow_style is True:
  196. if comment: # eol comment on flow style sequence
  197. map_comment = comment[0]
  198. # comment[0] = None
  199. if comment and len(comment) > 2:
  200. end_comment = comment[2]
  201. self.emitter.emit(
  202. MappingStartEvent(
  203. alias,
  204. node.tag,
  205. implicit,
  206. flow_style=node.flow_style,
  207. comment=node.comment,
  208. nr_items=len(node.value),
  209. )
  210. )
  211. for key, value in node.value:
  212. self.serialize_node(key, node, None)
  213. self.serialize_node(value, node, key)
  214. self.emitter.emit(MappingEndEvent(comment=[map_comment, end_comment]))
  215. self.resolver.ascend_resolver()
  216. def templated_id(s):
  217. # type: (Text) -> Any
  218. return Serializer.ANCHOR_RE.match(s)