mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	Handle yaml merge keys correcly. (#888)
* Handle yaml merge keys correcly. * Removed old debug bool. * Deleted after request from @OttoWinder. * Small refactoring. Removed unused variable `value` Small refactoring to make the code clearer. Added comments. * Fix merge sequence edge case
This commit is contained in:
		
				
					committed by
					
						 Otto Winter
						Otto Winter
					
				
			
			
				
	
			
			
			
						parent
						
							ea652e3587
						
					
				
				
					commit
					8a08d1fb5d
				
			| @@ -93,50 +93,66 @@ class ESPHomeLoader(yaml.SafeLoader):  # pylint: disable=too-many-ancestors | ||||
|         return super(ESPHomeLoader, self).construct_yaml_seq(node) | ||||
|  | ||||
|     def custom_flatten_mapping(self, node): | ||||
|         pre_merge = [] | ||||
|         post_merge = [] | ||||
|         merge = [] | ||||
|         index = 0 | ||||
|         while index < len(node.value): | ||||
|             if isinstance(node.value[index], yaml.ScalarNode): | ||||
|                 index += 1 | ||||
|                 continue | ||||
|  | ||||
|             key_node, value_node = node.value[index] | ||||
|             if key_node.tag == u'tag:yaml.org,2002:merge': | ||||
|             if key_node.tag == 'tag:yaml.org,2002:merge': | ||||
|                 del node.value[index] | ||||
|  | ||||
|                 if isinstance(value_node, yaml.MappingNode): | ||||
|                     self.custom_flatten_mapping(value_node) | ||||
|                     node.value = node.value[:index] + value_node.value + node.value[index:] | ||||
|                     merge.extend(value_node.value) | ||||
|                 elif isinstance(value_node, yaml.SequenceNode): | ||||
|                     submerge = [] | ||||
|                     for subnode in value_node.value: | ||||
|                         if not isinstance(subnode, yaml.MappingNode): | ||||
|                             raise yaml.constructor.ConstructorError( | ||||
|                                 "while constructing a mapping", node.start_mark, | ||||
|                                 "expected a mapping for merging, but found %{}".format(subnode.id), | ||||
|                                 "expected a mapping for merging, but found {}".format(subnode.id), | ||||
|                                 subnode.start_mark) | ||||
|                         self.custom_flatten_mapping(subnode) | ||||
|                         submerge.append(subnode.value) | ||||
|                     # submerge.reverse() | ||||
|                     node.value = node.value[:index] + submerge + node.value[index:] | ||||
|                 elif isinstance(value_node, yaml.ScalarNode): | ||||
|                     node.value = node.value[:index] + [value_node] + node.value[index:] | ||||
|                     # post_merge.append(value_node) | ||||
|                     submerge.reverse() | ||||
|                     for value in submerge: | ||||
|                         merge.extend(value) | ||||
|                 else: | ||||
|                     raise yaml.constructor.ConstructorError( | ||||
|                         "while constructing a mapping", node.start_mark, | ||||
|                         "expected a mapping or list of mappings for merging, " | ||||
|                         "but found {}".format(value_node.id), value_node.start_mark) | ||||
|             elif key_node.tag == u'tag:yaml.org,2002:value': | ||||
|                 key_node.tag = u'tag:yaml.org,2002:str' | ||||
|             elif key_node.tag == 'tag:yaml.org,2002:value': | ||||
|                 key_node.tag = 'tag:yaml.org,2002:str' | ||||
|                 index += 1 | ||||
|             else: | ||||
|                 index += 1 | ||||
|         if pre_merge: | ||||
|             node.value = pre_merge + node.value | ||||
|         if post_merge: | ||||
|             node.value = node.value + post_merge | ||||
|         if merge: | ||||
|             # https://yaml.org/type/merge.html | ||||
|             # Generate a set of keys that should override values in `merge`. | ||||
|             haystack = {key.value for (key, _) in node.value} | ||||
|  | ||||
|             # Construct a new merge set with values overridden by current mapping or earlier | ||||
|             # sequence entries removed | ||||
|             new_merge = [] | ||||
|  | ||||
|             for key, value in merge: | ||||
|                 if key.value in haystack: | ||||
|                     # key already in the current map or from an earlier merge sequence entry, | ||||
|                     # do not override | ||||
|                     # | ||||
|                     # "... each of its key/value pairs is inserted into the current mapping, | ||||
|                     # unless the key already exists in it." | ||||
|                     # | ||||
|                     # "If the value associated with the merge key is a sequence, then this sequence | ||||
|                     #  is expected to contain mapping nodes and each of these nodes is merged in | ||||
|                     #  turn according to its order in the sequence. Keys in mapping nodes earlier | ||||
|                     #  in the sequence override keys specified in later mapping nodes." | ||||
|                     continue | ||||
|                 new_merge.append((key, value)) | ||||
|                 # Add key node to haystack, for sequence merge values. | ||||
|                 haystack.add(key.value) | ||||
|  | ||||
|             # Merge | ||||
|             node.value = new_merge + node.value | ||||
|  | ||||
|     def custom_construct_pairs(self, node): | ||||
|         pairs = [] | ||||
|   | ||||
		Reference in New Issue
	
	Block a user