Mercurial > repos > shellac > guppy_basecaller
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) |