def test_set_nonunique_values(self, quiet_logger, yamlpath, value, verifications): yamldata = """--- aliases: - &alias_number 1 - &alias_bool true number: 1 bool: true alias_number: *alias_number alias_bool: *alias_bool hash: number: 1 bool: true alias_number: *alias_number alias_bool: *alias_bool complex: hash: number: 1 bool: true alias_number: *alias_number alias_bool: *alias_bool """ yaml = YAML() data = yaml.load(yamldata) processor = Processor(quiet_logger, data) processor.set_value(yamlpath, value) for verification in verifications: for verify_node_coord in processor.get_nodes(verification[0]): assert unwrap_node_coords(verify_node_coord) == verification[1]
def test_key_anchor_changes(self, quiet_logger, yamlpath, value, tally, mustexist, vformat, pathsep): yamldata = """--- anchorKeys: &keyOne aliasOne: 11A1 &keyTwo aliasTwo: 22B2 &recursiveAnchorKey subjectKey: *recursiveAnchorKey hash: *keyOne : subval: 1.1 *keyTwo : subval: 2.2 *recursiveAnchorKey : subval: 3.3 """ yaml = YAML() data = yaml.load(yamldata) processor = Processor(quiet_logger, data) yamlpath = YAMLPath(yamlpath) processor.set_value(yamlpath, value, mustexist=mustexist, value_format=vformat, pathsep=pathsep) matchtally = 0 for node in processor.get_nodes(yamlpath): assert unwrap_node_coords(node) == value matchtally += 1 assert matchtally == tally
def test_set_value(self, quiet_logger, yamlpath, value, tally, mustexist, vformat, pathsep): yamldata = """--- aliases: - &testAnchor Initial Value top_array: # Comment 1 - 1 # Comment 2 - 2 # Comment N top_scalar: Top-level plain scalar string top_hash: positive_float: 3.14159265358 negative_float: -11.034 null_value: """ yaml = YAML() data = yaml.load(yamldata) processor = Processor(quiet_logger, data) processor.set_value(yamlpath, value, mustexist=mustexist, value_format=vformat, pathsep=pathsep) matchtally = 0 for node in processor.get_nodes(yamlpath, mustexist=mustexist): assert unwrap_node_coords(node) == value matchtally += 1 assert matchtally == tally
def test_set_value(self, quiet_logger, yamlpath, value, tally, mustexist, vformat, pathsep): yamldata = """--- aliases: - &testAnchor Initial Value top_array: # Comment 1 - 1 # Comment 2 - 2 # Comment N top_scalar: Top-level plain scalar string """ yaml = YAML() data = yaml.load(yamldata) processor = Processor(quiet_logger, data) processor.set_value(yamlpath, value, mustexist=mustexist, value_format=vformat, pathsep=pathsep) matchtally = 0 for node in processor.get_nodes(yamlpath, mustexist=mustexist): assert node == value matchtally += 1 assert matchtally == tally
def test_set_value_in_empty_data(self, capsys, quiet_logger): import sys yamldata = "" yaml = YAML() data = yaml.load(yamldata) processor = Processor(quiet_logger, data) processor.set_value("abc", "void") yaml.dump(data, sys.stdout) assert -1 == capsys.readouterr().out.find("abc")
def test_cannot_set_nonexistent_required_node_error(self, quiet_logger): yamldata = """--- key: value """ yaml = YAML() data = yaml.load(yamldata) processor = Processor(quiet_logger, data) with pytest.raises(YAMLPathException) as ex: processor.set_value("abc", "void", mustexist=True) assert -1 < str(ex.value).find("No nodes matched")
def test_non_int_slice_error(self, quiet_logger): yamldata = """--- - step: 1 - step: 2 - step: 3 """ yaml = YAML() data = yaml.load(yamldata) processor = Processor(quiet_logger, data) with pytest.raises(YAMLPathException) as ex: processor.set_value("[1:4F]", "") assert -1 < str(ex.value).find("is not an integer array slice")
def test_key_anchor_children(self, quiet_logger): yamldata = """--- anchorKeys: &keyOne aliasOne: 1 1 Alpha 1 &keyTwo aliasTwo: 2 2 Beta 2 hash: *keyOne : subval: 1.1 *keyTwo : subval: 2.2 """ yaml = YAML() data = yaml.load(yamldata) processor = Processor(quiet_logger, data) yamlpath = YAMLPath("hash[&keyTwo].subval") newvalue = "Mute audibles" processor.set_value(yamlpath, newvalue, mustexist=True) matchtally = 0 for node in processor.get_nodes(yamlpath): assert unwrap_node_coords(node) == newvalue matchtally += 1 assert matchtally == 1
def _insert_scalar( self, insert_at: YAMLPath, lhs: Any, lhs_proc: Processor, rhs: Any ) -> bool: """Insert an RHS scalar into the LHS document.""" merge_performed = False if isinstance(lhs, CommentedSeq): self.logger.debug( "Merger::_insert_scalar: Merging a scalar into a list.") Nodes.append_list_element(lhs, rhs) merge_performed = True elif isinstance(lhs, CommentedSet): self.logger.debug( "Merger::_insert_scalar: Merging a scalar into a set.") self._merge_sets( lhs, CommentedSet([rhs]), insert_at, NodeCoords(rhs, None, None)) merge_performed = True elif isinstance(lhs, CommentedMap): ex_message = ( "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) if len(str(rhs)) < 1 and not sys.stdin.isatty(): ex_message += ( " You may be seeing this because your workflow" " inadvertently opened a STDIN handle to {}. If" " this may be the case, try adding --nostdin or -S" " so as to block unintentional STDIN reading." ).format(basename(sys.argv[0])) raise MergeException(ex_message, insert_at) else: lhs_proc.set_value(insert_at, rhs) merge_performed = True return merge_performed
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)