def __init__( self, logger: ConsolePrinter, lhs: Any, config: MergerConfig ) -> None: """ Instantiate this class into an object. Parameters: 1. logger (ConsolePrinter) Instance of ConsoleWriter or subclass 2. lhs (Any) The prime left-hand-side parsed YAML data 3. config (MergerConfig) User-defined document merging rules Returns: N/A Raises: N/A """ self.logger: ConsolePrinter = logger self.config: MergerConfig = config self.data: Any = lhs # ryamel.yaml unfortunately tracks comments AFTER each YAML node. As # such, it is impossible to copy comments from RHS to LHS in any # sensible way. Trying leads to absurd merge results that are data- # accurate but comment-insane. This ruamel.yaml design decision forces # me to simply delete all comments from all merge documents to produce # a sensible result. That said, enable users to attempt to preserve # LHS comments. if not self.config.is_preserving_lhs_comments(): Parsers.delete_all_comments(self.data)
def delete_all_comments(cls, *args): """Relay function call to static method.""" if not cls.depwarn_printed: cls.depwarn_printed = True print(Merger.DEPRECATION_WARNING, file=sys.stderr) Parsers.delete_all_comments(*args)
def merge_with(self, rhs: Any) -> None: """ Merge this document with another. Parameters: 1. rhs (Any) The document to merge into this one. Returns: N/A Raises: - `MergeException` when a clean merge is impossible. """ # Do nothing when RHS is None (empty document) if rhs is None: return # Remove all comments (no sensible way to merge them) Parsers.delete_all_comments(rhs) # When LHS is None (empty document), just dump all of RHS into it, # honoring any --mergeat|-m location as best as possible. insert_at = self.config.get_insertion_point() if self.data is None: self.logger.debug( "Replacing None data with:", prefix="Merger::merge_with: ", data=rhs, data_header=" *****") self.data = Nodes.build_next_node(insert_at, 0, rhs) self.logger.debug( "Merged document is now:", prefix="Merger::merge_with: ", data=self.data, footer=" ***** ***** *****") if isinstance(rhs, (dict, list, CommentedSet, set)): # Only Scalar values need further processing return # Resolve any anchor conflicts self._resolve_anchor_conflicts(rhs) # Prepare the merge rules self.config.prepare(rhs) # Merge into each insertion point merge_performed = False lhs_proc = Processor(self.logger, self.data) for node_coord in self._get_merge_target_nodes( insert_at, lhs_proc, rhs ): target_node = node_coord.node Parsers.set_flow_style( rhs, (target_node.fa.flow_style() if hasattr(target_node, "fa") else None)) if isinstance(rhs, CommentedMap): # The RHS document root is a dict merge_performed = self._insert_dict( insert_at, target_node, rhs) elif isinstance(rhs, CommentedSeq): # The RHS document root is a list merge_performed = self._insert_list( insert_at, target_node, rhs) elif isinstance(rhs, CommentedSet): # The RHS document is a set merge_performed = self._insert_set( insert_at, target_node, rhs) else: # The RHS document root is a Scalar value merge_performed = self._insert_scalar( insert_at, target_node, lhs_proc, rhs) self.logger.debug( "Completed merge operation, resulting in document:", prefix="Merger::merge_with: ", data=self.data) if not merge_performed: raise MergeException( "A merge was not performed. Ensure your target path matches" " at least one node in the left document(s).", insert_at)
def data(self, value: Any) -> None: """Document data being merged into (mutator).""" if not self.config.is_preserving_lhs_comments(): Parsers.delete_all_comments(value) self._data = value
def data(self, value: Any) -> None: """Document data being merged into (mutator).""" Parsers.delete_all_comments(value) self._data = value
def merge_with(self, rhs: Any) -> None: """ Merge this document with another. Parameters: 1. rhs (Any) The document to merge into this one. Returns: N/A Raises: - `MergeException` when a clean merge is impossible. """ # Do nothing when RHS is None (empty document) if rhs is None: return # Remove all comments (no sensible way to merge them) Parsers.delete_all_comments(rhs) # When LHS is None (empty document), just dump all of RHS into it, # honoring any --mergeat|-m location as best as possible. insert_at = self.config.get_insertion_point() if self.data is None: self.logger.debug("Replacing None data with:", prefix="Merger::merge_with: ", data=rhs, data_header=" *****") self.data = Nodes.build_next_node(insert_at, 0, rhs) self.logger.debug("Merged document is now:", prefix="Merger::merge_with: ", data=self.data, footer=" ***** ***** *****") if isinstance(rhs, (dict, list)): # Only Scalar values need further processing return # Resolve any anchor conflicts self._resolve_anchor_conflicts(rhs) # Prepare the merge rules self.config.prepare(rhs) # Identify a reasonable default should a DOM need to be built up to # receive the RHS data. default_val = rhs if isinstance(rhs, CommentedMap): default_val = {} elif isinstance(rhs, CommentedSeq): default_val = [] # Loop through all insertion points and the elements in RHS merge_performed = False nodes: List[NodeCoords] = [] lhs_proc = Processor(self.logger, self.data) for node_coord in lhs_proc.get_nodes(insert_at, default_value=default_val): nodes.append(node_coord) for node_coord in nodes: target_node = (node_coord.node if isinstance( node_coord.node, (CommentedMap, CommentedSeq)) else node_coord.parent) Parsers.set_flow_style(rhs, (target_node.fa.flow_style() if hasattr( target_node, "fa") else None)) if isinstance(rhs, CommentedMap): # The RHS document root is a map if isinstance(target_node, CommentedSeq): # But the destination is a list self._merge_lists(target_node, CommentedSeq([rhs]), insert_at) else: self._merge_dicts(target_node, rhs, insert_at) # Synchronize YAML Tags self.logger.debug( "Merger::merge_with: Setting LHS tag from {} to {}.". format(target_node.tag.value, rhs.tag.value)) target_node.yaml_set_tag(rhs.tag.value) merge_performed = True elif isinstance(rhs, CommentedSeq): # The RHS document root is a list self._merge_lists(target_node, rhs, insert_at) merge_performed = True # Synchronize any YAML Tag self.logger.debug( "Merger::merge_with: Setting LHS tag from {} to {}.". format(target_node.tag.value, rhs.tag.value)) target_node.yaml_set_tag(rhs.tag.value) else: # The RHS document root is a Scalar value target_node = node_coord.node if isinstance(target_node, CommentedSeq): Nodes.append_list_element(target_node, rhs) merge_performed = True elif isinstance(target_node, CommentedMap): raise MergeException( "Impossible to add Scalar value, {}, to a Hash without" " a key. Change the value to a 'key: value' pair, a" " '{{key: value}}' Hash, or change the merge target to" " an Array or other Scalar value.".format(rhs), insert_at) else: lhs_proc.set_value(insert_at, rhs) merge_performed = True self.logger.debug("Completed merge operation, resulting in document:", prefix="Merger::merge_with: ", data=self.data) if not merge_performed: raise MergeException( "A merge was not performed. Ensure your target path matches" " at least one node in the left document(s).", insert_at)