main.py 58 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664
  1. # coding: utf-8
  2. import sys
  3. import os
  4. import warnings
  5. import glob
  6. from importlib import import_module
  7. import ruamel.yaml
  8. from ruamel.yaml.error import UnsafeLoaderWarning, YAMLError # NOQA
  9. from ruamel.yaml.tokens import * # NOQA
  10. from ruamel.yaml.events import * # NOQA
  11. from ruamel.yaml.nodes import * # NOQA
  12. from ruamel.yaml.loader import BaseLoader, SafeLoader, Loader, RoundTripLoader # NOQA
  13. from ruamel.yaml.dumper import BaseDumper, SafeDumper, Dumper, RoundTripDumper # NOQA
  14. from ruamel.yaml.compat import StringIO, BytesIO, with_metaclass, nprint, nprintf # NOQA
  15. from ruamel.yaml.resolver import VersionedResolver, Resolver # NOQA
  16. from ruamel.yaml.representer import (
  17. BaseRepresenter,
  18. SafeRepresenter,
  19. Representer,
  20. RoundTripRepresenter,
  21. )
  22. from ruamel.yaml.constructor import (
  23. BaseConstructor,
  24. SafeConstructor,
  25. Constructor,
  26. RoundTripConstructor,
  27. )
  28. from ruamel.yaml.loader import Loader as UnsafeLoader # NOQA
  29. from ruamel.yaml.comments import CommentedMap, CommentedSeq, C_PRE
  30. from typing import List, Set, Dict, Union, Any, Callable, Optional, Text, Type # NOQA
  31. from types import TracebackType
  32. from ruamel.yaml.compat import StreamType, StreamTextType, VersionType # NOQA
  33. from pathlib import Path # NOQA
  34. try:
  35. from _ruamel_yaml import CParser, CEmitter # type: ignore
  36. except: # NOQA
  37. CParser = CEmitter = None
  38. # import io
  39. # YAML is an acronym, i.e. spoken: rhymes with "camel". And thus a
  40. # subset of abbreviations, which should be all caps according to PEP8
  41. class YAML:
  42. def __init__(
  43. self: Any,
  44. *,
  45. typ: Optional[Union[List[Text], Text]] = None,
  46. pure: Any = False,
  47. output: Any = None,
  48. plug_ins: Any = None,
  49. ) -> None: # input=None,
  50. """
  51. typ: 'rt'/None -> RoundTripLoader/RoundTripDumper, (default)
  52. 'safe' -> SafeLoader/SafeDumper,
  53. 'unsafe' -> normal/unsafe Loader/Dumper
  54. 'base' -> baseloader
  55. pure: if True only use Python modules
  56. input/output: needed to work as context manager
  57. plug_ins: a list of plug-in files
  58. """
  59. self.typ = ['rt'] if typ is None else (typ if isinstance(typ, list) else [typ])
  60. self.pure = pure
  61. # self._input = input
  62. self._output = output
  63. self._context_manager: Any = None
  64. self.plug_ins: List[Any] = []
  65. for pu in ([] if plug_ins is None else plug_ins) + self.official_plug_ins():
  66. file_name = pu.replace(os.sep, '.')
  67. self.plug_ins.append(import_module(file_name))
  68. self.Resolver: Any = ruamel.yaml.resolver.VersionedResolver
  69. self.allow_unicode = True
  70. self.Reader: Any = None
  71. self.Representer: Any = None
  72. self.Constructor: Any = None
  73. self.Scanner: Any = None
  74. self.Serializer: Any = None
  75. self.default_flow_style: Any = None
  76. self.comment_handling = None
  77. typ_found = 1
  78. setup_rt = False
  79. if 'rt' in self.typ:
  80. setup_rt = True
  81. elif 'safe' in self.typ:
  82. self.Emitter = (
  83. ruamel.yaml.emitter.Emitter if pure or CEmitter is None else CEmitter
  84. )
  85. self.Representer = ruamel.yaml.representer.SafeRepresenter
  86. self.Parser = ruamel.yaml.parser.Parser if pure or CParser is None else CParser
  87. self.Composer = ruamel.yaml.composer.Composer
  88. self.Constructor = ruamel.yaml.constructor.SafeConstructor
  89. elif 'base' in self.typ:
  90. self.Emitter = ruamel.yaml.emitter.Emitter
  91. self.Representer = ruamel.yaml.representer.BaseRepresenter
  92. self.Parser = ruamel.yaml.parser.Parser if pure or CParser is None else CParser
  93. self.Composer = ruamel.yaml.composer.Composer
  94. self.Constructor = ruamel.yaml.constructor.BaseConstructor
  95. elif 'unsafe' in self.typ:
  96. self.Emitter = (
  97. ruamel.yaml.emitter.Emitter if pure or CEmitter is None else CEmitter
  98. )
  99. self.Representer = ruamel.yaml.representer.Representer
  100. self.Parser = ruamel.yaml.parser.Parser if pure or CParser is None else CParser
  101. self.Composer = ruamel.yaml.composer.Composer
  102. self.Constructor = ruamel.yaml.constructor.Constructor
  103. elif 'rtsc' in self.typ:
  104. self.default_flow_style = False
  105. # no optimized rt-dumper yet
  106. self.Emitter = ruamel.yaml.emitter.RoundTripEmitter
  107. self.Serializer = ruamel.yaml.serializer.Serializer
  108. self.Representer = ruamel.yaml.representer.RoundTripRepresenter
  109. self.Scanner = ruamel.yaml.scanner.RoundTripScannerSC
  110. # no optimized rt-parser yet
  111. self.Parser = ruamel.yaml.parser.RoundTripParserSC
  112. self.Composer = ruamel.yaml.composer.Composer
  113. self.Constructor = ruamel.yaml.constructor.RoundTripConstructor
  114. self.comment_handling = C_PRE
  115. else:
  116. setup_rt = True
  117. typ_found = 0
  118. if setup_rt:
  119. self.default_flow_style = False
  120. # no optimized rt-dumper yet
  121. self.Emitter = ruamel.yaml.emitter.RoundTripEmitter
  122. self.Serializer = ruamel.yaml.serializer.Serializer
  123. self.Representer = ruamel.yaml.representer.RoundTripRepresenter
  124. self.Scanner = ruamel.yaml.scanner.RoundTripScanner
  125. # no optimized rt-parser yet
  126. self.Parser = ruamel.yaml.parser.RoundTripParser
  127. self.Composer = ruamel.yaml.composer.Composer
  128. self.Constructor = ruamel.yaml.constructor.RoundTripConstructor
  129. del setup_rt
  130. self.stream = None
  131. self.canonical = None
  132. self.old_indent = None
  133. self.width: Union[int, None] = None
  134. self.line_break = None
  135. self.map_indent: Union[int, None] = None
  136. self.sequence_indent: Union[int, None] = None
  137. self.sequence_dash_offset: int = 0
  138. self.compact_seq_seq = None
  139. self.compact_seq_map = None
  140. self.sort_base_mapping_type_on_output = None # default: sort
  141. self.top_level_colon_align = None
  142. self.prefix_colon = None
  143. self._version: Optional[Any] = None
  144. self.preserve_quotes: Optional[bool] = None
  145. self.allow_duplicate_keys = False # duplicate keys in map, set
  146. self.encoding = 'utf-8'
  147. self.explicit_start: Union[bool, None] = None
  148. self.explicit_end: Union[bool, None] = None
  149. self.tags = None
  150. self.default_style = None
  151. self.top_level_block_style_scalar_no_indent_error_1_1 = False
  152. # directives end indicator with single scalar document
  153. self.scalar_after_indicator: Optional[bool] = None
  154. # [a, b: 1, c: {d: 2}] vs. [a, {b: 1}, {c: {d: 2}}]
  155. self.brace_single_entry_mapping_in_flow_sequence = False
  156. for module in self.plug_ins:
  157. if getattr(module, 'typ', None) in self.typ:
  158. typ_found += 1
  159. module.init_typ(self)
  160. break
  161. if typ_found == 0:
  162. raise NotImplementedError(
  163. f'typ "{self.typ}" not recognised (need to install plug-in?)',
  164. )
  165. @property
  166. def reader(self) -> Any:
  167. try:
  168. return self._reader # type: ignore
  169. except AttributeError:
  170. self._reader = self.Reader(None, loader=self)
  171. return self._reader
  172. @property
  173. def scanner(self) -> Any:
  174. try:
  175. return self._scanner # type: ignore
  176. except AttributeError:
  177. self._scanner = self.Scanner(loader=self)
  178. return self._scanner
  179. @property
  180. def parser(self) -> Any:
  181. attr = '_' + sys._getframe().f_code.co_name
  182. if not hasattr(self, attr):
  183. if self.Parser is not CParser:
  184. setattr(self, attr, self.Parser(loader=self))
  185. else:
  186. if getattr(self, '_stream', None) is None:
  187. # wait for the stream
  188. return None
  189. else:
  190. # if not hasattr(self._stream, 'read') and hasattr(self._stream, 'open'):
  191. # # pathlib.Path() instance
  192. # setattr(self, attr, CParser(self._stream))
  193. # else:
  194. setattr(self, attr, CParser(self._stream))
  195. # self._parser = self._composer = self
  196. # nprint('scanner', self.loader.scanner)
  197. return getattr(self, attr)
  198. @property
  199. def composer(self) -> Any:
  200. attr = '_' + sys._getframe().f_code.co_name
  201. if not hasattr(self, attr):
  202. setattr(self, attr, self.Composer(loader=self))
  203. return getattr(self, attr)
  204. @property
  205. def constructor(self) -> Any:
  206. attr = '_' + sys._getframe().f_code.co_name
  207. if not hasattr(self, attr):
  208. cnst = self.Constructor(preserve_quotes=self.preserve_quotes, loader=self)
  209. cnst.allow_duplicate_keys = self.allow_duplicate_keys
  210. setattr(self, attr, cnst)
  211. return getattr(self, attr)
  212. @property
  213. def resolver(self) -> Any:
  214. attr = '_' + sys._getframe().f_code.co_name
  215. if not hasattr(self, attr):
  216. setattr(self, attr, self.Resolver(version=self.version, loader=self))
  217. return getattr(self, attr)
  218. @property
  219. def emitter(self) -> Any:
  220. attr = '_' + sys._getframe().f_code.co_name
  221. if not hasattr(self, attr):
  222. if self.Emitter is not CEmitter:
  223. _emitter = self.Emitter(
  224. None,
  225. canonical=self.canonical,
  226. indent=self.old_indent,
  227. width=self.width,
  228. allow_unicode=self.allow_unicode,
  229. line_break=self.line_break,
  230. prefix_colon=self.prefix_colon,
  231. brace_single_entry_mapping_in_flow_sequence=self.brace_single_entry_mapping_in_flow_sequence, # NOQA
  232. dumper=self,
  233. )
  234. setattr(self, attr, _emitter)
  235. if self.map_indent is not None:
  236. _emitter.best_map_indent = self.map_indent
  237. if self.sequence_indent is not None:
  238. _emitter.best_sequence_indent = self.sequence_indent
  239. if self.sequence_dash_offset is not None:
  240. _emitter.sequence_dash_offset = self.sequence_dash_offset
  241. # _emitter.block_seq_indent = self.sequence_dash_offset
  242. if self.compact_seq_seq is not None:
  243. _emitter.compact_seq_seq = self.compact_seq_seq
  244. if self.compact_seq_map is not None:
  245. _emitter.compact_seq_map = self.compact_seq_map
  246. else:
  247. if getattr(self, '_stream', None) is None:
  248. # wait for the stream
  249. return None
  250. return None
  251. return getattr(self, attr)
  252. @property
  253. def serializer(self) -> Any:
  254. attr = '_' + sys._getframe().f_code.co_name
  255. if not hasattr(self, attr):
  256. setattr(
  257. self,
  258. attr,
  259. self.Serializer(
  260. encoding=self.encoding,
  261. explicit_start=self.explicit_start,
  262. explicit_end=self.explicit_end,
  263. version=self.version,
  264. tags=self.tags,
  265. dumper=self,
  266. ),
  267. )
  268. return getattr(self, attr)
  269. @property
  270. def representer(self) -> Any:
  271. attr = '_' + sys._getframe().f_code.co_name
  272. if not hasattr(self, attr):
  273. repres = self.Representer(
  274. default_style=self.default_style,
  275. default_flow_style=self.default_flow_style,
  276. dumper=self,
  277. )
  278. if self.sort_base_mapping_type_on_output is not None:
  279. repres.sort_base_mapping_type_on_output = self.sort_base_mapping_type_on_output
  280. setattr(self, attr, repres)
  281. return getattr(self, attr)
  282. def scan(self, stream: StreamTextType) -> Any:
  283. """
  284. Scan a YAML stream and produce scanning tokens.
  285. """
  286. if not hasattr(stream, 'read') and hasattr(stream, 'open'):
  287. # pathlib.Path() instance
  288. with stream.open('rb') as fp:
  289. return self.scan(fp)
  290. _, parser = self.get_constructor_parser(stream)
  291. try:
  292. while self.scanner.check_token():
  293. yield self.scanner.get_token()
  294. finally:
  295. parser.dispose()
  296. try:
  297. self._reader.reset_reader()
  298. except AttributeError:
  299. pass
  300. try:
  301. self._scanner.reset_scanner()
  302. except AttributeError:
  303. pass
  304. def parse(self, stream: StreamTextType) -> Any:
  305. """
  306. Parse a YAML stream and produce parsing events.
  307. """
  308. if not hasattr(stream, 'read') and hasattr(stream, 'open'):
  309. # pathlib.Path() instance
  310. with stream.open('rb') as fp:
  311. return self.parse(fp)
  312. _, parser = self.get_constructor_parser(stream)
  313. try:
  314. while parser.check_event():
  315. yield parser.get_event()
  316. finally:
  317. parser.dispose()
  318. try:
  319. self._reader.reset_reader()
  320. except AttributeError:
  321. pass
  322. try:
  323. self._scanner.reset_scanner()
  324. except AttributeError:
  325. pass
  326. def compose(self, stream: Union[Path, StreamTextType]) -> Any:
  327. """
  328. Parse the first YAML document in a stream
  329. and produce the corresponding representation tree.
  330. """
  331. if not hasattr(stream, 'read') and hasattr(stream, 'open'):
  332. # pathlib.Path() instance
  333. with stream.open('rb') as fp:
  334. return self.compose(fp)
  335. constructor, parser = self.get_constructor_parser(stream)
  336. try:
  337. return constructor.composer.get_single_node()
  338. finally:
  339. parser.dispose()
  340. try:
  341. self._reader.reset_reader()
  342. except AttributeError:
  343. pass
  344. try:
  345. self._scanner.reset_scanner()
  346. except AttributeError:
  347. pass
  348. def compose_all(self, stream: Union[Path, StreamTextType]) -> Any:
  349. """
  350. Parse all YAML documents in a stream
  351. and produce corresponding representation trees.
  352. """
  353. constructor, parser = self.get_constructor_parser(stream)
  354. try:
  355. while constructor.composer.check_node():
  356. yield constructor.composer.get_node()
  357. finally:
  358. parser.dispose()
  359. try:
  360. self._reader.reset_reader()
  361. except AttributeError:
  362. pass
  363. try:
  364. self._scanner.reset_scanner()
  365. except AttributeError:
  366. pass
  367. # separate output resolver?
  368. # def load(self, stream=None):
  369. # if self._context_manager:
  370. # if not self._input:
  371. # raise TypeError("Missing input stream while dumping from context manager")
  372. # for data in self._context_manager.load():
  373. # yield data
  374. # return
  375. # if stream is None:
  376. # raise TypeError("Need a stream argument when not loading from context manager")
  377. # return self.load_one(stream)
  378. def load(self, stream: Union[Path, StreamTextType]) -> Any:
  379. """
  380. at this point you either have the non-pure Parser (which has its own reader and
  381. scanner) or you have the pure Parser.
  382. If the pure Parser is set, then set the Reader and Scanner, if not already set.
  383. If either the Scanner or Reader are set, you cannot use the non-pure Parser,
  384. so reset it to the pure parser and set the Reader resp. Scanner if necessary
  385. """
  386. if not hasattr(stream, 'read') and hasattr(stream, 'open'):
  387. # pathlib.Path() instance
  388. with stream.open('rb') as fp:
  389. return self.load(fp)
  390. constructor, parser = self.get_constructor_parser(stream)
  391. try:
  392. return constructor.get_single_data()
  393. finally:
  394. parser.dispose()
  395. try:
  396. self._reader.reset_reader()
  397. except AttributeError:
  398. pass
  399. try:
  400. self._scanner.reset_scanner()
  401. except AttributeError:
  402. pass
  403. def load_all(self, stream: Union[Path, StreamTextType]) -> Any: # *, skip=None):
  404. if not hasattr(stream, 'read') and hasattr(stream, 'open'):
  405. # pathlib.Path() instance
  406. with stream.open('r') as fp:
  407. for d in self.load_all(fp):
  408. yield d
  409. return
  410. # if skip is None:
  411. # skip = []
  412. # elif isinstance(skip, int):
  413. # skip = [skip]
  414. constructor, parser = self.get_constructor_parser(stream)
  415. try:
  416. while constructor.check_data():
  417. yield constructor.get_data()
  418. finally:
  419. parser.dispose()
  420. try:
  421. self._reader.reset_reader()
  422. except AttributeError:
  423. pass
  424. try:
  425. self._scanner.reset_scanner()
  426. except AttributeError:
  427. pass
  428. def get_constructor_parser(self, stream: StreamTextType) -> Any:
  429. """
  430. the old cyaml needs special setup, and therefore the stream
  431. """
  432. if self.Parser is not CParser:
  433. if self.Reader is None:
  434. self.Reader = ruamel.yaml.reader.Reader
  435. if self.Scanner is None:
  436. self.Scanner = ruamel.yaml.scanner.Scanner
  437. self.reader.stream = stream
  438. else:
  439. if self.Reader is not None:
  440. if self.Scanner is None:
  441. self.Scanner = ruamel.yaml.scanner.Scanner
  442. self.Parser = ruamel.yaml.parser.Parser
  443. self.reader.stream = stream
  444. elif self.Scanner is not None:
  445. if self.Reader is None:
  446. self.Reader = ruamel.yaml.reader.Reader
  447. self.Parser = ruamel.yaml.parser.Parser
  448. self.reader.stream = stream
  449. else:
  450. # combined C level reader>scanner>parser
  451. # does some calls to the resolver, e.g. BaseResolver.descend_resolver
  452. # if you just initialise the CParser, to much of resolver.py
  453. # is actually used
  454. rslvr = self.Resolver
  455. # if rslvr is ruamel.yaml.resolver.VersionedResolver:
  456. # rslvr = ruamel.yaml.resolver.Resolver
  457. class XLoader(self.Parser, self.Constructor, rslvr): # type: ignore
  458. def __init__(
  459. selfx,
  460. stream: StreamTextType,
  461. version: Optional[VersionType] = self.version,
  462. preserve_quotes: Optional[bool] = None,
  463. ) -> None:
  464. # NOQA
  465. CParser.__init__(selfx, stream)
  466. selfx._parser = selfx._composer = selfx
  467. self.Constructor.__init__(selfx, loader=selfx)
  468. selfx.allow_duplicate_keys = self.allow_duplicate_keys
  469. rslvr.__init__(selfx, version=version, loadumper=selfx)
  470. self._stream = stream
  471. loader = XLoader(stream)
  472. return loader, loader
  473. return self.constructor, self.parser
  474. def emit(self, events: Any, stream: Any) -> None:
  475. """
  476. Emit YAML parsing events into a stream.
  477. If stream is None, return the produced string instead.
  478. """
  479. _, _, emitter = self.get_serializer_representer_emitter(stream, None)
  480. try:
  481. for event in events:
  482. emitter.emit(event)
  483. finally:
  484. try:
  485. emitter.dispose()
  486. except AttributeError:
  487. raise
  488. def serialize(self, node: Any, stream: Optional[StreamType]) -> Any:
  489. """
  490. Serialize a representation tree into a YAML stream.
  491. If stream is None, return the produced string instead.
  492. """
  493. self.serialize_all([node], stream)
  494. def serialize_all(self, nodes: Any, stream: Optional[StreamType]) -> Any:
  495. """
  496. Serialize a sequence of representation trees into a YAML stream.
  497. If stream is None, return the produced string instead.
  498. """
  499. serializer, _, emitter = self.get_serializer_representer_emitter(stream, None)
  500. try:
  501. serializer.open()
  502. for node in nodes:
  503. serializer.serialize(node)
  504. serializer.close()
  505. finally:
  506. try:
  507. emitter.dispose()
  508. except AttributeError:
  509. raise
  510. def dump(
  511. self: Any, data: Union[Path, StreamType], stream: Any = None, *, transform: Any = None,
  512. ) -> Any:
  513. if self._context_manager:
  514. if not self._output:
  515. raise TypeError('Missing output stream while dumping from context manager')
  516. if transform is not None:
  517. x = self.__class__.__name__
  518. raise TypeError(
  519. f'{x}.dump() in the context manager cannot have transform keyword',
  520. )
  521. self._context_manager.dump(data)
  522. else: # old style
  523. if stream is None:
  524. raise TypeError('Need a stream argument when not dumping from context manager')
  525. return self.dump_all([data], stream, transform=transform)
  526. def dump_all(
  527. self, documents: Any, stream: Union[Path, StreamType], *, transform: Any = None,
  528. ) -> Any:
  529. if self._context_manager:
  530. raise NotImplementedError
  531. self._output = stream
  532. self._context_manager = YAMLContextManager(self, transform=transform)
  533. for data in documents:
  534. self._context_manager.dump(data)
  535. self._context_manager.teardown_output()
  536. self._output = None
  537. self._context_manager = None
  538. def Xdump_all(self, documents: Any, stream: Any, *, transform: Any = None) -> Any:
  539. """
  540. Serialize a sequence of Python objects into a YAML stream.
  541. """
  542. if not hasattr(stream, 'write') and hasattr(stream, 'open'):
  543. # pathlib.Path() instance
  544. with stream.open('w') as fp:
  545. return self.dump_all(documents, fp, transform=transform)
  546. # The stream should have the methods `write` and possibly `flush`.
  547. if self.top_level_colon_align is True:
  548. tlca: Any = max([len(str(x)) for x in documents[0]])
  549. else:
  550. tlca = self.top_level_colon_align
  551. if transform is not None:
  552. fstream = stream
  553. if self.encoding is None:
  554. stream = StringIO()
  555. else:
  556. stream = BytesIO()
  557. serializer, representer, emitter = self.get_serializer_representer_emitter(
  558. stream, tlca,
  559. )
  560. try:
  561. self.serializer.open()
  562. for data in documents:
  563. try:
  564. self.representer.represent(data)
  565. except AttributeError:
  566. # nprint(dir(dumper._representer))
  567. raise
  568. self.serializer.close()
  569. finally:
  570. try:
  571. self.emitter.dispose()
  572. except AttributeError:
  573. raise
  574. # self.dumper.dispose() # cyaml
  575. delattr(self, '_serializer')
  576. delattr(self, '_emitter')
  577. if transform:
  578. val = stream.getvalue()
  579. if self.encoding:
  580. val = val.decode(self.encoding)
  581. if fstream is None:
  582. transform(val)
  583. else:
  584. fstream.write(transform(val))
  585. return None
  586. def get_serializer_representer_emitter(self, stream: StreamType, tlca: Any) -> Any:
  587. # we have only .Serializer to deal with (vs .Reader & .Scanner), much simpler
  588. if self.Emitter is not CEmitter:
  589. if self.Serializer is None:
  590. self.Serializer = ruamel.yaml.serializer.Serializer
  591. self.emitter.stream = stream
  592. self.emitter.top_level_colon_align = tlca
  593. if self.scalar_after_indicator is not None:
  594. self.emitter.scalar_after_indicator = self.scalar_after_indicator
  595. return self.serializer, self.representer, self.emitter
  596. if self.Serializer is not None:
  597. # cannot set serializer with CEmitter
  598. self.Emitter = ruamel.yaml.emitter.Emitter
  599. self.emitter.stream = stream
  600. self.emitter.top_level_colon_align = tlca
  601. if self.scalar_after_indicator is not None:
  602. self.emitter.scalar_after_indicator = self.scalar_after_indicator
  603. return self.serializer, self.representer, self.emitter
  604. # C routines
  605. rslvr = (
  606. ruamel.yaml.resolver.BaseResolver
  607. if 'base' in self.typ
  608. else ruamel.yaml.resolver.Resolver
  609. )
  610. class XDumper(CEmitter, self.Representer, rslvr): # type: ignore
  611. def __init__(
  612. selfx: StreamType,
  613. stream: Any,
  614. default_style: Any = None,
  615. default_flow_style: Any = None,
  616. canonical: Optional[bool] = None,
  617. indent: Optional[int] = None,
  618. width: Optional[int] = None,
  619. allow_unicode: Optional[bool] = None,
  620. line_break: Any = None,
  621. encoding: Any = None,
  622. explicit_start: Optional[bool] = None,
  623. explicit_end: Optional[bool] = None,
  624. version: Any = None,
  625. tags: Any = None,
  626. block_seq_indent: Any = None,
  627. top_level_colon_align: Any = None,
  628. prefix_colon: Any = None,
  629. ) -> None:
  630. # NOQA
  631. CEmitter.__init__(
  632. selfx,
  633. stream,
  634. canonical=canonical,
  635. indent=indent,
  636. width=width,
  637. encoding=encoding,
  638. allow_unicode=allow_unicode,
  639. line_break=line_break,
  640. explicit_start=explicit_start,
  641. explicit_end=explicit_end,
  642. version=version,
  643. tags=tags,
  644. )
  645. selfx._emitter = selfx._serializer = selfx._representer = selfx
  646. self.Representer.__init__(
  647. selfx, default_style=default_style, default_flow_style=default_flow_style,
  648. )
  649. rslvr.__init__(selfx)
  650. self._stream = stream
  651. dumper = XDumper(
  652. stream,
  653. default_style=self.default_style,
  654. default_flow_style=self.default_flow_style,
  655. canonical=self.canonical,
  656. indent=self.old_indent,
  657. width=self.width,
  658. allow_unicode=self.allow_unicode,
  659. line_break=self.line_break,
  660. explicit_start=self.explicit_start,
  661. explicit_end=self.explicit_end,
  662. version=self.version,
  663. tags=self.tags,
  664. )
  665. self._emitter = self._serializer = dumper
  666. return dumper, dumper, dumper
  667. # basic types
  668. def map(self, **kw: Any) -> Any:
  669. if 'rt' in self.typ:
  670. return CommentedMap(**kw)
  671. else:
  672. return dict(**kw)
  673. def seq(self, *args: Any) -> Any:
  674. if 'rt' in self.typ:
  675. return CommentedSeq(*args)
  676. else:
  677. return list(*args)
  678. # helpers
  679. def official_plug_ins(self) -> Any:
  680. """search for list of subdirs that are plug-ins, if __file__ is not available, e.g.
  681. single file installers that are not properly emulating a file-system (issue 324)
  682. no plug-ins will be found. If any are packaged, you know which file that are
  683. and you can explicitly provide it during instantiation:
  684. yaml = ruamel.yaml.YAML(plug_ins=['ruamel/yaml/jinja2/__plug_in__'])
  685. """
  686. try:
  687. bd = os.path.dirname(__file__)
  688. except NameError:
  689. return []
  690. gpbd = os.path.dirname(os.path.dirname(bd))
  691. res = [x.replace(gpbd, "")[1:-3] for x in glob.glob(bd + '/*/__plug_in__.py')]
  692. return res
  693. def register_class(self, cls: Any) -> Any:
  694. """
  695. register a class for dumping/loading
  696. - if it has attribute yaml_tag use that to register, else use class name
  697. - if it has methods to_yaml/from_yaml use those to dump/load else dump attributes
  698. as mapping
  699. """
  700. tag = getattr(cls, 'yaml_tag', '!' + cls.__name__)
  701. try:
  702. self.representer.add_representer(cls, cls.to_yaml)
  703. except AttributeError:
  704. def t_y(representer: Any, data: Any) -> Any:
  705. return representer.represent_yaml_object(
  706. tag, data, cls, flow_style=representer.default_flow_style,
  707. )
  708. self.representer.add_representer(cls, t_y)
  709. try:
  710. self.constructor.add_constructor(tag, cls.from_yaml)
  711. except AttributeError:
  712. def f_y(constructor: Any, node: Any) -> Any:
  713. return constructor.construct_yaml_object(node, cls)
  714. self.constructor.add_constructor(tag, f_y)
  715. return cls
  716. # ### context manager
  717. def __enter__(self) -> Any:
  718. self._context_manager = YAMLContextManager(self)
  719. return self
  720. def __exit__(
  721. self,
  722. typ: Optional[Type[BaseException]],
  723. value: Optional[BaseException],
  724. traceback: Optional[TracebackType],
  725. ) -> None:
  726. if typ:
  727. nprint('typ', typ)
  728. self._context_manager.teardown_output()
  729. # self._context_manager.teardown_input()
  730. self._context_manager = None
  731. # ### backwards compatibility
  732. def _indent(self, mapping: Any = None, sequence: Any = None, offset: Any = None) -> None:
  733. if mapping is not None:
  734. self.map_indent = mapping
  735. if sequence is not None:
  736. self.sequence_indent = sequence
  737. if offset is not None:
  738. self.sequence_dash_offset = offset
  739. @property
  740. def version(self) -> Optional[Any]:
  741. return self._version
  742. @version.setter
  743. def version(self, val: Optional[VersionType]) -> None:
  744. if val is None:
  745. self._version = val
  746. return
  747. if isinstance(val, str):
  748. sval = tuple(int(x) for x in val.split('.'))
  749. else:
  750. sval = tuple(int(x) for x in val)
  751. assert len(sval) == 2, f'version can only have major.minor, got {val}'
  752. assert sval[0] == 1, f'version major part can only be 1, got {val}'
  753. assert sval[1] in [1, 2], f'version minor part can only be 2 or 1, got {val}'
  754. self._version = sval
  755. @property
  756. def indent(self) -> Any:
  757. return self._indent
  758. @indent.setter
  759. def indent(self, val: Any) -> None:
  760. self.old_indent = val
  761. @property
  762. def block_seq_indent(self) -> Any:
  763. return self.sequence_dash_offset
  764. @block_seq_indent.setter
  765. def block_seq_indent(self, val: Any) -> None:
  766. self.sequence_dash_offset = val
  767. def compact(self, seq_seq: Any = None, seq_map: Any = None) -> None:
  768. self.compact_seq_seq = seq_seq
  769. self.compact_seq_map = seq_map
  770. class YAMLContextManager:
  771. def __init__(self, yaml: Any, transform: Any = None) -> None:
  772. # used to be: (Any, Optional[Callable]) -> None
  773. self._yaml = yaml
  774. self._output_inited = False
  775. self._output_path = None
  776. self._output = self._yaml._output
  777. self._transform = transform
  778. # self._input_inited = False
  779. # self._input = input
  780. # self._input_path = None
  781. # self._transform = yaml.transform
  782. # self._fstream = None
  783. if not hasattr(self._output, 'write') and hasattr(self._output, 'open'):
  784. # pathlib.Path() instance, open with the same mode
  785. self._output_path = self._output
  786. self._output = self._output_path.open('w')
  787. # if not hasattr(self._stream, 'write') and hasattr(stream, 'open'):
  788. # if not hasattr(self._input, 'read') and hasattr(self._input, 'open'):
  789. # # pathlib.Path() instance, open with the same mode
  790. # self._input_path = self._input
  791. # self._input = self._input_path.open('r')
  792. if self._transform is not None:
  793. self._fstream = self._output
  794. if self._yaml.encoding is None:
  795. self._output = StringIO()
  796. else:
  797. self._output = BytesIO()
  798. def teardown_output(self) -> None:
  799. if self._output_inited:
  800. self._yaml.serializer.close()
  801. else:
  802. return
  803. try:
  804. self._yaml.emitter.dispose()
  805. except AttributeError:
  806. raise
  807. # self.dumper.dispose() # cyaml
  808. try:
  809. delattr(self._yaml, '_serializer')
  810. delattr(self._yaml, '_emitter')
  811. except AttributeError:
  812. raise
  813. if self._transform:
  814. val = self._output.getvalue()
  815. if self._yaml.encoding:
  816. val = val.decode(self._yaml.encoding)
  817. if self._fstream is None:
  818. self._transform(val)
  819. else:
  820. self._fstream.write(self._transform(val))
  821. self._fstream.flush()
  822. self._output = self._fstream # maybe not necessary
  823. if self._output_path is not None:
  824. self._output.close()
  825. def init_output(self, first_data: Any) -> None:
  826. if self._yaml.top_level_colon_align is True:
  827. tlca: Any = max([len(str(x)) for x in first_data])
  828. else:
  829. tlca = self._yaml.top_level_colon_align
  830. self._yaml.get_serializer_representer_emitter(self._output, tlca)
  831. self._yaml.serializer.open()
  832. self._output_inited = True
  833. def dump(self, data: Any) -> None:
  834. if not self._output_inited:
  835. self.init_output(data)
  836. try:
  837. self._yaml.representer.represent(data)
  838. except AttributeError:
  839. # nprint(dir(dumper._representer))
  840. raise
  841. # def teardown_input(self):
  842. # pass
  843. #
  844. # def init_input(self):
  845. # # set the constructor and parser on YAML() instance
  846. # self._yaml.get_constructor_parser(stream)
  847. #
  848. # def load(self):
  849. # if not self._input_inited:
  850. # self.init_input()
  851. # try:
  852. # while self._yaml.constructor.check_data():
  853. # yield self._yaml.constructor.get_data()
  854. # finally:
  855. # parser.dispose()
  856. # try:
  857. # self._reader.reset_reader() # type: ignore
  858. # except AttributeError:
  859. # pass
  860. # try:
  861. # self._scanner.reset_scanner() # type: ignore
  862. # except AttributeError:
  863. # pass
  864. def yaml_object(yml: Any) -> Any:
  865. """ decorator for classes that needs to dump/load objects
  866. The tag for such objects is taken from the class attribute yaml_tag (or the
  867. class name in lowercase in case unavailable)
  868. If methods to_yaml and/or from_yaml are available, these are called for dumping resp.
  869. loading, default routines (dumping a mapping of the attributes) used otherwise.
  870. """
  871. def yo_deco(cls: Any) -> Any:
  872. tag = getattr(cls, 'yaml_tag', '!' + cls.__name__)
  873. try:
  874. yml.representer.add_representer(cls, cls.to_yaml)
  875. except AttributeError:
  876. def t_y(representer: Any, data: Any) -> Any:
  877. return representer.represent_yaml_object(
  878. tag, data, cls, flow_style=representer.default_flow_style,
  879. )
  880. yml.representer.add_representer(cls, t_y)
  881. try:
  882. yml.constructor.add_constructor(tag, cls.from_yaml)
  883. except AttributeError:
  884. def f_y(constructor: Any, node: Any) -> Any:
  885. return constructor.construct_yaml_object(node, cls)
  886. yml.constructor.add_constructor(tag, f_y)
  887. return cls
  888. return yo_deco
  889. ########################################################################################
  890. def warn_deprecation(fun: Any, method: Any, arg: str = '') -> None:
  891. warnings.warn(
  892. f'\n{fun} will be removed, use\n\n yaml=YAML({arg})\n yaml.{method}(...)\n\ninstead', # NOQA
  893. PendingDeprecationWarning, # this will show when testing with pytest/tox
  894. stacklevel=3,
  895. )
  896. def error_deprecation(fun: Any, method: Any, arg: str = '') -> None:
  897. warnings.warn(
  898. f'\n{fun} has been removed, use\n\n yaml=YAML({arg})\n yaml.{method}(...)\n\ninstead', # NOQA
  899. DeprecationWarning,
  900. stacklevel=3,
  901. )
  902. sys.exit(1)
  903. ########################################################################################
  904. def scan(stream: StreamTextType, Loader: Any = Loader) -> Any:
  905. """
  906. Scan a YAML stream and produce scanning tokens.
  907. """
  908. warn_deprecation('scan', 'scan', arg="typ='unsafe', pure=True")
  909. loader = Loader(stream)
  910. try:
  911. while loader.scanner.check_token():
  912. yield loader.scanner.get_token()
  913. finally:
  914. loader._parser.dispose()
  915. def parse(stream: StreamTextType, Loader: Any = Loader) -> Any:
  916. """
  917. Parse a YAML stream and produce parsing events.
  918. """
  919. warn_deprecation('parse', 'parse', arg="typ='unsafe', pure=True")
  920. loader = Loader(stream)
  921. try:
  922. while loader._parser.check_event():
  923. yield loader._parser.get_event()
  924. finally:
  925. loader._parser.dispose()
  926. def compose(stream: StreamTextType, Loader: Any = Loader) -> Any:
  927. """
  928. Parse the first YAML document in a stream
  929. and produce the corresponding representation tree.
  930. """
  931. warn_deprecation('compose', 'compose', arg="typ='unsafe', pure=True")
  932. loader = Loader(stream)
  933. try:
  934. return loader.get_single_node()
  935. finally:
  936. loader.dispose()
  937. def compose_all(stream: StreamTextType, Loader: Any = Loader) -> Any:
  938. """
  939. Parse all YAML documents in a stream
  940. and produce corresponding representation trees.
  941. """
  942. warn_deprecation('compose', 'compose', arg="typ='unsafe', pure=True")
  943. loader = Loader(stream)
  944. try:
  945. while loader.check_node():
  946. yield loader._composer.get_node()
  947. finally:
  948. loader._parser.dispose()
  949. def load(
  950. stream: Any, Loader: Any = None, version: Any = None, preserve_quotes: Any = None,
  951. ) -> Any:
  952. """
  953. Parse the first YAML document in a stream
  954. and produce the corresponding Python object.
  955. """
  956. warn_deprecation('load', 'load', arg="typ='unsafe', pure=True")
  957. if Loader is None:
  958. warnings.warn(UnsafeLoaderWarning.text, UnsafeLoaderWarning, stacklevel=2)
  959. Loader = UnsafeLoader
  960. loader = Loader(stream, version, preserve_quotes=preserve_quotes) # type: Any
  961. try:
  962. return loader._constructor.get_single_data()
  963. finally:
  964. loader._parser.dispose()
  965. try:
  966. loader._reader.reset_reader()
  967. except AttributeError:
  968. pass
  969. try:
  970. loader._scanner.reset_scanner()
  971. except AttributeError:
  972. pass
  973. def load_all(
  974. stream: Any, Loader: Any = None, version: Any = None, preserve_quotes: Any = None,
  975. ) -> Any:
  976. # NOQA
  977. """
  978. Parse all YAML documents in a stream
  979. and produce corresponding Python objects.
  980. """
  981. warn_deprecation('load_all', 'load_all', arg="typ='unsafe', pure=True")
  982. if Loader is None:
  983. warnings.warn(UnsafeLoaderWarning.text, UnsafeLoaderWarning, stacklevel=2)
  984. Loader = UnsafeLoader
  985. loader = Loader(stream, version, preserve_quotes=preserve_quotes) # type: Any
  986. try:
  987. while loader._constructor.check_data():
  988. yield loader._constructor.get_data()
  989. finally:
  990. loader._parser.dispose()
  991. try:
  992. loader._reader.reset_reader()
  993. except AttributeError:
  994. pass
  995. try:
  996. loader._scanner.reset_scanner()
  997. except AttributeError:
  998. pass
  999. def safe_load(stream: StreamTextType, version: Optional[VersionType] = None) -> Any:
  1000. """
  1001. Parse the first YAML document in a stream
  1002. and produce the corresponding Python object.
  1003. Resolve only basic YAML tags.
  1004. """
  1005. warn_deprecation('safe_load', 'load', arg="typ='safe', pure=True")
  1006. return load(stream, SafeLoader, version)
  1007. def safe_load_all(stream: StreamTextType, version: Optional[VersionType] = None) -> Any:
  1008. """
  1009. Parse all YAML documents in a stream
  1010. and produce corresponding Python objects.
  1011. Resolve only basic YAML tags.
  1012. """
  1013. warn_deprecation('safe_load_all', 'load_all', arg="typ='safe', pure=True")
  1014. return load_all(stream, SafeLoader, version)
  1015. def round_trip_load(
  1016. stream: StreamTextType,
  1017. version: Optional[VersionType] = None,
  1018. preserve_quotes: Optional[bool] = None,
  1019. ) -> Any:
  1020. """
  1021. Parse the first YAML document in a stream
  1022. and produce the corresponding Python object.
  1023. Resolve only basic YAML tags.
  1024. """
  1025. warn_deprecation('round_trip_load_all', 'load')
  1026. return load(stream, RoundTripLoader, version, preserve_quotes=preserve_quotes)
  1027. def round_trip_load_all(
  1028. stream: StreamTextType,
  1029. version: Optional[VersionType] = None,
  1030. preserve_quotes: Optional[bool] = None,
  1031. ) -> Any:
  1032. """
  1033. Parse all YAML documents in a stream
  1034. and produce corresponding Python objects.
  1035. Resolve only basic YAML tags.
  1036. """
  1037. warn_deprecation('round_trip_load_all', 'load_all')
  1038. return load_all(stream, RoundTripLoader, version, preserve_quotes=preserve_quotes)
  1039. def emit(
  1040. events: Any,
  1041. stream: Optional[StreamType] = None,
  1042. Dumper: Any = Dumper,
  1043. canonical: Optional[bool] = None,
  1044. indent: Union[int, None] = None,
  1045. width: Optional[int] = None,
  1046. allow_unicode: Optional[bool] = None,
  1047. line_break: Any = None,
  1048. ) -> Any:
  1049. # NOQA
  1050. """
  1051. Emit YAML parsing events into a stream.
  1052. If stream is None, return the produced string instead.
  1053. """
  1054. warn_deprecation('emit', 'emit', arg="typ='safe', pure=True")
  1055. getvalue = None
  1056. if stream is None:
  1057. stream = StringIO()
  1058. getvalue = stream.getvalue
  1059. dumper = Dumper(
  1060. stream,
  1061. canonical=canonical,
  1062. indent=indent,
  1063. width=width,
  1064. allow_unicode=allow_unicode,
  1065. line_break=line_break,
  1066. )
  1067. try:
  1068. for event in events:
  1069. dumper.emit(event)
  1070. finally:
  1071. try:
  1072. dumper._emitter.dispose()
  1073. except AttributeError:
  1074. raise
  1075. dumper.dispose() # cyaml
  1076. if getvalue is not None:
  1077. return getvalue()
  1078. enc = None
  1079. def serialize_all(
  1080. nodes: Any,
  1081. stream: Optional[StreamType] = None,
  1082. Dumper: Any = Dumper,
  1083. canonical: Any = None,
  1084. indent: Optional[int] = None,
  1085. width: Optional[int] = None,
  1086. allow_unicode: Optional[bool] = None,
  1087. line_break: Any = None,
  1088. encoding: Any = enc,
  1089. explicit_start: Optional[bool] = None,
  1090. explicit_end: Optional[bool] = None,
  1091. version: Optional[VersionType] = None,
  1092. tags: Any = None,
  1093. ) -> Any:
  1094. # NOQA
  1095. """
  1096. Serialize a sequence of representation trees into a YAML stream.
  1097. If stream is None, return the produced string instead.
  1098. """
  1099. warn_deprecation('serialize_all', 'serialize_all', arg="typ='safe', pure=True")
  1100. getvalue = None
  1101. if stream is None:
  1102. if encoding is None:
  1103. stream = StringIO()
  1104. else:
  1105. stream = BytesIO()
  1106. getvalue = stream.getvalue
  1107. dumper = Dumper(
  1108. stream,
  1109. canonical=canonical,
  1110. indent=indent,
  1111. width=width,
  1112. allow_unicode=allow_unicode,
  1113. line_break=line_break,
  1114. encoding=encoding,
  1115. version=version,
  1116. tags=tags,
  1117. explicit_start=explicit_start,
  1118. explicit_end=explicit_end,
  1119. )
  1120. try:
  1121. dumper._serializer.open()
  1122. for node in nodes:
  1123. dumper.serialize(node)
  1124. dumper._serializer.close()
  1125. finally:
  1126. try:
  1127. dumper._emitter.dispose()
  1128. except AttributeError:
  1129. raise
  1130. dumper.dispose() # cyaml
  1131. if getvalue is not None:
  1132. return getvalue()
  1133. def serialize(
  1134. node: Any, stream: Optional[StreamType] = None, Dumper: Any = Dumper, **kwds: Any,
  1135. ) -> Any:
  1136. """
  1137. Serialize a representation tree into a YAML stream.
  1138. If stream is None, return the produced string instead.
  1139. """
  1140. warn_deprecation('serialize', 'serialize', arg="typ='safe', pure=True")
  1141. return serialize_all([node], stream, Dumper=Dumper, **kwds)
  1142. def dump_all(
  1143. documents: Any,
  1144. stream: Optional[StreamType] = None,
  1145. Dumper: Any = Dumper,
  1146. default_style: Any = None,
  1147. default_flow_style: Any = None,
  1148. canonical: Optional[bool] = None,
  1149. indent: Optional[int] = None,
  1150. width: Optional[int] = None,
  1151. allow_unicode: Optional[bool] = None,
  1152. line_break: Any = None,
  1153. encoding: Any = enc,
  1154. explicit_start: Optional[bool] = None,
  1155. explicit_end: Optional[bool] = None,
  1156. version: Any = None,
  1157. tags: Any = None,
  1158. block_seq_indent: Any = None,
  1159. top_level_colon_align: Any = None,
  1160. prefix_colon: Any = None,
  1161. ) -> Any:
  1162. # NOQA
  1163. """
  1164. Serialize a sequence of Python objects into a YAML stream.
  1165. If stream is None, return the produced string instead.
  1166. """
  1167. warn_deprecation('dump_all', 'dump_all', arg="typ='unsafe', pure=True")
  1168. getvalue = None
  1169. if top_level_colon_align is True:
  1170. top_level_colon_align = max([len(str(x)) for x in documents[0]])
  1171. if stream is None:
  1172. if encoding is None:
  1173. stream = StringIO()
  1174. else:
  1175. stream = BytesIO()
  1176. getvalue = stream.getvalue
  1177. dumper = Dumper(
  1178. stream,
  1179. default_style=default_style,
  1180. default_flow_style=default_flow_style,
  1181. canonical=canonical,
  1182. indent=indent,
  1183. width=width,
  1184. allow_unicode=allow_unicode,
  1185. line_break=line_break,
  1186. encoding=encoding,
  1187. explicit_start=explicit_start,
  1188. explicit_end=explicit_end,
  1189. version=version,
  1190. tags=tags,
  1191. block_seq_indent=block_seq_indent,
  1192. top_level_colon_align=top_level_colon_align,
  1193. prefix_colon=prefix_colon,
  1194. )
  1195. try:
  1196. dumper._serializer.open()
  1197. for data in documents:
  1198. try:
  1199. dumper._representer.represent(data)
  1200. except AttributeError:
  1201. # nprint(dir(dumper._representer))
  1202. raise
  1203. dumper._serializer.close()
  1204. finally:
  1205. try:
  1206. dumper._emitter.dispose()
  1207. except AttributeError:
  1208. raise
  1209. dumper.dispose() # cyaml
  1210. if getvalue is not None:
  1211. return getvalue()
  1212. return None
  1213. def dump(
  1214. data: Any,
  1215. stream: Optional[StreamType] = None,
  1216. Dumper: Any = Dumper,
  1217. default_style: Any = None,
  1218. default_flow_style: Any = None,
  1219. canonical: Optional[bool] = None,
  1220. indent: Optional[int] = None,
  1221. width: Optional[int] = None,
  1222. allow_unicode: Optional[bool] = None,
  1223. line_break: Any = None,
  1224. encoding: Any = enc,
  1225. explicit_start: Optional[bool] = None,
  1226. explicit_end: Optional[bool] = None,
  1227. version: Optional[VersionType] = None,
  1228. tags: Any = None,
  1229. block_seq_indent: Any = None,
  1230. ) -> Any:
  1231. # NOQA
  1232. """
  1233. Serialize a Python object into a YAML stream.
  1234. If stream is None, return the produced string instead.
  1235. default_style ∈ None, '', '"', "'", '|', '>'
  1236. """
  1237. warn_deprecation('dump', 'dump', arg="typ='unsafe', pure=True")
  1238. return dump_all(
  1239. [data],
  1240. stream,
  1241. Dumper=Dumper,
  1242. default_style=default_style,
  1243. default_flow_style=default_flow_style,
  1244. canonical=canonical,
  1245. indent=indent,
  1246. width=width,
  1247. allow_unicode=allow_unicode,
  1248. line_break=line_break,
  1249. encoding=encoding,
  1250. explicit_start=explicit_start,
  1251. explicit_end=explicit_end,
  1252. version=version,
  1253. tags=tags,
  1254. block_seq_indent=block_seq_indent,
  1255. )
  1256. def safe_dump(data: Any, stream: Optional[StreamType] = None, **kwds: Any) -> Any:
  1257. """
  1258. Serialize a Python object into a YAML stream.
  1259. Produce only basic YAML tags.
  1260. If stream is None, return the produced string instead.
  1261. """
  1262. warn_deprecation('safe_dump', 'dump', arg="typ='safe', pure=True")
  1263. return dump_all([data], stream, Dumper=SafeDumper, **kwds)
  1264. def round_trip_dump(
  1265. data: Any,
  1266. stream: Optional[StreamType] = None,
  1267. Dumper: Any = RoundTripDumper,
  1268. default_style: Any = None,
  1269. default_flow_style: Any = None,
  1270. canonical: Optional[bool] = None,
  1271. indent: Optional[int] = None,
  1272. width: Optional[int] = None,
  1273. allow_unicode: Optional[bool] = None,
  1274. line_break: Any = None,
  1275. encoding: Any = enc,
  1276. explicit_start: Optional[bool] = None,
  1277. explicit_end: Optional[bool] = None,
  1278. version: Optional[VersionType] = None,
  1279. tags: Any = None,
  1280. block_seq_indent: Any = None,
  1281. top_level_colon_align: Any = None,
  1282. prefix_colon: Any = None,
  1283. ) -> Any:
  1284. allow_unicode = True if allow_unicode is None else allow_unicode
  1285. warn_deprecation('round_trip_dump', 'dump')
  1286. return dump_all(
  1287. [data],
  1288. stream,
  1289. Dumper=Dumper,
  1290. default_style=default_style,
  1291. default_flow_style=default_flow_style,
  1292. canonical=canonical,
  1293. indent=indent,
  1294. width=width,
  1295. allow_unicode=allow_unicode,
  1296. line_break=line_break,
  1297. encoding=encoding,
  1298. explicit_start=explicit_start,
  1299. explicit_end=explicit_end,
  1300. version=version,
  1301. tags=tags,
  1302. block_seq_indent=block_seq_indent,
  1303. top_level_colon_align=top_level_colon_align,
  1304. prefix_colon=prefix_colon,
  1305. )
  1306. # Loader/Dumper are no longer composites, to get to the associated
  1307. # Resolver()/Representer(), etc., you need to instantiate the class
  1308. def add_implicit_resolver(
  1309. tag: Any,
  1310. regexp: Any,
  1311. first: Any = None,
  1312. Loader: Any = None,
  1313. Dumper: Any = None,
  1314. resolver: Any = Resolver,
  1315. ) -> None:
  1316. """
  1317. Add an implicit scalar detector.
  1318. If an implicit scalar value matches the given regexp,
  1319. the corresponding tag is assigned to the scalar.
  1320. first is a sequence of possible initial characters or None.
  1321. """
  1322. if Loader is None and Dumper is None:
  1323. resolver.add_implicit_resolver(tag, regexp, first)
  1324. return
  1325. if Loader:
  1326. if hasattr(Loader, 'add_implicit_resolver'):
  1327. Loader.add_implicit_resolver(tag, regexp, first)
  1328. elif issubclass(
  1329. Loader, (BaseLoader, SafeLoader, ruamel.yaml.loader.Loader, RoundTripLoader),
  1330. ):
  1331. Resolver.add_implicit_resolver(tag, regexp, first)
  1332. else:
  1333. raise NotImplementedError
  1334. if Dumper:
  1335. if hasattr(Dumper, 'add_implicit_resolver'):
  1336. Dumper.add_implicit_resolver(tag, regexp, first)
  1337. elif issubclass(
  1338. Dumper, (BaseDumper, SafeDumper, ruamel.yaml.dumper.Dumper, RoundTripDumper),
  1339. ):
  1340. Resolver.add_implicit_resolver(tag, regexp, first)
  1341. else:
  1342. raise NotImplementedError
  1343. # this code currently not tested
  1344. def add_path_resolver(
  1345. tag: Any,
  1346. path: Any,
  1347. kind: Any = None,
  1348. Loader: Any = None,
  1349. Dumper: Any = None,
  1350. resolver: Any = Resolver,
  1351. ) -> None:
  1352. """
  1353. Add a path based resolver for the given tag.
  1354. A path is a list of keys that forms a path
  1355. to a node in the representation tree.
  1356. Keys can be string values, integers, or None.
  1357. """
  1358. if Loader is None and Dumper is None:
  1359. resolver.add_path_resolver(tag, path, kind)
  1360. return
  1361. if Loader:
  1362. if hasattr(Loader, 'add_path_resolver'):
  1363. Loader.add_path_resolver(tag, path, kind)
  1364. elif issubclass(
  1365. Loader, (BaseLoader, SafeLoader, ruamel.yaml.loader.Loader, RoundTripLoader),
  1366. ):
  1367. Resolver.add_path_resolver(tag, path, kind)
  1368. else:
  1369. raise NotImplementedError
  1370. if Dumper:
  1371. if hasattr(Dumper, 'add_path_resolver'):
  1372. Dumper.add_path_resolver(tag, path, kind)
  1373. elif issubclass(
  1374. Dumper, (BaseDumper, SafeDumper, ruamel.yaml.dumper.Dumper, RoundTripDumper),
  1375. ):
  1376. Resolver.add_path_resolver(tag, path, kind)
  1377. else:
  1378. raise NotImplementedError
  1379. def add_constructor(
  1380. tag: Any, object_constructor: Any, Loader: Any = None, constructor: Any = Constructor,
  1381. ) -> None:
  1382. """
  1383. Add an object constructor for the given tag.
  1384. object_onstructor is a function that accepts a Loader instance
  1385. and a node object and produces the corresponding Python object.
  1386. """
  1387. if Loader is None:
  1388. constructor.add_constructor(tag, object_constructor)
  1389. else:
  1390. if hasattr(Loader, 'add_constructor'):
  1391. Loader.add_constructor(tag, object_constructor)
  1392. return
  1393. if issubclass(Loader, BaseLoader):
  1394. BaseConstructor.add_constructor(tag, object_constructor)
  1395. elif issubclass(Loader, SafeLoader):
  1396. SafeConstructor.add_constructor(tag, object_constructor)
  1397. elif issubclass(Loader, Loader):
  1398. Constructor.add_constructor(tag, object_constructor)
  1399. elif issubclass(Loader, RoundTripLoader):
  1400. RoundTripConstructor.add_constructor(tag, object_constructor)
  1401. else:
  1402. raise NotImplementedError
  1403. def add_multi_constructor(
  1404. tag_prefix: Any, multi_constructor: Any, Loader: Any = None, constructor: Any = Constructor, # NOQA
  1405. ) -> None:
  1406. """
  1407. Add a multi-constructor for the given tag prefix.
  1408. Multi-constructor is called for a node if its tag starts with tag_prefix.
  1409. Multi-constructor accepts a Loader instance, a tag suffix,
  1410. and a node object and produces the corresponding Python object.
  1411. """
  1412. if Loader is None:
  1413. constructor.add_multi_constructor(tag_prefix, multi_constructor)
  1414. else:
  1415. if False and hasattr(Loader, 'add_multi_constructor'):
  1416. Loader.add_multi_constructor(tag_prefix, constructor)
  1417. return
  1418. if issubclass(Loader, BaseLoader):
  1419. BaseConstructor.add_multi_constructor(tag_prefix, multi_constructor)
  1420. elif issubclass(Loader, SafeLoader):
  1421. SafeConstructor.add_multi_constructor(tag_prefix, multi_constructor)
  1422. elif issubclass(Loader, ruamel.yaml.loader.Loader):
  1423. Constructor.add_multi_constructor(tag_prefix, multi_constructor)
  1424. elif issubclass(Loader, RoundTripLoader):
  1425. RoundTripConstructor.add_multi_constructor(tag_prefix, multi_constructor)
  1426. else:
  1427. raise NotImplementedError
  1428. def add_representer(
  1429. data_type: Any, object_representer: Any, Dumper: Any = None, representer: Any = Representer, # NOQA
  1430. ) -> None:
  1431. """
  1432. Add a representer for the given type.
  1433. object_representer is a function accepting a Dumper instance
  1434. and an instance of the given data type
  1435. and producing the corresponding representation node.
  1436. """
  1437. if Dumper is None:
  1438. representer.add_representer(data_type, object_representer)
  1439. else:
  1440. if hasattr(Dumper, 'add_representer'):
  1441. Dumper.add_representer(data_type, object_representer)
  1442. return
  1443. if issubclass(Dumper, BaseDumper):
  1444. BaseRepresenter.add_representer(data_type, object_representer)
  1445. elif issubclass(Dumper, SafeDumper):
  1446. SafeRepresenter.add_representer(data_type, object_representer)
  1447. elif issubclass(Dumper, Dumper):
  1448. Representer.add_representer(data_type, object_representer)
  1449. elif issubclass(Dumper, RoundTripDumper):
  1450. RoundTripRepresenter.add_representer(data_type, object_representer)
  1451. else:
  1452. raise NotImplementedError
  1453. # this code currently not tested
  1454. def add_multi_representer(
  1455. data_type: Any, multi_representer: Any, Dumper: Any = None, representer: Any = Representer,
  1456. ) -> None:
  1457. """
  1458. Add a representer for the given type.
  1459. multi_representer is a function accepting a Dumper instance
  1460. and an instance of the given data type or subtype
  1461. and producing the corresponding representation node.
  1462. """
  1463. if Dumper is None:
  1464. representer.add_multi_representer(data_type, multi_representer)
  1465. else:
  1466. if hasattr(Dumper, 'add_multi_representer'):
  1467. Dumper.add_multi_representer(data_type, multi_representer)
  1468. return
  1469. if issubclass(Dumper, BaseDumper):
  1470. BaseRepresenter.add_multi_representer(data_type, multi_representer)
  1471. elif issubclass(Dumper, SafeDumper):
  1472. SafeRepresenter.add_multi_representer(data_type, multi_representer)
  1473. elif issubclass(Dumper, Dumper):
  1474. Representer.add_multi_representer(data_type, multi_representer)
  1475. elif issubclass(Dumper, RoundTripDumper):
  1476. RoundTripRepresenter.add_multi_representer(data_type, multi_representer)
  1477. else:
  1478. raise NotImplementedError
  1479. class YAMLObjectMetaclass(type):
  1480. """
  1481. The metaclass for YAMLObject.
  1482. """
  1483. def __init__(cls, name: Any, bases: Any, kwds: Any) -> None:
  1484. super().__init__(name, bases, kwds)
  1485. if 'yaml_tag' in kwds and kwds['yaml_tag'] is not None:
  1486. cls.yaml_constructor.add_constructor(cls.yaml_tag, cls.from_yaml) # type: ignore
  1487. cls.yaml_representer.add_representer(cls, cls.to_yaml) # type: ignore
  1488. class YAMLObject(with_metaclass(YAMLObjectMetaclass)): # type: ignore
  1489. """
  1490. An object that can dump itself to a YAML stream
  1491. and load itself from a YAML stream.
  1492. """
  1493. __slots__ = () # no direct instantiation, so allow immutable subclasses
  1494. yaml_constructor = Constructor
  1495. yaml_representer = Representer
  1496. yaml_tag: Any = None
  1497. yaml_flow_style: Any = None
  1498. @classmethod
  1499. def from_yaml(cls, constructor: Any, node: Any) -> Any:
  1500. """
  1501. Convert a representation node to a Python object.
  1502. """
  1503. return constructor.construct_yaml_object(node, cls)
  1504. @classmethod
  1505. def to_yaml(cls, representer: Any, data: Any) -> Any:
  1506. """
  1507. Convert a Python object to a representation node.
  1508. """
  1509. return representer.represent_yaml_object(
  1510. cls.yaml_tag, data, cls, flow_style=cls.yaml_flow_style,
  1511. )