# vim:fileencoding=utf-8:noet from __future__ import (unicode_literals, division, absolute_import, print_function) import collections import types from functools import wraps from powerline.lint.markedjson.error import MarkedError from powerline.lint.markedjson import nodes from powerline.lint.markedjson.markedvalue import gen_marked_value from powerline.lib.unicode import unicode def marked(func): @wraps(func) def f(self, node, *args, **kwargs): return gen_marked_value(func(self, node, *args, **kwargs), node.start_mark) return f class ConstructorError(MarkedError): pass class BaseConstructor: yaml_constructors = {} def __init__(self): self.constructed_objects = {} self.state_generators = [] self.deep_construct = False def check_data(self): # If there are more documents available? return self.check_node() def get_data(self): # Construct and return the next document. if self.check_node(): return self.construct_document(self.get_node()) def get_single_data(self): # Ensure that the stream contains a single document and construct it. node = self.get_single_node() if node is not None: return self.construct_document(node) return None def construct_document(self, node): data = self.construct_object(node) while self.state_generators: state_generators = self.state_generators self.state_generators = [] for generator in state_generators: for dummy in generator: pass self.constructed_objects = {} self.deep_construct = False return data def construct_object(self, node, deep=False): if node in self.constructed_objects: return self.constructed_objects[node] if deep: old_deep = self.deep_construct self.deep_construct = True constructor = None tag_suffix = None if node.tag in self.yaml_constructors: constructor = self.yaml_constructors[node.tag] else: raise ConstructorError(None, None, 'no constructor for tag %s' % node.tag) if tag_suffix is None: data = constructor(self, node) else: data = constructor(self, tag_suffix, node) if isinstance(data, types.GeneratorType): generator = data data = next(generator) if self.deep_construct: for dummy in generator: pass else: self.state_generators.append(generator) self.constructed_objects[node] = data if deep: self.deep_construct = old_deep return data @marked def construct_scalar(self, node): if not isinstance(node, nodes.ScalarNode): raise ConstructorError( None, None, 'expected a scalar node, but found %s' % node.id, node.start_mark ) return node.value def construct_sequence(self, node, deep=False): if not isinstance(node, nodes.SequenceNode): raise ConstructorError( None, None, 'expected a sequence node, but found %s' % node.id, node.start_mark ) return [ self.construct_object(child, deep=deep) for child in node.value ] @marked def construct_mapping(self, node, deep=False): if not isinstance(node, nodes.MappingNode): raise ConstructorError( None, None, 'expected a mapping node, but found %s' % node.id, node.start_mark ) mapping = {} for key_node, value_node in node.value: key = self.construct_object(key_node, deep=deep) if not isinstance(key, collections.Hashable): self.echoerr( 'While constructing a mapping', node.start_mark, 'found unhashable key', key_node.start_mark ) continue elif type(key.value) != unicode: self.echoerr( 'Error while constructing a mapping', node.start_mark, 'found key that is not a string', key_node.start_mark ) continue elif key in mapping: self.echoerr( 'Error while constructing a mapping', node.start_mark, 'found duplicate key', key_node.start_mark ) continue value = self.construct_object(value_node, deep=deep) mapping[key] = value return mapping @classmethod def add_constructor(cls, tag, constructor): if 'yaml_constructors' not in cls.__dict__: cls.yaml_constructors = cls.yaml_constructors.copy() cls.yaml_constructors[tag] = constructor class Constructor(BaseConstructor): def construct_scalar(self, node): if isinstance(node, nodes.MappingNode): for key_node, value_node in node.value: if key_node.tag == 'tag:yaml.org,2002:value': return self.construct_scalar(value_node) return BaseConstructor.construct_scalar(self, node) def flatten_mapping(self, node): merge = [] index = 0 while index < len(node.value): key_node, value_node = node.value[index] if key_node.tag == 'tag:yaml.org,2002:merge': del node.value[index] if isinstance(value_node, nodes.MappingNode): self.flatten_mapping(value_node) merge.extend(value_node.value) elif isinstance(value_node, nodes.SequenceNode): submerge = [] for subnode in value_node.value: if not isinstance(subnode, nodes.MappingNode): raise ConstructorError( 'while constructing a mapping', node.start_mark, 'expected a mapping for merging, but found %s' % subnode.id, subnode.start_mark ) self.flatten_mapping(subnode) submerge.append(subnode.value) submerge.reverse() for value in submerge: merge.extend(value) else: raise ConstructorError( 'while constructing a mapping', node.start_mark, ('expected a mapping or list of mappings for merging, but found %s' % value_node.id), value_node.start_mark ) elif key_node.tag == 'tag:yaml.org,2002:value': key_node.tag = 'tag:yaml.org,2002:str' index += 1 else: index += 1 if merge: node.value = merge + node.value def construct_mapping(self, node, deep=False): if isinstance(node, nodes.MappingNode): self.flatten_mapping(node) return BaseConstructor.construct_mapping(self, node, deep=deep) @marked def construct_yaml_null(self, node): self.construct_scalar(node) return None @marked def construct_yaml_bool(self, node): value = self.construct_scalar(node).value return bool(value) @marked def construct_yaml_int(self, node): value = self.construct_scalar(node).value sign = +1 if value[0] == '-': sign = -1 if value[0] in '+-': value = value[1:] if value == '0': return 0 else: return sign * int(value) @marked def construct_yaml_float(self, node): value = self.construct_scalar(node).value sign = +1 if value[0] == '-': sign = -1 if value[0] in '+-': value = value[1:] else: return sign * float(value) def construct_yaml_str(self, node): return self.construct_scalar(node) def construct_yaml_seq(self, node): data = gen_marked_value([], node.start_mark) yield data data.extend(self.construct_sequence(node)) def construct_yaml_map(self, node): data = gen_marked_value({}, node.start_mark) yield data value = self.construct_mapping(node) data.update(value) def construct_undefined(self, node): raise ConstructorError( None, None, 'could not determine a constructor for the tag %r' % node.tag, node.start_mark ) Constructor.add_constructor( 'tag:yaml.org,2002:null', Constructor.construct_yaml_null) Constructor.add_constructor( 'tag:yaml.org,2002:bool', Constructor.construct_yaml_bool) Constructor.add_constructor( 'tag:yaml.org,2002:int', Constructor.construct_yaml_int) Constructor.add_constructor( 'tag:yaml.org,2002:float', Constructor.construct_yaml_float) Constructor.add_constructor( 'tag:yaml.org,2002:str', Constructor.construct_yaml_str) Constructor.add_constructor( 'tag:yaml.org,2002:seq', Constructor.construct_yaml_seq) Constructor.add_constructor( 'tag:yaml.org,2002:map', Constructor.construct_yaml_map) Constructor.add_constructor( None, Constructor.construct_undefined)