main.py 55 KB

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