source: pyyaml/tags/3.10/lib3/yaml/constructor.py @ 379

Revision 379, 25.0 KB checked in by xi, 3 years ago (diff)

Tagged PyYAML-3.10

Line 
1
2__all__ = ['BaseConstructor', 'SafeConstructor', 'Constructor',
3    'ConstructorError']
4
5from .error import *
6from .nodes import *
7
8import collections, datetime, base64, binascii, re, sys, types
9
10class ConstructorError(MarkedYAMLError):
11    pass
12
13class BaseConstructor:
14
15    yaml_constructors = {}
16    yaml_multi_constructors = {}
17
18    def __init__(self):
19        self.constructed_objects = {}
20        self.recursive_objects = {}
21        self.state_generators = []
22        self.deep_construct = False
23
24    def check_data(self):
25        # If there are more documents available?
26        return self.check_node()
27
28    def get_data(self):
29        # Construct and return the next document.
30        if self.check_node():
31            return self.construct_document(self.get_node())
32
33    def get_single_data(self):
34        # Ensure that the stream contains a single document and construct it.
35        node = self.get_single_node()
36        if node is not None:
37            return self.construct_document(node)
38        return None
39
40    def construct_document(self, node):
41        data = self.construct_object(node)
42        while self.state_generators:
43            state_generators = self.state_generators
44            self.state_generators = []
45            for generator in state_generators:
46                for dummy in generator:
47                    pass
48        self.constructed_objects = {}
49        self.recursive_objects = {}
50        self.deep_construct = False
51        return data
52
53    def construct_object(self, node, deep=False):
54        if node in self.constructed_objects:
55            return self.constructed_objects[node]
56        if deep:
57            old_deep = self.deep_construct
58            self.deep_construct = True
59        if node in self.recursive_objects:
60            raise ConstructorError(None, None,
61                    "found unconstructable recursive node", node.start_mark)
62        self.recursive_objects[node] = None
63        constructor = None
64        tag_suffix = None
65        if node.tag in self.yaml_constructors:
66            constructor = self.yaml_constructors[node.tag]
67        else:
68            for tag_prefix in self.yaml_multi_constructors:
69                if node.tag.startswith(tag_prefix):
70                    tag_suffix = node.tag[len(tag_prefix):]
71                    constructor = self.yaml_multi_constructors[tag_prefix]
72                    break
73            else:
74                if None in self.yaml_multi_constructors:
75                    tag_suffix = node.tag
76                    constructor = self.yaml_multi_constructors[None]
77                elif None in self.yaml_constructors:
78                    constructor = self.yaml_constructors[None]
79                elif isinstance(node, ScalarNode):
80                    constructor = self.__class__.construct_scalar
81                elif isinstance(node, SequenceNode):
82                    constructor = self.__class__.construct_sequence
83                elif isinstance(node, MappingNode):
84                    constructor = self.__class__.construct_mapping
85        if tag_suffix is None:
86            data = constructor(self, node)
87        else:
88            data = constructor(self, tag_suffix, node)
89        if isinstance(data, types.GeneratorType):
90            generator = data
91            data = next(generator)
92            if self.deep_construct:
93                for dummy in generator:
94                    pass
95            else:
96                self.state_generators.append(generator)
97        self.constructed_objects[node] = data
98        del self.recursive_objects[node]
99        if deep:
100            self.deep_construct = old_deep
101        return data
102
103    def construct_scalar(self, node):
104        if not isinstance(node, ScalarNode):
105            raise ConstructorError(None, None,
106                    "expected a scalar node, but found %s" % node.id,
107                    node.start_mark)
108        return node.value
109
110    def construct_sequence(self, node, deep=False):
111        if not isinstance(node, SequenceNode):
112            raise ConstructorError(None, None,
113                    "expected a sequence node, but found %s" % node.id,
114                    node.start_mark)
115        return [self.construct_object(child, deep=deep)
116                for child in node.value]
117
118    def construct_mapping(self, node, deep=False):
119        if not isinstance(node, MappingNode):
120            raise ConstructorError(None, None,
121                    "expected a mapping node, but found %s" % node.id,
122                    node.start_mark)
123        mapping = {}
124        for key_node, value_node in node.value:
125            key = self.construct_object(key_node, deep=deep)
126            if not isinstance(key, collections.Hashable):
127                raise ConstructorError("while constructing a mapping", node.start_mark,
128                        "found unhashable key", key_node.start_mark)
129            value = self.construct_object(value_node, deep=deep)
130            mapping[key] = value
131        return mapping
132
133    def construct_pairs(self, node, deep=False):
134        if not isinstance(node, MappingNode):
135            raise ConstructorError(None, None,
136                    "expected a mapping node, but found %s" % node.id,
137                    node.start_mark)
138        pairs = []
139        for key_node, value_node in node.value:
140            key = self.construct_object(key_node, deep=deep)
141            value = self.construct_object(value_node, deep=deep)
142            pairs.append((key, value))
143        return pairs
144
145    @classmethod
146    def add_constructor(cls, tag, constructor):
147        if not 'yaml_constructors' in cls.__dict__:
148            cls.yaml_constructors = cls.yaml_constructors.copy()
149        cls.yaml_constructors[tag] = constructor
150
151    @classmethod
152    def add_multi_constructor(cls, tag_prefix, multi_constructor):
153        if not 'yaml_multi_constructors' in cls.__dict__:
154            cls.yaml_multi_constructors = cls.yaml_multi_constructors.copy()
155        cls.yaml_multi_constructors[tag_prefix] = multi_constructor
156
157class SafeConstructor(BaseConstructor):
158
159    def construct_scalar(self, node):
160        if isinstance(node, MappingNode):
161            for key_node, value_node in node.value:
162                if key_node.tag == 'tag:yaml.org,2002:value':
163                    return self.construct_scalar(value_node)
164        return super().construct_scalar(node)
165
166    def flatten_mapping(self, node):
167        merge = []
168        index = 0
169        while index < len(node.value):
170            key_node, value_node = node.value[index]
171            if key_node.tag == 'tag:yaml.org,2002:merge':
172                del node.value[index]
173                if isinstance(value_node, MappingNode):
174                    self.flatten_mapping(value_node)
175                    merge.extend(value_node.value)
176                elif isinstance(value_node, SequenceNode):
177                    submerge = []
178                    for subnode in value_node.value:
179                        if not isinstance(subnode, MappingNode):
180                            raise ConstructorError("while constructing a mapping",
181                                    node.start_mark,
182                                    "expected a mapping for merging, but found %s"
183                                    % subnode.id, subnode.start_mark)
184                        self.flatten_mapping(subnode)
185                        submerge.append(subnode.value)
186                    submerge.reverse()
187                    for value in submerge:
188                        merge.extend(value)
189                else:
190                    raise ConstructorError("while constructing a mapping", node.start_mark,
191                            "expected a mapping or list of mappings for merging, but found %s"
192                            % value_node.id, value_node.start_mark)
193            elif key_node.tag == 'tag:yaml.org,2002:value':
194                key_node.tag = 'tag:yaml.org,2002:str'
195                index += 1
196            else:
197                index += 1
198        if merge:
199            node.value = merge + node.value
200
201    def construct_mapping(self, node, deep=False):
202        if isinstance(node, MappingNode):
203            self.flatten_mapping(node)
204        return super().construct_mapping(node, deep=deep)
205
206    def construct_yaml_null(self, node):
207        self.construct_scalar(node)
208        return None
209
210    bool_values = {
211        'yes':      True,
212        'no':       False,
213        'true':     True,
214        'false':    False,
215        'on':       True,
216        'off':      False,
217    }
218
219    def construct_yaml_bool(self, node):
220        value = self.construct_scalar(node)
221        return self.bool_values[value.lower()]
222
223    def construct_yaml_int(self, node):
224        value = self.construct_scalar(node)
225        value = value.replace('_', '')
226        sign = +1
227        if value[0] == '-':
228            sign = -1
229        if value[0] in '+-':
230            value = value[1:]
231        if value == '0':
232            return 0
233        elif value.startswith('0b'):
234            return sign*int(value[2:], 2)
235        elif value.startswith('0x'):
236            return sign*int(value[2:], 16)
237        elif value[0] == '0':
238            return sign*int(value, 8)
239        elif ':' in value:
240            digits = [int(part) for part in value.split(':')]
241            digits.reverse()
242            base = 1
243            value = 0
244            for digit in digits:
245                value += digit*base
246                base *= 60
247            return sign*value
248        else:
249            return sign*int(value)
250
251    inf_value = 1e300
252    while inf_value != inf_value*inf_value:
253        inf_value *= inf_value
254    nan_value = -inf_value/inf_value   # Trying to make a quiet NaN (like C99).
255
256    def construct_yaml_float(self, node):
257        value = self.construct_scalar(node)
258        value = value.replace('_', '').lower()
259        sign = +1
260        if value[0] == '-':
261            sign = -1
262        if value[0] in '+-':
263            value = value[1:]
264        if value == '.inf':
265            return sign*self.inf_value
266        elif value == '.nan':
267            return self.nan_value
268        elif ':' in value:
269            digits = [float(part) for part in value.split(':')]
270            digits.reverse()
271            base = 1
272            value = 0.0
273            for digit in digits:
274                value += digit*base
275                base *= 60
276            return sign*value
277        else:
278            return sign*float(value)
279
280    def construct_yaml_binary(self, node):
281        try:
282            value = self.construct_scalar(node).encode('ascii')
283        except UnicodeEncodeError as exc:
284            raise ConstructorError(None, None,
285                    "failed to convert base64 data into ascii: %s" % exc,
286                    node.start_mark)
287        try:
288            if hasattr(base64, 'decodebytes'):
289                return base64.decodebytes(value)
290            else:
291                return base64.decodestring(value)
292        except binascii.Error as exc:
293            raise ConstructorError(None, None,
294                    "failed to decode base64 data: %s" % exc, node.start_mark)
295
296    timestamp_regexp = re.compile(
297            r'''^(?P<year>[0-9][0-9][0-9][0-9])
298                -(?P<month>[0-9][0-9]?)
299                -(?P<day>[0-9][0-9]?)
300                (?:(?:[Tt]|[ \t]+)
301                (?P<hour>[0-9][0-9]?)
302                :(?P<minute>[0-9][0-9])
303                :(?P<second>[0-9][0-9])
304                (?:\.(?P<fraction>[0-9]*))?
305                (?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
306                (?::(?P<tz_minute>[0-9][0-9]))?))?)?$''', re.X)
307
308    def construct_yaml_timestamp(self, node):
309        value = self.construct_scalar(node)
310        match = self.timestamp_regexp.match(node.value)
311        values = match.groupdict()
312        year = int(values['year'])
313        month = int(values['month'])
314        day = int(values['day'])
315        if not values['hour']:
316            return datetime.date(year, month, day)
317        hour = int(values['hour'])
318        minute = int(values['minute'])
319        second = int(values['second'])
320        fraction = 0
321        if values['fraction']:
322            fraction = values['fraction'][:6]
323            while len(fraction) < 6:
324                fraction += '0'
325            fraction = int(fraction)
326        delta = None
327        if values['tz_sign']:
328            tz_hour = int(values['tz_hour'])
329            tz_minute = int(values['tz_minute'] or 0)
330            delta = datetime.timedelta(hours=tz_hour, minutes=tz_minute)
331            if values['tz_sign'] == '-':
332                delta = -delta
333        data = datetime.datetime(year, month, day, hour, minute, second, fraction)
334        if delta:
335            data -= delta
336        return data
337
338    def construct_yaml_omap(self, node):
339        # Note: we do not check for duplicate keys, because it's too
340        # CPU-expensive.
341        omap = []
342        yield omap
343        if not isinstance(node, SequenceNode):
344            raise ConstructorError("while constructing an ordered map", node.start_mark,
345                    "expected a sequence, but found %s" % node.id, node.start_mark)
346        for subnode in node.value:
347            if not isinstance(subnode, MappingNode):
348                raise ConstructorError("while constructing an ordered map", node.start_mark,
349                        "expected a mapping of length 1, but found %s" % subnode.id,
350                        subnode.start_mark)
351            if len(subnode.value) != 1:
352                raise ConstructorError("while constructing an ordered map", node.start_mark,
353                        "expected a single mapping item, but found %d items" % len(subnode.value),
354                        subnode.start_mark)
355            key_node, value_node = subnode.value[0]
356            key = self.construct_object(key_node)
357            value = self.construct_object(value_node)
358            omap.append((key, value))
359
360    def construct_yaml_pairs(self, node):
361        # Note: the same code as `construct_yaml_omap`.
362        pairs = []
363        yield pairs
364        if not isinstance(node, SequenceNode):
365            raise ConstructorError("while constructing pairs", node.start_mark,
366                    "expected a sequence, but found %s" % node.id, node.start_mark)
367        for subnode in node.value:
368            if not isinstance(subnode, MappingNode):
369                raise ConstructorError("while constructing pairs", node.start_mark,
370                        "expected a mapping of length 1, but found %s" % subnode.id,
371                        subnode.start_mark)
372            if len(subnode.value) != 1:
373                raise ConstructorError("while constructing pairs", node.start_mark,
374                        "expected a single mapping item, but found %d items" % len(subnode.value),
375                        subnode.start_mark)
376            key_node, value_node = subnode.value[0]
377            key = self.construct_object(key_node)
378            value = self.construct_object(value_node)
379            pairs.append((key, value))
380
381    def construct_yaml_set(self, node):
382        data = set()
383        yield data
384        value = self.construct_mapping(node)
385        data.update(value)
386
387    def construct_yaml_str(self, node):
388        return self.construct_scalar(node)
389
390    def construct_yaml_seq(self, node):
391        data = []
392        yield data
393        data.extend(self.construct_sequence(node))
394
395    def construct_yaml_map(self, node):
396        data = {}
397        yield data
398        value = self.construct_mapping(node)
399        data.update(value)
400
401    def construct_yaml_object(self, node, cls):
402        data = cls.__new__(cls)
403        yield data
404        if hasattr(data, '__setstate__'):
405            state = self.construct_mapping(node, deep=True)
406            data.__setstate__(state)
407        else:
408            state = self.construct_mapping(node)
409            data.__dict__.update(state)
410
411    def construct_undefined(self, node):
412        raise ConstructorError(None, None,
413                "could not determine a constructor for the tag %r" % node.tag,
414                node.start_mark)
415
416SafeConstructor.add_constructor(
417        'tag:yaml.org,2002:null',
418        SafeConstructor.construct_yaml_null)
419
420SafeConstructor.add_constructor(
421        'tag:yaml.org,2002:bool',
422        SafeConstructor.construct_yaml_bool)
423
424SafeConstructor.add_constructor(
425        'tag:yaml.org,2002:int',
426        SafeConstructor.construct_yaml_int)
427
428SafeConstructor.add_constructor(
429        'tag:yaml.org,2002:float',
430        SafeConstructor.construct_yaml_float)
431
432SafeConstructor.add_constructor(
433        'tag:yaml.org,2002:binary',
434        SafeConstructor.construct_yaml_binary)
435
436SafeConstructor.add_constructor(
437        'tag:yaml.org,2002:timestamp',
438        SafeConstructor.construct_yaml_timestamp)
439
440SafeConstructor.add_constructor(
441        'tag:yaml.org,2002:omap',
442        SafeConstructor.construct_yaml_omap)
443
444SafeConstructor.add_constructor(
445        'tag:yaml.org,2002:pairs',
446        SafeConstructor.construct_yaml_pairs)
447
448SafeConstructor.add_constructor(
449        'tag:yaml.org,2002:set',
450        SafeConstructor.construct_yaml_set)
451
452SafeConstructor.add_constructor(
453        'tag:yaml.org,2002:str',
454        SafeConstructor.construct_yaml_str)
455
456SafeConstructor.add_constructor(
457        'tag:yaml.org,2002:seq',
458        SafeConstructor.construct_yaml_seq)
459
460SafeConstructor.add_constructor(
461        'tag:yaml.org,2002:map',
462        SafeConstructor.construct_yaml_map)
463
464SafeConstructor.add_constructor(None,
465        SafeConstructor.construct_undefined)
466
467class Constructor(SafeConstructor):
468
469    def construct_python_str(self, node):
470        return self.construct_scalar(node)
471
472    def construct_python_unicode(self, node):
473        return self.construct_scalar(node)
474
475    def construct_python_bytes(self, node):
476        try:
477            value = self.construct_scalar(node).encode('ascii')
478        except UnicodeEncodeError as exc:
479            raise ConstructorError(None, None,
480                    "failed to convert base64 data into ascii: %s" % exc,
481                    node.start_mark)
482        try:
483            if hasattr(base64, 'decodebytes'):
484                return base64.decodebytes(value)
485            else:
486                return base64.decodestring(value)
487        except binascii.Error as exc:
488            raise ConstructorError(None, None,
489                    "failed to decode base64 data: %s" % exc, node.start_mark)
490
491    def construct_python_long(self, node):
492        return self.construct_yaml_int(node)
493
494    def construct_python_complex(self, node):
495       return complex(self.construct_scalar(node))
496
497    def construct_python_tuple(self, node):
498        return tuple(self.construct_sequence(node))
499
500    def find_python_module(self, name, mark):
501        if not name:
502            raise ConstructorError("while constructing a Python module", mark,
503                    "expected non-empty name appended to the tag", mark)
504        try:
505            __import__(name)
506        except ImportError as exc:
507            raise ConstructorError("while constructing a Python module", mark,
508                    "cannot find module %r (%s)" % (name, exc), mark)
509        return sys.modules[name]
510
511    def find_python_name(self, name, mark):
512        if not name:
513            raise ConstructorError("while constructing a Python object", mark,
514                    "expected non-empty name appended to the tag", mark)
515        if '.' in name:
516            module_name, object_name = name.rsplit('.', 1)
517        else:
518            module_name = 'builtins'
519            object_name = name
520        try:
521            __import__(module_name)
522        except ImportError as exc:
523            raise ConstructorError("while constructing a Python object", mark,
524                    "cannot find module %r (%s)" % (module_name, exc), mark)
525        module = sys.modules[module_name]
526        if not hasattr(module, object_name):
527            raise ConstructorError("while constructing a Python object", mark,
528                    "cannot find %r in the module %r"
529                    % (object_name, module.__name__), mark)
530        return getattr(module, object_name)
531
532    def construct_python_name(self, suffix, node):
533        value = self.construct_scalar(node)
534        if value:
535            raise ConstructorError("while constructing a Python name", node.start_mark,
536                    "expected the empty value, but found %r" % value, node.start_mark)
537        return self.find_python_name(suffix, node.start_mark)
538
539    def construct_python_module(self, suffix, node):
540        value = self.construct_scalar(node)
541        if value:
542            raise ConstructorError("while constructing a Python module", node.start_mark,
543                    "expected the empty value, but found %r" % value, node.start_mark)
544        return self.find_python_module(suffix, node.start_mark)
545
546    def make_python_instance(self, suffix, node,
547            args=None, kwds=None, newobj=False):
548        if not args:
549            args = []
550        if not kwds:
551            kwds = {}
552        cls = self.find_python_name(suffix, node.start_mark)
553        if newobj and isinstance(cls, type):
554            return cls.__new__(cls, *args, **kwds)
555        else:
556            return cls(*args, **kwds)
557
558    def set_python_instance_state(self, instance, state):
559        if hasattr(instance, '__setstate__'):
560            instance.__setstate__(state)
561        else:
562            slotstate = {}
563            if isinstance(state, tuple) and len(state) == 2:
564                state, slotstate = state
565            if hasattr(instance, '__dict__'):
566                instance.__dict__.update(state)
567            elif state:
568                slotstate.update(state)
569            for key, value in slotstate.items():
570                setattr(object, key, value)
571
572    def construct_python_object(self, suffix, node):
573        # Format:
574        #   !!python/object:module.name { ... state ... }
575        instance = self.make_python_instance(suffix, node, newobj=True)
576        yield instance
577        deep = hasattr(instance, '__setstate__')
578        state = self.construct_mapping(node, deep=deep)
579        self.set_python_instance_state(instance, state)
580
581    def construct_python_object_apply(self, suffix, node, newobj=False):
582        # Format:
583        #   !!python/object/apply       # (or !!python/object/new)
584        #   args: [ ... arguments ... ]
585        #   kwds: { ... keywords ... }
586        #   state: ... state ...
587        #   listitems: [ ... listitems ... ]
588        #   dictitems: { ... dictitems ... }
589        # or short format:
590        #   !!python/object/apply [ ... arguments ... ]
591        # The difference between !!python/object/apply and !!python/object/new
592        # is how an object is created, check make_python_instance for details.
593        if isinstance(node, SequenceNode):
594            args = self.construct_sequence(node, deep=True)
595            kwds = {}
596            state = {}
597            listitems = []
598            dictitems = {}
599        else:
600            value = self.construct_mapping(node, deep=True)
601            args = value.get('args', [])
602            kwds = value.get('kwds', {})
603            state = value.get('state', {})
604            listitems = value.get('listitems', [])
605            dictitems = value.get('dictitems', {})
606        instance = self.make_python_instance(suffix, node, args, kwds, newobj)
607        if state:
608            self.set_python_instance_state(instance, state)
609        if listitems:
610            instance.extend(listitems)
611        if dictitems:
612            for key in dictitems:
613                instance[key] = dictitems[key]
614        return instance
615
616    def construct_python_object_new(self, suffix, node):
617        return self.construct_python_object_apply(suffix, node, newobj=True)
618
619Constructor.add_constructor(
620    'tag:yaml.org,2002:python/none',
621    Constructor.construct_yaml_null)
622
623Constructor.add_constructor(
624    'tag:yaml.org,2002:python/bool',
625    Constructor.construct_yaml_bool)
626
627Constructor.add_constructor(
628    'tag:yaml.org,2002:python/str',
629    Constructor.construct_python_str)
630
631Constructor.add_constructor(
632    'tag:yaml.org,2002:python/unicode',
633    Constructor.construct_python_unicode)
634
635Constructor.add_constructor(
636    'tag:yaml.org,2002:python/bytes',
637    Constructor.construct_python_bytes)
638
639Constructor.add_constructor(
640    'tag:yaml.org,2002:python/int',
641    Constructor.construct_yaml_int)
642
643Constructor.add_constructor(
644    'tag:yaml.org,2002:python/long',
645    Constructor.construct_python_long)
646
647Constructor.add_constructor(
648    'tag:yaml.org,2002:python/float',
649    Constructor.construct_yaml_float)
650
651Constructor.add_constructor(
652    'tag:yaml.org,2002:python/complex',
653    Constructor.construct_python_complex)
654
655Constructor.add_constructor(
656    'tag:yaml.org,2002:python/list',
657    Constructor.construct_yaml_seq)
658
659Constructor.add_constructor(
660    'tag:yaml.org,2002:python/tuple',
661    Constructor.construct_python_tuple)
662
663Constructor.add_constructor(
664    'tag:yaml.org,2002:python/dict',
665    Constructor.construct_yaml_map)
666
667Constructor.add_multi_constructor(
668    'tag:yaml.org,2002:python/name:',
669    Constructor.construct_python_name)
670
671Constructor.add_multi_constructor(
672    'tag:yaml.org,2002:python/module:',
673    Constructor.construct_python_module)
674
675Constructor.add_multi_constructor(
676    'tag:yaml.org,2002:python/object:',
677    Constructor.construct_python_object)
678
679Constructor.add_multi_constructor(
680    'tag:yaml.org,2002:python/object/apply:',
681    Constructor.construct_python_object_apply)
682
683Constructor.add_multi_constructor(
684    'tag:yaml.org,2002:python/object/new:',
685    Constructor.construct_python_object_new)
686
Note: See TracBrowser for help on using the repository browser.