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)