comparison env/lib/python3.7/site-packages/ruamel/yaml/constructor.py @ 0:26e78fe6e8c4 draft

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