main.py 53 KB

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