Mercurial > repos > shellac > sam_consensus_v3
comparison env/lib/python3.9/site-packages/ruamel/yaml/constructor.py @ 0:4f3585e2f14b draft default tip
"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author | shellac |
---|---|
date | Mon, 22 Mar 2021 18:12:50 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4f3585e2f14b |
---|---|
1 # coding: utf-8 | |
2 | |
3 from __future__ import print_function, absolute_import, division | |
4 | |
5 import datetime | |
6 import base64 | |
7 import binascii | |
8 import re | |
9 import sys | |
10 import types | |
11 import warnings | |
12 | |
13 # fmt: off | |
14 from ruamel.yaml.error import (MarkedYAMLError, MarkedYAMLFutureWarning, | |
15 MantissaNoDotYAML1_1Warning) | |
16 from ruamel.yaml.nodes import * # NOQA | |
17 from ruamel.yaml.nodes import (SequenceNode, MappingNode, ScalarNode) | |
18 from ruamel.yaml.compat import (utf8, builtins_module, to_str, PY2, PY3, # NOQA | |
19 text_type, nprint, nprintf, version_tnf) | |
20 from ruamel.yaml.compat import ordereddict, Hashable, MutableSequence # type: ignore | |
21 from ruamel.yaml.compat import MutableMapping # type: ignore | |
22 | |
23 from ruamel.yaml.comments import * # NOQA | |
24 from ruamel.yaml.comments import (CommentedMap, CommentedOrderedMap, CommentedSet, | |
25 CommentedKeySeq, CommentedSeq, TaggedScalar, | |
26 CommentedKeyMap) | |
27 from ruamel.yaml.scalarstring import (SingleQuotedScalarString, DoubleQuotedScalarString, | |
28 LiteralScalarString, FoldedScalarString, | |
29 PlainScalarString, ScalarString,) | |
30 from ruamel.yaml.scalarint import ScalarInt, BinaryInt, OctalInt, HexInt, HexCapsInt | |
31 from ruamel.yaml.scalarfloat import ScalarFloat | |
32 from ruamel.yaml.scalarbool import ScalarBoolean | |
33 from ruamel.yaml.timestamp import TimeStamp | |
34 from ruamel.yaml.util import RegExp | |
35 | |
36 if False: # MYPY | |
37 from typing import Any, Dict, List, Set, Generator, Union, Optional # NOQA | |
38 | |
39 | |
40 __all__ = ['BaseConstructor', 'SafeConstructor', 'Constructor', | |
41 'ConstructorError', 'RoundTripConstructor'] | |
42 # fmt: on | |
43 | |
44 | |
45 class ConstructorError(MarkedYAMLError): | |
46 pass | |
47 | |
48 | |
49 class DuplicateKeyFutureWarning(MarkedYAMLFutureWarning): | |
50 pass | |
51 | |
52 | |
53 class DuplicateKeyError(MarkedYAMLFutureWarning): | |
54 pass | |
55 | |
56 | |
57 class BaseConstructor(object): | |
58 | |
59 yaml_constructors = {} # type: Dict[Any, Any] | |
60 yaml_multi_constructors = {} # type: Dict[Any, Any] | |
61 | |
62 def __init__(self, preserve_quotes=None, loader=None): | |
63 # type: (Optional[bool], Any) -> None | |
64 self.loader = loader | |
65 if self.loader is not None and getattr(self.loader, '_constructor', None) is None: | |
66 self.loader._constructor = self | |
67 self.loader = loader | |
68 self.yaml_base_dict_type = dict | |
69 self.yaml_base_list_type = list | |
70 self.constructed_objects = {} # type: Dict[Any, Any] | |
71 self.recursive_objects = {} # type: Dict[Any, Any] | |
72 self.state_generators = [] # type: List[Any] | |
73 self.deep_construct = False | |
74 self._preserve_quotes = preserve_quotes | |
75 self.allow_duplicate_keys = version_tnf((0, 15, 1), (0, 16)) | |
76 | |
77 @property | |
78 def composer(self): | |
79 # type: () -> Any | |
80 if hasattr(self.loader, 'typ'): | |
81 return self.loader.composer | |
82 try: | |
83 return self.loader._composer | |
84 except AttributeError: | |
85 sys.stdout.write('slt {}\n'.format(type(self))) | |
86 sys.stdout.write('slc {}\n'.format(self.loader._composer)) | |
87 sys.stdout.write('{}\n'.format(dir(self))) | |
88 raise | |
89 | |
90 @property | |
91 def resolver(self): | |
92 # type: () -> Any | |
93 if hasattr(self.loader, 'typ'): | |
94 return self.loader.resolver | |
95 return self.loader._resolver | |
96 | |
97 def check_data(self): | |
98 # type: () -> Any | |
99 # If there are more documents available? | |
100 return self.composer.check_node() | |
101 | |
102 def get_data(self): | |
103 # type: () -> Any | |
104 # Construct and return the next document. | |
105 if self.composer.check_node(): | |
106 return self.construct_document(self.composer.get_node()) | |
107 | |
108 def get_single_data(self): | |
109 # type: () -> Any | |
110 # Ensure that the stream contains a single document and construct it. | |
111 node = self.composer.get_single_node() | |
112 if node is not None: | |
113 return self.construct_document(node) | |
114 return None | |
115 | |
116 def construct_document(self, node): | |
117 # type: (Any) -> Any | |
118 data = self.construct_object(node) | |
119 while bool(self.state_generators): | |
120 state_generators = self.state_generators | |
121 self.state_generators = [] | |
122 for generator in state_generators: | |
123 for _dummy in generator: | |
124 pass | |
125 self.constructed_objects = {} | |
126 self.recursive_objects = {} | |
127 self.deep_construct = False | |
128 return data | |
129 | |
130 def construct_object(self, node, deep=False): | |
131 # type: (Any, bool) -> Any | |
132 """deep is True when creating an object/mapping recursively, | |
133 in that case want the underlying elements available during construction | |
134 """ | |
135 if node in self.constructed_objects: | |
136 return self.constructed_objects[node] | |
137 if deep: | |
138 old_deep = self.deep_construct | |
139 self.deep_construct = True | |
140 if node in self.recursive_objects: | |
141 return self.recursive_objects[node] | |
142 # raise ConstructorError( | |
143 # None, None, 'found unconstructable recursive node', node.start_mark | |
144 # ) | |
145 self.recursive_objects[node] = None | |
146 data = self.construct_non_recursive_object(node) | |
147 | |
148 self.constructed_objects[node] = data | |
149 del self.recursive_objects[node] | |
150 if deep: | |
151 self.deep_construct = old_deep | |
152 return data | |
153 | |
154 def construct_non_recursive_object(self, node, tag=None): | |
155 # type: (Any, Optional[str]) -> Any | |
156 constructor = None # type: Any | |
157 tag_suffix = None | |
158 if tag is None: | |
159 tag = node.tag | |
160 if tag in self.yaml_constructors: | |
161 constructor = self.yaml_constructors[tag] | |
162 else: | |
163 for tag_prefix in self.yaml_multi_constructors: | |
164 if tag.startswith(tag_prefix): | |
165 tag_suffix = tag[len(tag_prefix) :] | |
166 constructor = self.yaml_multi_constructors[tag_prefix] | |
167 break | |
168 else: | |
169 if None in self.yaml_multi_constructors: | |
170 tag_suffix = tag | |
171 constructor = self.yaml_multi_constructors[None] | |
172 elif None in self.yaml_constructors: | |
173 constructor = self.yaml_constructors[None] | |
174 elif isinstance(node, ScalarNode): | |
175 constructor = self.__class__.construct_scalar | |
176 elif isinstance(node, SequenceNode): | |
177 constructor = self.__class__.construct_sequence | |
178 elif isinstance(node, MappingNode): | |
179 constructor = self.__class__.construct_mapping | |
180 if tag_suffix is None: | |
181 data = constructor(self, node) | |
182 else: | |
183 data = constructor(self, tag_suffix, node) | |
184 if isinstance(data, types.GeneratorType): | |
185 generator = data | |
186 data = next(generator) | |
187 if self.deep_construct: | |
188 for _dummy in generator: | |
189 pass | |
190 else: | |
191 self.state_generators.append(generator) | |
192 return data | |
193 | |
194 def construct_scalar(self, node): | |
195 # type: (Any) -> Any | |
196 if not isinstance(node, ScalarNode): | |
197 raise ConstructorError( | |
198 None, None, 'expected a scalar node, but found %s' % node.id, node.start_mark | |
199 ) | |
200 return node.value | |
201 | |
202 def construct_sequence(self, node, deep=False): | |
203 # type: (Any, bool) -> Any | |
204 """deep is True when creating an object/mapping recursively, | |
205 in that case want the underlying elements available during construction | |
206 """ | |
207 if not isinstance(node, SequenceNode): | |
208 raise ConstructorError( | |
209 None, None, 'expected a sequence node, but found %s' % node.id, node.start_mark | |
210 ) | |
211 return [self.construct_object(child, deep=deep) for child in node.value] | |
212 | |
213 def construct_mapping(self, node, deep=False): | |
214 # type: (Any, bool) -> Any | |
215 """deep is True when creating an object/mapping recursively, | |
216 in that case want the underlying elements available during construction | |
217 """ | |
218 if not isinstance(node, MappingNode): | |
219 raise ConstructorError( | |
220 None, None, 'expected a mapping node, but found %s' % node.id, node.start_mark | |
221 ) | |
222 total_mapping = self.yaml_base_dict_type() | |
223 if getattr(node, 'merge', None) is not None: | |
224 todo = [(node.merge, False), (node.value, False)] | |
225 else: | |
226 todo = [(node.value, True)] | |
227 for values, check in todo: | |
228 mapping = self.yaml_base_dict_type() # type: Dict[Any, Any] | |
229 for key_node, value_node in values: | |
230 # keys can be list -> deep | |
231 key = self.construct_object(key_node, deep=True) | |
232 # lists are not hashable, but tuples are | |
233 if not isinstance(key, Hashable): | |
234 if isinstance(key, list): | |
235 key = tuple(key) | |
236 if PY2: | |
237 try: | |
238 hash(key) | |
239 except TypeError as exc: | |
240 raise ConstructorError( | |
241 'while constructing a mapping', | |
242 node.start_mark, | |
243 'found unacceptable key (%s)' % exc, | |
244 key_node.start_mark, | |
245 ) | |
246 else: | |
247 if not isinstance(key, Hashable): | |
248 raise ConstructorError( | |
249 'while constructing a mapping', | |
250 node.start_mark, | |
251 'found unhashable key', | |
252 key_node.start_mark, | |
253 ) | |
254 | |
255 value = self.construct_object(value_node, deep=deep) | |
256 if check: | |
257 if self.check_mapping_key(node, key_node, mapping, key, value): | |
258 mapping[key] = value | |
259 else: | |
260 mapping[key] = value | |
261 total_mapping.update(mapping) | |
262 return total_mapping | |
263 | |
264 def check_mapping_key(self, node, key_node, mapping, key, value): | |
265 # type: (Any, Any, Any, Any, Any) -> bool | |
266 """return True if key is unique""" | |
267 if key in mapping: | |
268 if not self.allow_duplicate_keys: | |
269 mk = mapping.get(key) | |
270 if PY2: | |
271 if isinstance(key, unicode): | |
272 key = key.encode('utf-8') | |
273 if isinstance(value, unicode): | |
274 value = value.encode('utf-8') | |
275 if isinstance(mk, unicode): | |
276 mk = mk.encode('utf-8') | |
277 args = [ | |
278 'while constructing a mapping', | |
279 node.start_mark, | |
280 'found duplicate key "{}" with value "{}" ' | |
281 '(original value: "{}")'.format(key, value, mk), | |
282 key_node.start_mark, | |
283 """ | |
284 To suppress this check see: | |
285 http://yaml.readthedocs.io/en/latest/api.html#duplicate-keys | |
286 """, | |
287 """\ | |
288 Duplicate keys will become an error in future releases, and are errors | |
289 by default when using the new API. | |
290 """, | |
291 ] | |
292 if self.allow_duplicate_keys is None: | |
293 warnings.warn(DuplicateKeyFutureWarning(*args)) | |
294 else: | |
295 raise DuplicateKeyError(*args) | |
296 return False | |
297 return True | |
298 | |
299 def check_set_key(self, node, key_node, setting, key): | |
300 # type: (Any, Any, Any, Any, Any) -> None | |
301 if key in setting: | |
302 if not self.allow_duplicate_keys: | |
303 if PY2: | |
304 if isinstance(key, unicode): | |
305 key = key.encode('utf-8') | |
306 args = [ | |
307 'while constructing a set', | |
308 node.start_mark, | |
309 'found duplicate key "{}"'.format(key), | |
310 key_node.start_mark, | |
311 """ | |
312 To suppress this check see: | |
313 http://yaml.readthedocs.io/en/latest/api.html#duplicate-keys | |
314 """, | |
315 """\ | |
316 Duplicate keys will become an error in future releases, and are errors | |
317 by default when using the new API. | |
318 """, | |
319 ] | |
320 if self.allow_duplicate_keys is None: | |
321 warnings.warn(DuplicateKeyFutureWarning(*args)) | |
322 else: | |
323 raise DuplicateKeyError(*args) | |
324 | |
325 def construct_pairs(self, node, deep=False): | |
326 # type: (Any, bool) -> Any | |
327 if not isinstance(node, MappingNode): | |
328 raise ConstructorError( | |
329 None, None, 'expected a mapping node, but found %s' % node.id, node.start_mark | |
330 ) | |
331 pairs = [] | |
332 for key_node, value_node in node.value: | |
333 key = self.construct_object(key_node, deep=deep) | |
334 value = self.construct_object(value_node, deep=deep) | |
335 pairs.append((key, value)) | |
336 return pairs | |
337 | |
338 @classmethod | |
339 def add_constructor(cls, tag, constructor): | |
340 # type: (Any, Any) -> None | |
341 if 'yaml_constructors' not in cls.__dict__: | |
342 cls.yaml_constructors = cls.yaml_constructors.copy() | |
343 cls.yaml_constructors[tag] = constructor | |
344 | |
345 @classmethod | |
346 def add_multi_constructor(cls, tag_prefix, multi_constructor): | |
347 # type: (Any, Any) -> None | |
348 if 'yaml_multi_constructors' not in cls.__dict__: | |
349 cls.yaml_multi_constructors = cls.yaml_multi_constructors.copy() | |
350 cls.yaml_multi_constructors[tag_prefix] = multi_constructor | |
351 | |
352 | |
353 class SafeConstructor(BaseConstructor): | |
354 def construct_scalar(self, node): | |
355 # type: (Any) -> Any | |
356 if isinstance(node, MappingNode): | |
357 for key_node, value_node in node.value: | |
358 if key_node.tag == u'tag:yaml.org,2002:value': | |
359 return self.construct_scalar(value_node) | |
360 return BaseConstructor.construct_scalar(self, node) | |
361 | |
362 def flatten_mapping(self, node): | |
363 # type: (Any) -> Any | |
364 """ | |
365 This implements the merge key feature http://yaml.org/type/merge.html | |
366 by inserting keys from the merge dict/list of dicts if not yet | |
367 available in this node | |
368 """ | |
369 merge = [] # type: List[Any] | |
370 index = 0 | |
371 while index < len(node.value): | |
372 key_node, value_node = node.value[index] | |
373 if key_node.tag == u'tag:yaml.org,2002:merge': | |
374 if merge: # double << key | |
375 if self.allow_duplicate_keys: | |
376 del node.value[index] | |
377 index += 1 | |
378 continue | |
379 args = [ | |
380 'while constructing a mapping', | |
381 node.start_mark, | |
382 'found duplicate key "{}"'.format(key_node.value), | |
383 key_node.start_mark, | |
384 """ | |
385 To suppress this check see: | |
386 http://yaml.readthedocs.io/en/latest/api.html#duplicate-keys | |
387 """, | |
388 """\ | |
389 Duplicate keys will become an error in future releases, and are errors | |
390 by default when using the new API. | |
391 """, | |
392 ] | |
393 if self.allow_duplicate_keys is None: | |
394 warnings.warn(DuplicateKeyFutureWarning(*args)) | |
395 else: | |
396 raise DuplicateKeyError(*args) | |
397 del node.value[index] | |
398 if isinstance(value_node, MappingNode): | |
399 self.flatten_mapping(value_node) | |
400 merge.extend(value_node.value) | |
401 elif isinstance(value_node, SequenceNode): | |
402 submerge = [] | |
403 for subnode in value_node.value: | |
404 if not isinstance(subnode, MappingNode): | |
405 raise ConstructorError( | |
406 'while constructing a mapping', | |
407 node.start_mark, | |
408 'expected a mapping for merging, but found %s' % subnode.id, | |
409 subnode.start_mark, | |
410 ) | |
411 self.flatten_mapping(subnode) | |
412 submerge.append(subnode.value) | |
413 submerge.reverse() | |
414 for value in submerge: | |
415 merge.extend(value) | |
416 else: | |
417 raise ConstructorError( | |
418 'while constructing a mapping', | |
419 node.start_mark, | |
420 'expected a mapping or list of mappings for merging, ' | |
421 'but found %s' % value_node.id, | |
422 value_node.start_mark, | |
423 ) | |
424 elif key_node.tag == u'tag:yaml.org,2002:value': | |
425 key_node.tag = u'tag:yaml.org,2002:str' | |
426 index += 1 | |
427 else: | |
428 index += 1 | |
429 if bool(merge): | |
430 node.merge = merge # separate merge keys to be able to update without duplicate | |
431 node.value = merge + node.value | |
432 | |
433 def construct_mapping(self, node, deep=False): | |
434 # type: (Any, bool) -> Any | |
435 """deep is True when creating an object/mapping recursively, | |
436 in that case want the underlying elements available during construction | |
437 """ | |
438 if isinstance(node, MappingNode): | |
439 self.flatten_mapping(node) | |
440 return BaseConstructor.construct_mapping(self, node, deep=deep) | |
441 | |
442 def construct_yaml_null(self, node): | |
443 # type: (Any) -> Any | |
444 self.construct_scalar(node) | |
445 return None | |
446 | |
447 # YAML 1.2 spec doesn't mention yes/no etc any more, 1.1 does | |
448 bool_values = { | |
449 u'yes': True, | |
450 u'no': False, | |
451 u'y': True, | |
452 u'n': False, | |
453 u'true': True, | |
454 u'false': False, | |
455 u'on': True, | |
456 u'off': False, | |
457 } | |
458 | |
459 def construct_yaml_bool(self, node): | |
460 # type: (Any) -> bool | |
461 value = self.construct_scalar(node) | |
462 return self.bool_values[value.lower()] | |
463 | |
464 def construct_yaml_int(self, node): | |
465 # type: (Any) -> int | |
466 value_s = to_str(self.construct_scalar(node)) | |
467 value_s = value_s.replace('_', "") | |
468 sign = +1 | |
469 if value_s[0] == '-': | |
470 sign = -1 | |
471 if value_s[0] in '+-': | |
472 value_s = value_s[1:] | |
473 if value_s == '0': | |
474 return 0 | |
475 elif value_s.startswith('0b'): | |
476 return sign * int(value_s[2:], 2) | |
477 elif value_s.startswith('0x'): | |
478 return sign * int(value_s[2:], 16) | |
479 elif value_s.startswith('0o'): | |
480 return sign * int(value_s[2:], 8) | |
481 elif self.resolver.processing_version == (1, 1) and value_s[0] == '0': | |
482 return sign * int(value_s, 8) | |
483 elif self.resolver.processing_version == (1, 1) and ':' in value_s: | |
484 digits = [int(part) for part in value_s.split(':')] | |
485 digits.reverse() | |
486 base = 1 | |
487 value = 0 | |
488 for digit in digits: | |
489 value += digit * base | |
490 base *= 60 | |
491 return sign * value | |
492 else: | |
493 return sign * int(value_s) | |
494 | |
495 inf_value = 1e300 | |
496 while inf_value != inf_value * inf_value: | |
497 inf_value *= inf_value | |
498 nan_value = -inf_value / inf_value # Trying to make a quiet NaN (like C99). | |
499 | |
500 def construct_yaml_float(self, node): | |
501 # type: (Any) -> float | |
502 value_so = to_str(self.construct_scalar(node)) | |
503 value_s = value_so.replace('_', "").lower() | |
504 sign = +1 | |
505 if value_s[0] == '-': | |
506 sign = -1 | |
507 if value_s[0] in '+-': | |
508 value_s = value_s[1:] | |
509 if value_s == '.inf': | |
510 return sign * self.inf_value | |
511 elif value_s == '.nan': | |
512 return self.nan_value | |
513 elif self.resolver.processing_version != (1, 2) and ':' in value_s: | |
514 digits = [float(part) for part in value_s.split(':')] | |
515 digits.reverse() | |
516 base = 1 | |
517 value = 0.0 | |
518 for digit in digits: | |
519 value += digit * base | |
520 base *= 60 | |
521 return sign * value | |
522 else: | |
523 if self.resolver.processing_version != (1, 2) and 'e' in value_s: | |
524 # value_s is lower case independent of input | |
525 mantissa, exponent = value_s.split('e') | |
526 if '.' not in mantissa: | |
527 warnings.warn(MantissaNoDotYAML1_1Warning(node, value_so)) | |
528 return sign * float(value_s) | |
529 | |
530 if PY3: | |
531 | |
532 def construct_yaml_binary(self, node): | |
533 # type: (Any) -> Any | |
534 try: | |
535 value = self.construct_scalar(node).encode('ascii') | |
536 except UnicodeEncodeError as exc: | |
537 raise ConstructorError( | |
538 None, | |
539 None, | |
540 'failed to convert base64 data into ascii: %s' % exc, | |
541 node.start_mark, | |
542 ) | |
543 try: | |
544 if hasattr(base64, 'decodebytes'): | |
545 return base64.decodebytes(value) | |
546 else: | |
547 return base64.decodestring(value) | |
548 except binascii.Error as exc: | |
549 raise ConstructorError( | |
550 None, None, 'failed to decode base64 data: %s' % exc, node.start_mark | |
551 ) | |
552 | |
553 else: | |
554 | |
555 def construct_yaml_binary(self, node): | |
556 # type: (Any) -> Any | |
557 value = self.construct_scalar(node) | |
558 try: | |
559 return to_str(value).decode('base64') | |
560 except (binascii.Error, UnicodeEncodeError) as exc: | |
561 raise ConstructorError( | |
562 None, None, 'failed to decode base64 data: %s' % exc, node.start_mark | |
563 ) | |
564 | |
565 timestamp_regexp = RegExp( | |
566 u"""^(?P<year>[0-9][0-9][0-9][0-9]) | |
567 -(?P<month>[0-9][0-9]?) | |
568 -(?P<day>[0-9][0-9]?) | |
569 (?:((?P<t>[Tt])|[ \\t]+) # explictly not retaining extra spaces | |
570 (?P<hour>[0-9][0-9]?) | |
571 :(?P<minute>[0-9][0-9]) | |
572 :(?P<second>[0-9][0-9]) | |
573 (?:\\.(?P<fraction>[0-9]*))? | |
574 (?:[ \\t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?) | |
575 (?::(?P<tz_minute>[0-9][0-9]))?))?)?$""", | |
576 re.X, | |
577 ) | |
578 | |
579 def construct_yaml_timestamp(self, node, values=None): | |
580 # type: (Any, Any) -> Any | |
581 if values is None: | |
582 try: | |
583 match = self.timestamp_regexp.match(node.value) | |
584 except TypeError: | |
585 match = None | |
586 if match is None: | |
587 raise ConstructorError( | |
588 None, | |
589 None, | |
590 'failed to construct timestamp from "{}"'.format(node.value), | |
591 node.start_mark, | |
592 ) | |
593 values = match.groupdict() | |
594 year = int(values['year']) | |
595 month = int(values['month']) | |
596 day = int(values['day']) | |
597 if not values['hour']: | |
598 return datetime.date(year, month, day) | |
599 hour = int(values['hour']) | |
600 minute = int(values['minute']) | |
601 second = int(values['second']) | |
602 fraction = 0 | |
603 if values['fraction']: | |
604 fraction_s = values['fraction'][:6] | |
605 while len(fraction_s) < 6: | |
606 fraction_s += '0' | |
607 fraction = int(fraction_s) | |
608 if len(values['fraction']) > 6 and int(values['fraction'][6]) > 4: | |
609 fraction += 1 | |
610 delta = None | |
611 if values['tz_sign']: | |
612 tz_hour = int(values['tz_hour']) | |
613 minutes = values['tz_minute'] | |
614 tz_minute = int(minutes) if minutes else 0 | |
615 delta = datetime.timedelta(hours=tz_hour, minutes=tz_minute) | |
616 if values['tz_sign'] == '-': | |
617 delta = -delta | |
618 # should do something else instead (or hook this up to the preceding if statement | |
619 # in reverse | |
620 # if delta is None: | |
621 # return datetime.datetime(year, month, day, hour, minute, second, fraction) | |
622 # return datetime.datetime(year, month, day, hour, minute, second, fraction, | |
623 # datetime.timezone.utc) | |
624 # the above is not good enough though, should provide tzinfo. In Python3 that is easily | |
625 # doable drop that kind of support for Python2 as it has not native tzinfo | |
626 data = datetime.datetime(year, month, day, hour, minute, second, fraction) | |
627 if delta: | |
628 data -= delta | |
629 return data | |
630 | |
631 def construct_yaml_omap(self, node): | |
632 # type: (Any) -> Any | |
633 # Note: we do now check for duplicate keys | |
634 omap = ordereddict() | |
635 yield omap | |
636 if not isinstance(node, SequenceNode): | |
637 raise ConstructorError( | |
638 'while constructing an ordered map', | |
639 node.start_mark, | |
640 'expected a sequence, but found %s' % node.id, | |
641 node.start_mark, | |
642 ) | |
643 for subnode in node.value: | |
644 if not isinstance(subnode, MappingNode): | |
645 raise ConstructorError( | |
646 'while constructing an ordered map', | |
647 node.start_mark, | |
648 'expected a mapping of length 1, but found %s' % subnode.id, | |
649 subnode.start_mark, | |
650 ) | |
651 if len(subnode.value) != 1: | |
652 raise ConstructorError( | |
653 'while constructing an ordered map', | |
654 node.start_mark, | |
655 'expected a single mapping item, but found %d items' % len(subnode.value), | |
656 subnode.start_mark, | |
657 ) | |
658 key_node, value_node = subnode.value[0] | |
659 key = self.construct_object(key_node) | |
660 assert key not in omap | |
661 value = self.construct_object(value_node) | |
662 omap[key] = value | |
663 | |
664 def construct_yaml_pairs(self, node): | |
665 # type: (Any) -> Any | |
666 # Note: the same code as `construct_yaml_omap`. | |
667 pairs = [] # type: List[Any] | |
668 yield pairs | |
669 if not isinstance(node, SequenceNode): | |
670 raise ConstructorError( | |
671 'while constructing pairs', | |
672 node.start_mark, | |
673 'expected a sequence, but found %s' % node.id, | |
674 node.start_mark, | |
675 ) | |
676 for subnode in node.value: | |
677 if not isinstance(subnode, MappingNode): | |
678 raise ConstructorError( | |
679 'while constructing pairs', | |
680 node.start_mark, | |
681 'expected a mapping of length 1, but found %s' % subnode.id, | |
682 subnode.start_mark, | |
683 ) | |
684 if len(subnode.value) != 1: | |
685 raise ConstructorError( | |
686 'while constructing pairs', | |
687 node.start_mark, | |
688 'expected a single mapping item, but found %d items' % len(subnode.value), | |
689 subnode.start_mark, | |
690 ) | |
691 key_node, value_node = subnode.value[0] | |
692 key = self.construct_object(key_node) | |
693 value = self.construct_object(value_node) | |
694 pairs.append((key, value)) | |
695 | |
696 def construct_yaml_set(self, node): | |
697 # type: (Any) -> Any | |
698 data = set() # type: Set[Any] | |
699 yield data | |
700 value = self.construct_mapping(node) | |
701 data.update(value) | |
702 | |
703 def construct_yaml_str(self, node): | |
704 # type: (Any) -> Any | |
705 value = self.construct_scalar(node) | |
706 if PY3: | |
707 return value | |
708 try: | |
709 return value.encode('ascii') | |
710 except UnicodeEncodeError: | |
711 return value | |
712 | |
713 def construct_yaml_seq(self, node): | |
714 # type: (Any) -> Any | |
715 data = self.yaml_base_list_type() # type: List[Any] | |
716 yield data | |
717 data.extend(self.construct_sequence(node)) | |
718 | |
719 def construct_yaml_map(self, node): | |
720 # type: (Any) -> Any | |
721 data = self.yaml_base_dict_type() # type: Dict[Any, Any] | |
722 yield data | |
723 value = self.construct_mapping(node) | |
724 data.update(value) | |
725 | |
726 def construct_yaml_object(self, node, cls): | |
727 # type: (Any, Any) -> Any | |
728 data = cls.__new__(cls) | |
729 yield data | |
730 if hasattr(data, '__setstate__'): | |
731 state = self.construct_mapping(node, deep=True) | |
732 data.__setstate__(state) | |
733 else: | |
734 state = self.construct_mapping(node) | |
735 data.__dict__.update(state) | |
736 | |
737 def construct_undefined(self, node): | |
738 # type: (Any) -> None | |
739 raise ConstructorError( | |
740 None, | |
741 None, | |
742 'could not determine a constructor for the tag %r' % utf8(node.tag), | |
743 node.start_mark, | |
744 ) | |
745 | |
746 | |
747 SafeConstructor.add_constructor(u'tag:yaml.org,2002:null', SafeConstructor.construct_yaml_null) | |
748 | |
749 SafeConstructor.add_constructor(u'tag:yaml.org,2002:bool', SafeConstructor.construct_yaml_bool) | |
750 | |
751 SafeConstructor.add_constructor(u'tag:yaml.org,2002:int', SafeConstructor.construct_yaml_int) | |
752 | |
753 SafeConstructor.add_constructor( | |
754 u'tag:yaml.org,2002:float', SafeConstructor.construct_yaml_float | |
755 ) | |
756 | |
757 SafeConstructor.add_constructor( | |
758 u'tag:yaml.org,2002:binary', SafeConstructor.construct_yaml_binary | |
759 ) | |
760 | |
761 SafeConstructor.add_constructor( | |
762 u'tag:yaml.org,2002:timestamp', SafeConstructor.construct_yaml_timestamp | |
763 ) | |
764 | |
765 SafeConstructor.add_constructor(u'tag:yaml.org,2002:omap', SafeConstructor.construct_yaml_omap) | |
766 | |
767 SafeConstructor.add_constructor( | |
768 u'tag:yaml.org,2002:pairs', SafeConstructor.construct_yaml_pairs | |
769 ) | |
770 | |
771 SafeConstructor.add_constructor(u'tag:yaml.org,2002:set', SafeConstructor.construct_yaml_set) | |
772 | |
773 SafeConstructor.add_constructor(u'tag:yaml.org,2002:str', SafeConstructor.construct_yaml_str) | |
774 | |
775 SafeConstructor.add_constructor(u'tag:yaml.org,2002:seq', SafeConstructor.construct_yaml_seq) | |
776 | |
777 SafeConstructor.add_constructor(u'tag:yaml.org,2002:map', SafeConstructor.construct_yaml_map) | |
778 | |
779 SafeConstructor.add_constructor(None, SafeConstructor.construct_undefined) | |
780 | |
781 if PY2: | |
782 | |
783 class classobj: | |
784 pass | |
785 | |
786 | |
787 class Constructor(SafeConstructor): | |
788 def construct_python_str(self, node): | |
789 # type: (Any) -> Any | |
790 return utf8(self.construct_scalar(node)) | |
791 | |
792 def construct_python_unicode(self, node): | |
793 # type: (Any) -> Any | |
794 return self.construct_scalar(node) | |
795 | |
796 if PY3: | |
797 | |
798 def construct_python_bytes(self, node): | |
799 # type: (Any) -> Any | |
800 try: | |
801 value = self.construct_scalar(node).encode('ascii') | |
802 except UnicodeEncodeError as exc: | |
803 raise ConstructorError( | |
804 None, | |
805 None, | |
806 'failed to convert base64 data into ascii: %s' % exc, | |
807 node.start_mark, | |
808 ) | |
809 try: | |
810 if hasattr(base64, 'decodebytes'): | |
811 return base64.decodebytes(value) | |
812 else: | |
813 return base64.decodestring(value) | |
814 except binascii.Error as exc: | |
815 raise ConstructorError( | |
816 None, None, 'failed to decode base64 data: %s' % exc, node.start_mark | |
817 ) | |
818 | |
819 def construct_python_long(self, node): | |
820 # type: (Any) -> int | |
821 val = self.construct_yaml_int(node) | |
822 if PY3: | |
823 return val | |
824 return int(val) | |
825 | |
826 def construct_python_complex(self, node): | |
827 # type: (Any) -> Any | |
828 return complex(self.construct_scalar(node)) | |
829 | |
830 def construct_python_tuple(self, node): | |
831 # type: (Any) -> Any | |
832 return tuple(self.construct_sequence(node)) | |
833 | |
834 def find_python_module(self, name, mark): | |
835 # type: (Any, Any) -> Any | |
836 if not name: | |
837 raise ConstructorError( | |
838 'while constructing a Python module', | |
839 mark, | |
840 'expected non-empty name appended to the tag', | |
841 mark, | |
842 ) | |
843 try: | |
844 __import__(name) | |
845 except ImportError as exc: | |
846 raise ConstructorError( | |
847 'while constructing a Python module', | |
848 mark, | |
849 'cannot find module %r (%s)' % (utf8(name), exc), | |
850 mark, | |
851 ) | |
852 return sys.modules[name] | |
853 | |
854 def find_python_name(self, name, mark): | |
855 # type: (Any, Any) -> Any | |
856 if not name: | |
857 raise ConstructorError( | |
858 'while constructing a Python object', | |
859 mark, | |
860 'expected non-empty name appended to the tag', | |
861 mark, | |
862 ) | |
863 if u'.' in name: | |
864 lname = name.split('.') | |
865 lmodule_name = lname | |
866 lobject_name = [] # type: List[Any] | |
867 while len(lmodule_name) > 1: | |
868 lobject_name.insert(0, lmodule_name.pop()) | |
869 module_name = '.'.join(lmodule_name) | |
870 try: | |
871 __import__(module_name) | |
872 # object_name = '.'.join(object_name) | |
873 break | |
874 except ImportError: | |
875 continue | |
876 else: | |
877 module_name = builtins_module | |
878 lobject_name = [name] | |
879 try: | |
880 __import__(module_name) | |
881 except ImportError as exc: | |
882 raise ConstructorError( | |
883 'while constructing a Python object', | |
884 mark, | |
885 'cannot find module %r (%s)' % (utf8(module_name), exc), | |
886 mark, | |
887 ) | |
888 module = sys.modules[module_name] | |
889 object_name = '.'.join(lobject_name) | |
890 obj = module | |
891 while lobject_name: | |
892 if not hasattr(obj, lobject_name[0]): | |
893 | |
894 raise ConstructorError( | |
895 'while constructing a Python object', | |
896 mark, | |
897 'cannot find %r in the module %r' % (utf8(object_name), module.__name__), | |
898 mark, | |
899 ) | |
900 obj = getattr(obj, lobject_name.pop(0)) | |
901 return obj | |
902 | |
903 def construct_python_name(self, suffix, node): | |
904 # type: (Any, Any) -> Any | |
905 value = self.construct_scalar(node) | |
906 if value: | |
907 raise ConstructorError( | |
908 'while constructing a Python name', | |
909 node.start_mark, | |
910 'expected the empty value, but found %r' % utf8(value), | |
911 node.start_mark, | |
912 ) | |
913 return self.find_python_name(suffix, node.start_mark) | |
914 | |
915 def construct_python_module(self, suffix, node): | |
916 # type: (Any, Any) -> Any | |
917 value = self.construct_scalar(node) | |
918 if value: | |
919 raise ConstructorError( | |
920 'while constructing a Python module', | |
921 node.start_mark, | |
922 'expected the empty value, but found %r' % utf8(value), | |
923 node.start_mark, | |
924 ) | |
925 return self.find_python_module(suffix, node.start_mark) | |
926 | |
927 def make_python_instance(self, suffix, node, args=None, kwds=None, newobj=False): | |
928 # type: (Any, Any, Any, Any, bool) -> Any | |
929 if not args: | |
930 args = [] | |
931 if not kwds: | |
932 kwds = {} | |
933 cls = self.find_python_name(suffix, node.start_mark) | |
934 if PY3: | |
935 if newobj and isinstance(cls, type): | |
936 return cls.__new__(cls, *args, **kwds) | |
937 else: | |
938 return cls(*args, **kwds) | |
939 else: | |
940 if newobj and isinstance(cls, type(classobj)) and not args and not kwds: | |
941 instance = classobj() | |
942 instance.__class__ = cls | |
943 return instance | |
944 elif newobj and isinstance(cls, type): | |
945 return cls.__new__(cls, *args, **kwds) | |
946 else: | |
947 return cls(*args, **kwds) | |
948 | |
949 def set_python_instance_state(self, instance, state): | |
950 # type: (Any, Any) -> None | |
951 if hasattr(instance, '__setstate__'): | |
952 instance.__setstate__(state) | |
953 else: | |
954 slotstate = {} # type: Dict[Any, Any] | |
955 if isinstance(state, tuple) and len(state) == 2: | |
956 state, slotstate = state | |
957 if hasattr(instance, '__dict__'): | |
958 instance.__dict__.update(state) | |
959 elif state: | |
960 slotstate.update(state) | |
961 for key, value in slotstate.items(): | |
962 setattr(object, key, value) | |
963 | |
964 def construct_python_object(self, suffix, node): | |
965 # type: (Any, Any) -> Any | |
966 # Format: | |
967 # !!python/object:module.name { ... state ... } | |
968 instance = self.make_python_instance(suffix, node, newobj=True) | |
969 self.recursive_objects[node] = instance | |
970 yield instance | |
971 deep = hasattr(instance, '__setstate__') | |
972 state = self.construct_mapping(node, deep=deep) | |
973 self.set_python_instance_state(instance, state) | |
974 | |
975 def construct_python_object_apply(self, suffix, node, newobj=False): | |
976 # type: (Any, Any, bool) -> Any | |
977 # Format: | |
978 # !!python/object/apply # (or !!python/object/new) | |
979 # args: [ ... arguments ... ] | |
980 # kwds: { ... keywords ... } | |
981 # state: ... state ... | |
982 # listitems: [ ... listitems ... ] | |
983 # dictitems: { ... dictitems ... } | |
984 # or short format: | |
985 # !!python/object/apply [ ... arguments ... ] | |
986 # The difference between !!python/object/apply and !!python/object/new | |
987 # is how an object is created, check make_python_instance for details. | |
988 if isinstance(node, SequenceNode): | |
989 args = self.construct_sequence(node, deep=True) | |
990 kwds = {} # type: Dict[Any, Any] | |
991 state = {} # type: Dict[Any, Any] | |
992 listitems = [] # type: List[Any] | |
993 dictitems = {} # type: Dict[Any, Any] | |
994 else: | |
995 value = self.construct_mapping(node, deep=True) | |
996 args = value.get('args', []) | |
997 kwds = value.get('kwds', {}) | |
998 state = value.get('state', {}) | |
999 listitems = value.get('listitems', []) | |
1000 dictitems = value.get('dictitems', {}) | |
1001 instance = self.make_python_instance(suffix, node, args, kwds, newobj) | |
1002 if bool(state): | |
1003 self.set_python_instance_state(instance, state) | |
1004 if bool(listitems): | |
1005 instance.extend(listitems) | |
1006 if bool(dictitems): | |
1007 for key in dictitems: | |
1008 instance[key] = dictitems[key] | |
1009 return instance | |
1010 | |
1011 def construct_python_object_new(self, suffix, node): | |
1012 # type: (Any, Any) -> Any | |
1013 return self.construct_python_object_apply(suffix, node, newobj=True) | |
1014 | |
1015 | |
1016 Constructor.add_constructor(u'tag:yaml.org,2002:python/none', Constructor.construct_yaml_null) | |
1017 | |
1018 Constructor.add_constructor(u'tag:yaml.org,2002:python/bool', Constructor.construct_yaml_bool) | |
1019 | |
1020 Constructor.add_constructor(u'tag:yaml.org,2002:python/str', Constructor.construct_python_str) | |
1021 | |
1022 Constructor.add_constructor( | |
1023 u'tag:yaml.org,2002:python/unicode', Constructor.construct_python_unicode | |
1024 ) | |
1025 | |
1026 if PY3: | |
1027 Constructor.add_constructor( | |
1028 u'tag:yaml.org,2002:python/bytes', Constructor.construct_python_bytes | |
1029 ) | |
1030 | |
1031 Constructor.add_constructor(u'tag:yaml.org,2002:python/int', Constructor.construct_yaml_int) | |
1032 | |
1033 Constructor.add_constructor( | |
1034 u'tag:yaml.org,2002:python/long', Constructor.construct_python_long | |
1035 ) | |
1036 | |
1037 Constructor.add_constructor( | |
1038 u'tag:yaml.org,2002:python/float', Constructor.construct_yaml_float | |
1039 ) | |
1040 | |
1041 Constructor.add_constructor( | |
1042 u'tag:yaml.org,2002:python/complex', Constructor.construct_python_complex | |
1043 ) | |
1044 | |
1045 Constructor.add_constructor(u'tag:yaml.org,2002:python/list', Constructor.construct_yaml_seq) | |
1046 | |
1047 Constructor.add_constructor( | |
1048 u'tag:yaml.org,2002:python/tuple', Constructor.construct_python_tuple | |
1049 ) | |
1050 | |
1051 Constructor.add_constructor(u'tag:yaml.org,2002:python/dict', Constructor.construct_yaml_map) | |
1052 | |
1053 Constructor.add_multi_constructor( | |
1054 u'tag:yaml.org,2002:python/name:', Constructor.construct_python_name | |
1055 ) | |
1056 | |
1057 Constructor.add_multi_constructor( | |
1058 u'tag:yaml.org,2002:python/module:', Constructor.construct_python_module | |
1059 ) | |
1060 | |
1061 Constructor.add_multi_constructor( | |
1062 u'tag:yaml.org,2002:python/object:', Constructor.construct_python_object | |
1063 ) | |
1064 | |
1065 Constructor.add_multi_constructor( | |
1066 u'tag:yaml.org,2002:python/object/apply:', Constructor.construct_python_object_apply | |
1067 ) | |
1068 | |
1069 Constructor.add_multi_constructor( | |
1070 u'tag:yaml.org,2002:python/object/new:', Constructor.construct_python_object_new | |
1071 ) | |
1072 | |
1073 | |
1074 class RoundTripConstructor(SafeConstructor): | |
1075 """need to store the comments on the node itself, | |
1076 as well as on the items | |
1077 """ | |
1078 | |
1079 def construct_scalar(self, node): | |
1080 # type: (Any) -> Any | |
1081 if not isinstance(node, ScalarNode): | |
1082 raise ConstructorError( | |
1083 None, None, 'expected a scalar node, but found %s' % node.id, node.start_mark | |
1084 ) | |
1085 | |
1086 if node.style == '|' and isinstance(node.value, text_type): | |
1087 lss = LiteralScalarString(node.value, anchor=node.anchor) | |
1088 if node.comment and node.comment[1]: | |
1089 lss.comment = node.comment[1][0] # type: ignore | |
1090 return lss | |
1091 if node.style == '>' and isinstance(node.value, text_type): | |
1092 fold_positions = [] # type: List[int] | |
1093 idx = -1 | |
1094 while True: | |
1095 idx = node.value.find('\a', idx + 1) | |
1096 if idx < 0: | |
1097 break | |
1098 fold_positions.append(idx - len(fold_positions)) | |
1099 fss = FoldedScalarString(node.value.replace('\a', ''), anchor=node.anchor) | |
1100 if node.comment and node.comment[1]: | |
1101 fss.comment = node.comment[1][0] # type: ignore | |
1102 if fold_positions: | |
1103 fss.fold_pos = fold_positions # type: ignore | |
1104 return fss | |
1105 elif bool(self._preserve_quotes) and isinstance(node.value, text_type): | |
1106 if node.style == "'": | |
1107 return SingleQuotedScalarString(node.value, anchor=node.anchor) | |
1108 if node.style == '"': | |
1109 return DoubleQuotedScalarString(node.value, anchor=node.anchor) | |
1110 if node.anchor: | |
1111 return PlainScalarString(node.value, anchor=node.anchor) | |
1112 return node.value | |
1113 | |
1114 def construct_yaml_int(self, node): | |
1115 # type: (Any) -> Any | |
1116 width = None # type: Any | |
1117 value_su = to_str(self.construct_scalar(node)) | |
1118 try: | |
1119 sx = value_su.rstrip('_') | |
1120 underscore = [len(sx) - sx.rindex('_') - 1, False, False] # type: Any | |
1121 except ValueError: | |
1122 underscore = None | |
1123 except IndexError: | |
1124 underscore = None | |
1125 value_s = value_su.replace('_', "") | |
1126 sign = +1 | |
1127 if value_s[0] == '-': | |
1128 sign = -1 | |
1129 if value_s[0] in '+-': | |
1130 value_s = value_s[1:] | |
1131 if value_s == '0': | |
1132 return 0 | |
1133 elif value_s.startswith('0b'): | |
1134 if self.resolver.processing_version > (1, 1) and value_s[2] == '0': | |
1135 width = len(value_s[2:]) | |
1136 if underscore is not None: | |
1137 underscore[1] = value_su[2] == '_' | |
1138 underscore[2] = len(value_su[2:]) > 1 and value_su[-1] == '_' | |
1139 return BinaryInt( | |
1140 sign * int(value_s[2:], 2), | |
1141 width=width, | |
1142 underscore=underscore, | |
1143 anchor=node.anchor, | |
1144 ) | |
1145 elif value_s.startswith('0x'): | |
1146 # default to lower-case if no a-fA-F in string | |
1147 if self.resolver.processing_version > (1, 1) and value_s[2] == '0': | |
1148 width = len(value_s[2:]) | |
1149 hex_fun = HexInt # type: Any | |
1150 for ch in value_s[2:]: | |
1151 if ch in 'ABCDEF': # first non-digit is capital | |
1152 hex_fun = HexCapsInt | |
1153 break | |
1154 if ch in 'abcdef': | |
1155 break | |
1156 if underscore is not None: | |
1157 underscore[1] = value_su[2] == '_' | |
1158 underscore[2] = len(value_su[2:]) > 1 and value_su[-1] == '_' | |
1159 return hex_fun( | |
1160 sign * int(value_s[2:], 16), | |
1161 width=width, | |
1162 underscore=underscore, | |
1163 anchor=node.anchor, | |
1164 ) | |
1165 elif value_s.startswith('0o'): | |
1166 if self.resolver.processing_version > (1, 1) and value_s[2] == '0': | |
1167 width = len(value_s[2:]) | |
1168 if underscore is not None: | |
1169 underscore[1] = value_su[2] == '_' | |
1170 underscore[2] = len(value_su[2:]) > 1 and value_su[-1] == '_' | |
1171 return OctalInt( | |
1172 sign * int(value_s[2:], 8), | |
1173 width=width, | |
1174 underscore=underscore, | |
1175 anchor=node.anchor, | |
1176 ) | |
1177 elif self.resolver.processing_version != (1, 2) and value_s[0] == '0': | |
1178 return sign * int(value_s, 8) | |
1179 elif self.resolver.processing_version != (1, 2) and ':' in value_s: | |
1180 digits = [int(part) for part in value_s.split(':')] | |
1181 digits.reverse() | |
1182 base = 1 | |
1183 value = 0 | |
1184 for digit in digits: | |
1185 value += digit * base | |
1186 base *= 60 | |
1187 return sign * value | |
1188 elif self.resolver.processing_version > (1, 1) and value_s[0] == '0': | |
1189 # not an octal, an integer with leading zero(s) | |
1190 if underscore is not None: | |
1191 # cannot have a leading underscore | |
1192 underscore[2] = len(value_su) > 1 and value_su[-1] == '_' | |
1193 return ScalarInt(sign * int(value_s), width=len(value_s), underscore=underscore) | |
1194 elif underscore: | |
1195 # cannot have a leading underscore | |
1196 underscore[2] = len(value_su) > 1 and value_su[-1] == '_' | |
1197 return ScalarInt( | |
1198 sign * int(value_s), width=None, underscore=underscore, anchor=node.anchor | |
1199 ) | |
1200 elif node.anchor: | |
1201 return ScalarInt(sign * int(value_s), width=None, anchor=node.anchor) | |
1202 else: | |
1203 return sign * int(value_s) | |
1204 | |
1205 def construct_yaml_float(self, node): | |
1206 # type: (Any) -> Any | |
1207 def leading_zeros(v): | |
1208 # type: (Any) -> int | |
1209 lead0 = 0 | |
1210 idx = 0 | |
1211 while idx < len(v) and v[idx] in '0.': | |
1212 if v[idx] == '0': | |
1213 lead0 += 1 | |
1214 idx += 1 | |
1215 return lead0 | |
1216 | |
1217 # underscore = None | |
1218 m_sign = False # type: Any | |
1219 value_so = to_str(self.construct_scalar(node)) | |
1220 value_s = value_so.replace('_', "").lower() | |
1221 sign = +1 | |
1222 if value_s[0] == '-': | |
1223 sign = -1 | |
1224 if value_s[0] in '+-': | |
1225 m_sign = value_s[0] | |
1226 value_s = value_s[1:] | |
1227 if value_s == '.inf': | |
1228 return sign * self.inf_value | |
1229 if value_s == '.nan': | |
1230 return self.nan_value | |
1231 if self.resolver.processing_version != (1, 2) and ':' in value_s: | |
1232 digits = [float(part) for part in value_s.split(':')] | |
1233 digits.reverse() | |
1234 base = 1 | |
1235 value = 0.0 | |
1236 for digit in digits: | |
1237 value += digit * base | |
1238 base *= 60 | |
1239 return sign * value | |
1240 if 'e' in value_s: | |
1241 try: | |
1242 mantissa, exponent = value_so.split('e') | |
1243 exp = 'e' | |
1244 except ValueError: | |
1245 mantissa, exponent = value_so.split('E') | |
1246 exp = 'E' | |
1247 if self.resolver.processing_version != (1, 2): | |
1248 # value_s is lower case independent of input | |
1249 if '.' not in mantissa: | |
1250 warnings.warn(MantissaNoDotYAML1_1Warning(node, value_so)) | |
1251 lead0 = leading_zeros(mantissa) | |
1252 width = len(mantissa) | |
1253 prec = mantissa.find('.') | |
1254 if m_sign: | |
1255 width -= 1 | |
1256 e_width = len(exponent) | |
1257 e_sign = exponent[0] in '+-' | |
1258 # nprint('sf', width, prec, m_sign, exp, e_width, e_sign) | |
1259 return ScalarFloat( | |
1260 sign * float(value_s), | |
1261 width=width, | |
1262 prec=prec, | |
1263 m_sign=m_sign, | |
1264 m_lead0=lead0, | |
1265 exp=exp, | |
1266 e_width=e_width, | |
1267 e_sign=e_sign, | |
1268 anchor=node.anchor, | |
1269 ) | |
1270 width = len(value_so) | |
1271 prec = value_so.index('.') # you can use index, this would not be float without dot | |
1272 lead0 = leading_zeros(value_so) | |
1273 return ScalarFloat( | |
1274 sign * float(value_s), | |
1275 width=width, | |
1276 prec=prec, | |
1277 m_sign=m_sign, | |
1278 m_lead0=lead0, | |
1279 anchor=node.anchor, | |
1280 ) | |
1281 | |
1282 def construct_yaml_str(self, node): | |
1283 # type: (Any) -> Any | |
1284 value = self.construct_scalar(node) | |
1285 if isinstance(value, ScalarString): | |
1286 return value | |
1287 if PY3: | |
1288 return value | |
1289 try: | |
1290 return value.encode('ascii') | |
1291 except AttributeError: | |
1292 # in case you replace the node dynamically e.g. with a dict | |
1293 return value | |
1294 except UnicodeEncodeError: | |
1295 return value | |
1296 | |
1297 def construct_rt_sequence(self, node, seqtyp, deep=False): | |
1298 # type: (Any, Any, bool) -> Any | |
1299 if not isinstance(node, SequenceNode): | |
1300 raise ConstructorError( | |
1301 None, None, 'expected a sequence node, but found %s' % node.id, node.start_mark | |
1302 ) | |
1303 ret_val = [] | |
1304 if node.comment: | |
1305 seqtyp._yaml_add_comment(node.comment[:2]) | |
1306 if len(node.comment) > 2: | |
1307 seqtyp.yaml_end_comment_extend(node.comment[2], clear=True) | |
1308 if node.anchor: | |
1309 from ruamel.yaml.serializer import templated_id | |
1310 | |
1311 if not templated_id(node.anchor): | |
1312 seqtyp.yaml_set_anchor(node.anchor) | |
1313 for idx, child in enumerate(node.value): | |
1314 ret_val.append(self.construct_object(child, deep=deep)) | |
1315 if child.comment: | |
1316 seqtyp._yaml_add_comment(child.comment, key=idx) | |
1317 seqtyp._yaml_set_idx_line_col( | |
1318 idx, [child.start_mark.line, child.start_mark.column] | |
1319 ) | |
1320 return ret_val | |
1321 | |
1322 def flatten_mapping(self, node): | |
1323 # type: (Any) -> Any | |
1324 """ | |
1325 This implements the merge key feature http://yaml.org/type/merge.html | |
1326 by inserting keys from the merge dict/list of dicts if not yet | |
1327 available in this node | |
1328 """ | |
1329 | |
1330 def constructed(value_node): | |
1331 # type: (Any) -> Any | |
1332 # If the contents of a merge are defined within the | |
1333 # merge marker, then they won't have been constructed | |
1334 # yet. But if they were already constructed, we need to use | |
1335 # the existing object. | |
1336 if value_node in self.constructed_objects: | |
1337 value = self.constructed_objects[value_node] | |
1338 else: | |
1339 value = self.construct_object(value_node, deep=False) | |
1340 return value | |
1341 | |
1342 # merge = [] | |
1343 merge_map_list = [] # type: List[Any] | |
1344 index = 0 | |
1345 while index < len(node.value): | |
1346 key_node, value_node = node.value[index] | |
1347 if key_node.tag == u'tag:yaml.org,2002:merge': | |
1348 if merge_map_list: # double << key | |
1349 if self.allow_duplicate_keys: | |
1350 del node.value[index] | |
1351 index += 1 | |
1352 continue | |
1353 args = [ | |
1354 'while constructing a mapping', | |
1355 node.start_mark, | |
1356 'found duplicate key "{}"'.format(key_node.value), | |
1357 key_node.start_mark, | |
1358 """ | |
1359 To suppress this check see: | |
1360 http://yaml.readthedocs.io/en/latest/api.html#duplicate-keys | |
1361 """, | |
1362 """\ | |
1363 Duplicate keys will become an error in future releases, and are errors | |
1364 by default when using the new API. | |
1365 """, | |
1366 ] | |
1367 if self.allow_duplicate_keys is None: | |
1368 warnings.warn(DuplicateKeyFutureWarning(*args)) | |
1369 else: | |
1370 raise DuplicateKeyError(*args) | |
1371 del node.value[index] | |
1372 if isinstance(value_node, MappingNode): | |
1373 merge_map_list.append((index, constructed(value_node))) | |
1374 # self.flatten_mapping(value_node) | |
1375 # merge.extend(value_node.value) | |
1376 elif isinstance(value_node, SequenceNode): | |
1377 # submerge = [] | |
1378 for subnode in value_node.value: | |
1379 if not isinstance(subnode, MappingNode): | |
1380 raise ConstructorError( | |
1381 'while constructing a mapping', | |
1382 node.start_mark, | |
1383 'expected a mapping for merging, but found %s' % subnode.id, | |
1384 subnode.start_mark, | |
1385 ) | |
1386 merge_map_list.append((index, constructed(subnode))) | |
1387 # self.flatten_mapping(subnode) | |
1388 # submerge.append(subnode.value) | |
1389 # submerge.reverse() | |
1390 # for value in submerge: | |
1391 # merge.extend(value) | |
1392 else: | |
1393 raise ConstructorError( | |
1394 'while constructing a mapping', | |
1395 node.start_mark, | |
1396 'expected a mapping or list of mappings for merging, ' | |
1397 'but found %s' % value_node.id, | |
1398 value_node.start_mark, | |
1399 ) | |
1400 elif key_node.tag == u'tag:yaml.org,2002:value': | |
1401 key_node.tag = u'tag:yaml.org,2002:str' | |
1402 index += 1 | |
1403 else: | |
1404 index += 1 | |
1405 return merge_map_list | |
1406 # if merge: | |
1407 # node.value = merge + node.value | |
1408 | |
1409 def _sentinel(self): | |
1410 # type: () -> None | |
1411 pass | |
1412 | |
1413 def construct_mapping(self, node, maptyp, deep=False): # type: ignore | |
1414 # type: (Any, Any, bool) -> Any | |
1415 if not isinstance(node, MappingNode): | |
1416 raise ConstructorError( | |
1417 None, None, 'expected a mapping node, but found %s' % node.id, node.start_mark | |
1418 ) | |
1419 merge_map = self.flatten_mapping(node) | |
1420 # mapping = {} | |
1421 if node.comment: | |
1422 maptyp._yaml_add_comment(node.comment[:2]) | |
1423 if len(node.comment) > 2: | |
1424 maptyp.yaml_end_comment_extend(node.comment[2], clear=True) | |
1425 if node.anchor: | |
1426 from ruamel.yaml.serializer import templated_id | |
1427 | |
1428 if not templated_id(node.anchor): | |
1429 maptyp.yaml_set_anchor(node.anchor) | |
1430 last_key, last_value = None, self._sentinel | |
1431 for key_node, value_node in node.value: | |
1432 # keys can be list -> deep | |
1433 key = self.construct_object(key_node, deep=True) | |
1434 # lists are not hashable, but tuples are | |
1435 if not isinstance(key, Hashable): | |
1436 if isinstance(key, MutableSequence): | |
1437 key_s = CommentedKeySeq(key) | |
1438 if key_node.flow_style is True: | |
1439 key_s.fa.set_flow_style() | |
1440 elif key_node.flow_style is False: | |
1441 key_s.fa.set_block_style() | |
1442 key = key_s | |
1443 elif isinstance(key, MutableMapping): | |
1444 key_m = CommentedKeyMap(key) | |
1445 if key_node.flow_style is True: | |
1446 key_m.fa.set_flow_style() | |
1447 elif key_node.flow_style is False: | |
1448 key_m.fa.set_block_style() | |
1449 key = key_m | |
1450 if PY2: | |
1451 try: | |
1452 hash(key) | |
1453 except TypeError as exc: | |
1454 raise ConstructorError( | |
1455 'while constructing a mapping', | |
1456 node.start_mark, | |
1457 'found unacceptable key (%s)' % exc, | |
1458 key_node.start_mark, | |
1459 ) | |
1460 else: | |
1461 if not isinstance(key, Hashable): | |
1462 raise ConstructorError( | |
1463 'while constructing a mapping', | |
1464 node.start_mark, | |
1465 'found unhashable key', | |
1466 key_node.start_mark, | |
1467 ) | |
1468 value = self.construct_object(value_node, deep=deep) | |
1469 if self.check_mapping_key(node, key_node, maptyp, key, value): | |
1470 | |
1471 if key_node.comment and len(key_node.comment) > 4 and key_node.comment[4]: | |
1472 if last_value is None: | |
1473 key_node.comment[0] = key_node.comment.pop(4) | |
1474 maptyp._yaml_add_comment(key_node.comment, value=last_key) | |
1475 else: | |
1476 key_node.comment[2] = key_node.comment.pop(4) | |
1477 maptyp._yaml_add_comment(key_node.comment, key=key) | |
1478 key_node.comment = None | |
1479 if key_node.comment: | |
1480 maptyp._yaml_add_comment(key_node.comment, key=key) | |
1481 if value_node.comment: | |
1482 maptyp._yaml_add_comment(value_node.comment, value=key) | |
1483 maptyp._yaml_set_kv_line_col( | |
1484 key, | |
1485 [ | |
1486 key_node.start_mark.line, | |
1487 key_node.start_mark.column, | |
1488 value_node.start_mark.line, | |
1489 value_node.start_mark.column, | |
1490 ], | |
1491 ) | |
1492 maptyp[key] = value | |
1493 last_key, last_value = key, value # could use indexing | |
1494 # do this last, or <<: before a key will prevent insertion in instances | |
1495 # of collections.OrderedDict (as they have no __contains__ | |
1496 if merge_map: | |
1497 maptyp.add_yaml_merge(merge_map) | |
1498 | |
1499 def construct_setting(self, node, typ, deep=False): | |
1500 # type: (Any, Any, bool) -> Any | |
1501 if not isinstance(node, MappingNode): | |
1502 raise ConstructorError( | |
1503 None, None, 'expected a mapping node, but found %s' % node.id, node.start_mark | |
1504 ) | |
1505 if node.comment: | |
1506 typ._yaml_add_comment(node.comment[:2]) | |
1507 if len(node.comment) > 2: | |
1508 typ.yaml_end_comment_extend(node.comment[2], clear=True) | |
1509 if node.anchor: | |
1510 from ruamel.yaml.serializer import templated_id | |
1511 | |
1512 if not templated_id(node.anchor): | |
1513 typ.yaml_set_anchor(node.anchor) | |
1514 for key_node, value_node in node.value: | |
1515 # keys can be list -> deep | |
1516 key = self.construct_object(key_node, deep=True) | |
1517 # lists are not hashable, but tuples are | |
1518 if not isinstance(key, Hashable): | |
1519 if isinstance(key, list): | |
1520 key = tuple(key) | |
1521 if PY2: | |
1522 try: | |
1523 hash(key) | |
1524 except TypeError as exc: | |
1525 raise ConstructorError( | |
1526 'while constructing a mapping', | |
1527 node.start_mark, | |
1528 'found unacceptable key (%s)' % exc, | |
1529 key_node.start_mark, | |
1530 ) | |
1531 else: | |
1532 if not isinstance(key, Hashable): | |
1533 raise ConstructorError( | |
1534 'while constructing a mapping', | |
1535 node.start_mark, | |
1536 'found unhashable key', | |
1537 key_node.start_mark, | |
1538 ) | |
1539 # construct but should be null | |
1540 value = self.construct_object(value_node, deep=deep) # NOQA | |
1541 self.check_set_key(node, key_node, typ, key) | |
1542 if key_node.comment: | |
1543 typ._yaml_add_comment(key_node.comment, key=key) | |
1544 if value_node.comment: | |
1545 typ._yaml_add_comment(value_node.comment, value=key) | |
1546 typ.add(key) | |
1547 | |
1548 def construct_yaml_seq(self, node): | |
1549 # type: (Any) -> Any | |
1550 data = CommentedSeq() | |
1551 data._yaml_set_line_col(node.start_mark.line, node.start_mark.column) | |
1552 if node.comment: | |
1553 data._yaml_add_comment(node.comment) | |
1554 yield data | |
1555 data.extend(self.construct_rt_sequence(node, data)) | |
1556 self.set_collection_style(data, node) | |
1557 | |
1558 def construct_yaml_map(self, node): | |
1559 # type: (Any) -> Any | |
1560 data = CommentedMap() | |
1561 data._yaml_set_line_col(node.start_mark.line, node.start_mark.column) | |
1562 yield data | |
1563 self.construct_mapping(node, data, deep=True) | |
1564 self.set_collection_style(data, node) | |
1565 | |
1566 def set_collection_style(self, data, node): | |
1567 # type: (Any, Any) -> None | |
1568 if len(data) == 0: | |
1569 return | |
1570 if node.flow_style is True: | |
1571 data.fa.set_flow_style() | |
1572 elif node.flow_style is False: | |
1573 data.fa.set_block_style() | |
1574 | |
1575 def construct_yaml_object(self, node, cls): | |
1576 # type: (Any, Any) -> Any | |
1577 data = cls.__new__(cls) | |
1578 yield data | |
1579 if hasattr(data, '__setstate__'): | |
1580 state = SafeConstructor.construct_mapping(self, node, deep=True) | |
1581 data.__setstate__(state) | |
1582 else: | |
1583 state = SafeConstructor.construct_mapping(self, node) | |
1584 data.__dict__.update(state) | |
1585 | |
1586 def construct_yaml_omap(self, node): | |
1587 # type: (Any) -> Any | |
1588 # Note: we do now check for duplicate keys | |
1589 omap = CommentedOrderedMap() | |
1590 omap._yaml_set_line_col(node.start_mark.line, node.start_mark.column) | |
1591 if node.flow_style is True: | |
1592 omap.fa.set_flow_style() | |
1593 elif node.flow_style is False: | |
1594 omap.fa.set_block_style() | |
1595 yield omap | |
1596 if node.comment: | |
1597 omap._yaml_add_comment(node.comment[:2]) | |
1598 if len(node.comment) > 2: | |
1599 omap.yaml_end_comment_extend(node.comment[2], clear=True) | |
1600 if not isinstance(node, SequenceNode): | |
1601 raise ConstructorError( | |
1602 'while constructing an ordered map', | |
1603 node.start_mark, | |
1604 'expected a sequence, but found %s' % node.id, | |
1605 node.start_mark, | |
1606 ) | |
1607 for subnode in node.value: | |
1608 if not isinstance(subnode, MappingNode): | |
1609 raise ConstructorError( | |
1610 'while constructing an ordered map', | |
1611 node.start_mark, | |
1612 'expected a mapping of length 1, but found %s' % subnode.id, | |
1613 subnode.start_mark, | |
1614 ) | |
1615 if len(subnode.value) != 1: | |
1616 raise ConstructorError( | |
1617 'while constructing an ordered map', | |
1618 node.start_mark, | |
1619 'expected a single mapping item, but found %d items' % len(subnode.value), | |
1620 subnode.start_mark, | |
1621 ) | |
1622 key_node, value_node = subnode.value[0] | |
1623 key = self.construct_object(key_node) | |
1624 assert key not in omap | |
1625 value = self.construct_object(value_node) | |
1626 if key_node.comment: | |
1627 omap._yaml_add_comment(key_node.comment, key=key) | |
1628 if subnode.comment: | |
1629 omap._yaml_add_comment(subnode.comment, key=key) | |
1630 if value_node.comment: | |
1631 omap._yaml_add_comment(value_node.comment, value=key) | |
1632 omap[key] = value | |
1633 | |
1634 def construct_yaml_set(self, node): | |
1635 # type: (Any) -> Any | |
1636 data = CommentedSet() | |
1637 data._yaml_set_line_col(node.start_mark.line, node.start_mark.column) | |
1638 yield data | |
1639 self.construct_setting(node, data) | |
1640 | |
1641 def construct_undefined(self, node): | |
1642 # type: (Any) -> Any | |
1643 try: | |
1644 if isinstance(node, MappingNode): | |
1645 data = CommentedMap() | |
1646 data._yaml_set_line_col(node.start_mark.line, node.start_mark.column) | |
1647 if node.flow_style is True: | |
1648 data.fa.set_flow_style() | |
1649 elif node.flow_style is False: | |
1650 data.fa.set_block_style() | |
1651 data.yaml_set_tag(node.tag) | |
1652 yield data | |
1653 if node.anchor: | |
1654 data.yaml_set_anchor(node.anchor) | |
1655 self.construct_mapping(node, data) | |
1656 return | |
1657 elif isinstance(node, ScalarNode): | |
1658 data2 = TaggedScalar() | |
1659 data2.value = self.construct_scalar(node) | |
1660 data2.style = node.style | |
1661 data2.yaml_set_tag(node.tag) | |
1662 yield data2 | |
1663 if node.anchor: | |
1664 data2.yaml_set_anchor(node.anchor, always_dump=True) | |
1665 return | |
1666 elif isinstance(node, SequenceNode): | |
1667 data3 = CommentedSeq() | |
1668 data3._yaml_set_line_col(node.start_mark.line, node.start_mark.column) | |
1669 if node.flow_style is True: | |
1670 data3.fa.set_flow_style() | |
1671 elif node.flow_style is False: | |
1672 data3.fa.set_block_style() | |
1673 data3.yaml_set_tag(node.tag) | |
1674 yield data3 | |
1675 if node.anchor: | |
1676 data3.yaml_set_anchor(node.anchor) | |
1677 data3.extend(self.construct_sequence(node)) | |
1678 return | |
1679 except: # NOQA | |
1680 pass | |
1681 raise ConstructorError( | |
1682 None, | |
1683 None, | |
1684 'could not determine a constructor for the tag %r' % utf8(node.tag), | |
1685 node.start_mark, | |
1686 ) | |
1687 | |
1688 def construct_yaml_timestamp(self, node, values=None): | |
1689 # type: (Any, Any) -> Any | |
1690 try: | |
1691 match = self.timestamp_regexp.match(node.value) | |
1692 except TypeError: | |
1693 match = None | |
1694 if match is None: | |
1695 raise ConstructorError( | |
1696 None, | |
1697 None, | |
1698 'failed to construct timestamp from "{}"'.format(node.value), | |
1699 node.start_mark, | |
1700 ) | |
1701 values = match.groupdict() | |
1702 if not values['hour']: | |
1703 return SafeConstructor.construct_yaml_timestamp(self, node, values) | |
1704 for part in ['t', 'tz_sign', 'tz_hour', 'tz_minute']: | |
1705 if values[part]: | |
1706 break | |
1707 else: | |
1708 return SafeConstructor.construct_yaml_timestamp(self, node, values) | |
1709 year = int(values['year']) | |
1710 month = int(values['month']) | |
1711 day = int(values['day']) | |
1712 hour = int(values['hour']) | |
1713 minute = int(values['minute']) | |
1714 second = int(values['second']) | |
1715 fraction = 0 | |
1716 if values['fraction']: | |
1717 fraction_s = values['fraction'][:6] | |
1718 while len(fraction_s) < 6: | |
1719 fraction_s += '0' | |
1720 fraction = int(fraction_s) | |
1721 if len(values['fraction']) > 6 and int(values['fraction'][6]) > 4: | |
1722 fraction += 1 | |
1723 delta = None | |
1724 if values['tz_sign']: | |
1725 tz_hour = int(values['tz_hour']) | |
1726 minutes = values['tz_minute'] | |
1727 tz_minute = int(minutes) if minutes else 0 | |
1728 delta = datetime.timedelta(hours=tz_hour, minutes=tz_minute) | |
1729 if values['tz_sign'] == '-': | |
1730 delta = -delta | |
1731 if delta: | |
1732 dt = datetime.datetime(year, month, day, hour, minute) | |
1733 dt -= delta | |
1734 data = TimeStamp(dt.year, dt.month, dt.day, dt.hour, dt.minute, second, fraction) | |
1735 data._yaml['delta'] = delta | |
1736 tz = values['tz_sign'] + values['tz_hour'] | |
1737 if values['tz_minute']: | |
1738 tz += ':' + values['tz_minute'] | |
1739 data._yaml['tz'] = tz | |
1740 else: | |
1741 data = TimeStamp(year, month, day, hour, minute, second, fraction) | |
1742 if values['tz']: # no delta | |
1743 data._yaml['tz'] = values['tz'] | |
1744 | |
1745 if values['t']: | |
1746 data._yaml['t'] = True | |
1747 return data | |
1748 | |
1749 def construct_yaml_bool(self, node): | |
1750 # type: (Any) -> Any | |
1751 b = SafeConstructor.construct_yaml_bool(self, node) | |
1752 if node.anchor: | |
1753 return ScalarBoolean(b, anchor=node.anchor) | |
1754 return b | |
1755 | |
1756 | |
1757 RoundTripConstructor.add_constructor( | |
1758 u'tag:yaml.org,2002:null', RoundTripConstructor.construct_yaml_null | |
1759 ) | |
1760 | |
1761 RoundTripConstructor.add_constructor( | |
1762 u'tag:yaml.org,2002:bool', RoundTripConstructor.construct_yaml_bool | |
1763 ) | |
1764 | |
1765 RoundTripConstructor.add_constructor( | |
1766 u'tag:yaml.org,2002:int', RoundTripConstructor.construct_yaml_int | |
1767 ) | |
1768 | |
1769 RoundTripConstructor.add_constructor( | |
1770 u'tag:yaml.org,2002:float', RoundTripConstructor.construct_yaml_float | |
1771 ) | |
1772 | |
1773 RoundTripConstructor.add_constructor( | |
1774 u'tag:yaml.org,2002:binary', RoundTripConstructor.construct_yaml_binary | |
1775 ) | |
1776 | |
1777 RoundTripConstructor.add_constructor( | |
1778 u'tag:yaml.org,2002:timestamp', RoundTripConstructor.construct_yaml_timestamp | |
1779 ) | |
1780 | |
1781 RoundTripConstructor.add_constructor( | |
1782 u'tag:yaml.org,2002:omap', RoundTripConstructor.construct_yaml_omap | |
1783 ) | |
1784 | |
1785 RoundTripConstructor.add_constructor( | |
1786 u'tag:yaml.org,2002:pairs', RoundTripConstructor.construct_yaml_pairs | |
1787 ) | |
1788 | |
1789 RoundTripConstructor.add_constructor( | |
1790 u'tag:yaml.org,2002:set', RoundTripConstructor.construct_yaml_set | |
1791 ) | |
1792 | |
1793 RoundTripConstructor.add_constructor( | |
1794 u'tag:yaml.org,2002:str', RoundTripConstructor.construct_yaml_str | |
1795 ) | |
1796 | |
1797 RoundTripConstructor.add_constructor( | |
1798 u'tag:yaml.org,2002:seq', RoundTripConstructor.construct_yaml_seq | |
1799 ) | |
1800 | |
1801 RoundTripConstructor.add_constructor( | |
1802 u'tag:yaml.org,2002:map', RoundTripConstructor.construct_yaml_map | |
1803 ) | |
1804 | |
1805 RoundTripConstructor.add_constructor(None, RoundTripConstructor.construct_undefined) |