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) |         return super(ESPHomeLoader, self).construct_yaml_seq(node) | ||||||
|  |  | ||||||
|     def custom_flatten_mapping(self, node): |     def custom_flatten_mapping(self, node): | ||||||
|         pre_merge = [] |         merge = [] | ||||||
|         post_merge = [] |  | ||||||
|         index = 0 |         index = 0 | ||||||
|         while index < len(node.value): |         while index < len(node.value): | ||||||
|             if isinstance(node.value[index], yaml.ScalarNode): |  | ||||||
|                 index += 1 |  | ||||||
|                 continue |  | ||||||
|  |  | ||||||
|             key_node, value_node = node.value[index] |             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] |                 del node.value[index] | ||||||
|  |  | ||||||
|                 if isinstance(value_node, yaml.MappingNode): |                 if isinstance(value_node, yaml.MappingNode): | ||||||
|                     self.custom_flatten_mapping(value_node) |                     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): |                 elif isinstance(value_node, yaml.SequenceNode): | ||||||
|                     submerge = [] |                     submerge = [] | ||||||
|                     for subnode in value_node.value: |                     for subnode in value_node.value: | ||||||
|                         if not isinstance(subnode, yaml.MappingNode): |                         if not isinstance(subnode, yaml.MappingNode): | ||||||
|                             raise yaml.constructor.ConstructorError( |                             raise yaml.constructor.ConstructorError( | ||||||
|                                 "while constructing a mapping", node.start_mark, |                                 "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) |                                 subnode.start_mark) | ||||||
|                         self.custom_flatten_mapping(subnode) |                         self.custom_flatten_mapping(subnode) | ||||||
|                         submerge.append(subnode.value) |                         submerge.append(subnode.value) | ||||||
|                     # submerge.reverse() |                     submerge.reverse() | ||||||
|                     node.value = node.value[:index] + submerge + node.value[index:] |                     for value in submerge: | ||||||
|                 elif isinstance(value_node, yaml.ScalarNode): |                         merge.extend(value) | ||||||
|                     node.value = node.value[:index] + [value_node] + node.value[index:] |  | ||||||
|                     # post_merge.append(value_node) |  | ||||||
|                 else: |                 else: | ||||||
|                     raise yaml.constructor.ConstructorError( |                     raise yaml.constructor.ConstructorError( | ||||||
|                         "while constructing a mapping", node.start_mark, |                         "while constructing a mapping", node.start_mark, | ||||||
|                         "expected a mapping or list of mappings for merging, " |                         "expected a mapping or list of mappings for merging, " | ||||||
|                         "but found {}".format(value_node.id), value_node.start_mark) |                         "but found {}".format(value_node.id), value_node.start_mark) | ||||||
|             elif key_node.tag == u'tag:yaml.org,2002:value': |             elif key_node.tag == 'tag:yaml.org,2002:value': | ||||||
|                 key_node.tag = u'tag:yaml.org,2002:str' |                 key_node.tag = 'tag:yaml.org,2002:str' | ||||||
|                 index += 1 |                 index += 1 | ||||||
|             else: |             else: | ||||||
|                 index += 1 |                 index += 1 | ||||||
|         if pre_merge: |         if merge: | ||||||
|             node.value = pre_merge + node.value |             # https://yaml.org/type/merge.html | ||||||
|         if post_merge: |             # Generate a set of keys that should override values in `merge`. | ||||||
|             node.value = node.value + post_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): |     def custom_construct_pairs(self, node): | ||||||
|         pairs = [] |         pairs = [] | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user