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(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_scalar_collectors(self, quiet_logger, yamlpath, results): yamldata = """--- list1: - 1 - 2 - 3 list2: - 4 - 5 - 6 exclude: - 3 - 4 """ yaml = YAML() processor = Processor(quiet_logger, yaml.load(yamldata)) matchidx = 0 # Note that Collectors deal with virtual DOMs, so mustexist must always # be set True. Otherwise, ephemeral virtual nodes would be created and # discarded. Is this desirable? Maybe, but not today. For now, using # Collectors without setting mustexist=True will be undefined behavior. for node in processor.get_nodes(yamlpath, mustexist=True): assert unwrap_node_coords(node) == results[matchidx] matchidx += 1 assert len(results) == matchidx
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_get_every_data_type(self, quiet_logger): # Contributed by https://github.com/AndydeCleyre yamldata = """--- intthing: 6 floatthing: 6.8 yesthing: yes nothing: no truething: true falsething: false nullthing: null nothingthing: emptystring: "" nullstring: "null" """ results = [6, 6.8, "yes", "no", True, False, None, None, "", "null"] yaml = YAML() data = yaml.load(yamldata) processor = Processor(quiet_logger, data) yamlpath = YAMLPath("*") match_index = 0 for node in processor.get_nodes(yamlpath): assert unwrap_node_coords(node) == results[match_index] match_index += 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_in_none_data(self, capsys, quiet_logger): import sys yaml = YAML() data = None processor = Processor(quiet_logger, data) processor._update_node(None, None, None, YAMLValueFormats.DEFAULT) yaml.dump(data, sys.stdout) assert -1 == capsys.readouterr().out.find("abc")
def test_none_data_to_get_nodes_by_path_segment(self, capsys, quiet_logger): import sys yamldata = "" yaml = YAML() data = yaml.load(yamldata) processor = Processor(quiet_logger, data) nodes = list(processor._get_nodes_by_path_segment(data, YAMLPath("abc"), 0)) yaml.dump(data, sys.stdout) assert -1 == capsys.readouterr().out.find("abc")
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_illegal_traversal_recursion(self, quiet_logger): yamldata = """--- any: data """ yaml = YAML() processor = Processor(quiet_logger, yaml.load(yamldata)) with pytest.raises(YAMLPathException) as ex: nodes = list(processor.get_nodes("**.**")) assert -1 < str(ex.value).find("Repeating traversals are not allowed")
def test_enforce_pathsep(self, quiet_logger): yamldata = """--- aliases: - &aliasAnchorOne Anchored Scalar Value """ yaml = YAML() processor = Processor(quiet_logger, yaml.load(yamldata)) yamlpath = YAMLPath("aliases[&aliasAnchorOne]") for node in processor.get_nodes(yamlpath, pathsep=PathSeperators.FSLASH): assert unwrap_node_coords(node) == "Anchored Scalar Value"
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_get_none_data_nodes(self, quiet_logger): processor = Processor(quiet_logger, None) yamlpath = YAMLPath("abc") matches = 0 for node in processor.get_nodes(yamlpath, mustexist=False): matches += 1 for node in processor.get_nodes(yamlpath, mustexist=True): matches += 1 for node in processor._get_required_nodes(None, yamlpath): matches += 1 assert matches == 0
def test_adjoined_collectors_error(self, quiet_logger): yamldata = """--- key: value """ yaml = YAML() data = yaml.load(yamldata) processor = Processor(quiet_logger, data) with pytest.raises(YAMLPathException) as ex: nodes = list(processor.get_nodes("(&arrayOfHashes.step)(disabled_steps)")) assert -1 < str(ex.value).find("has no meaning")
def test_null_docs_have_nothing_to_delete(self, capsys): args = SimpleNamespace(verbose=False, quiet=False, debug=True) logger = ConsolePrinter(args) processor = Processor(logger, None) deleted_nodes = [] for nc in processor.delete_nodes("**"): deleted_nodes.append(nc) console = capsys.readouterr() assert "Refusing to delete nodes from a null document" in console.out
def test_no_attrs_to_arrays_error(self, quiet_logger): yamldata = """--- array: - one - two """ yaml = YAML() data = yaml.load(yamldata) processor = Processor(quiet_logger, data) with pytest.raises(YAMLPathException) as ex: nodes = list(processor.get_nodes("array.attr")) assert -1 < str(ex.value).find("Cannot add")
def test_get_nodes_array_impossible_type_error(self, quiet_logger): yamldata = """--- array: - 1 - 2 """ yaml = YAML() data = yaml.load(yamldata) processor = Processor(quiet_logger, data) with pytest.raises(YAMLPathException) as ex: nodes = list(processor.get_nodes(r"/array/(.=~/^.{3,4}$/)", default_value="New value")) assert -1 < str(ex.value).find("Cannot add")
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 extract_values(data: {}, path: str) -> []: class Args: debug = True verbose = False quiet = True args = Args() log = ConsolePrinter(args) try: processor = Processor(log, data) nodes = processor.get_nodes(path) return [n.node for n in nodes] except YAMLPathException as ex: print(ex)
def test_no_attrs_to_scalars_errors(self, quiet_logger): yamldata = """--- scalar: value """ yaml = YAML() data = yaml.load(yamldata) processor = Processor(quiet_logger, data) with pytest.raises(YAMLPathException) as ex: nodes = list(processor.get_nodes("scalar[6]")) assert -1 < str(ex.value).find("Cannot add") with pytest.raises(YAMLPathException) as ex: nodes = list(processor.get_nodes("scalar.key")) assert -1 < str(ex.value).find("Cannot add")
def _prepare_user_rules(self, proc: Processor, section: str, collector: dict) -> None: """ Identify DOM nodes matching user-defined diff rules. Parameters: 1. proc (Processor) Reference to the DOM Processor. 2. section (str) User-configuration file section defining the diff rules to apply. 3. collector (dict) Storage collector for matching nodes. Returns: N/A """ if self.config is None or not section in self.config: self.log.warning( "User-specified configuration file has no {} section.".format( section)) return for rule_key in self.config[section]: rule_value = self.config[section][rule_key] if "=" in rule_value: # There were at least two = signs on the configuration line conf_line = rule_key + "=" + rule_value delim_pos = conf_line.rfind("=") rule_key = conf_line[0:delim_pos].strip() rule_value = conf_line[delim_pos + 1:].strip() self.log.debug( "DifferConfig::_prepare_user_rules: Reconstituted" " configuration line '{}' to extract adjusted key '{}'" " with value '{}'".format(conf_line, rule_key, rule_value)) rule_path = YAMLPath(rule_key) yaml_path = YAMLPath(rule_path) self.log.debug( "DifferConfig::_prepare_user_rules: Matching '{}' nodes to" " YAML Path '{}' from key, {}.".format(section, yaml_path, rule_key)) try: for node_coord in proc.get_nodes(yaml_path, mustexist=True): self.log.debug( "Node will have comparisons rule, {}:".format( rule_value), prefix="DifferConfig::_prepare_user_rules: ", data=node_coord.node) collector[node_coord] = rule_value except YAMLPathException: self.log.warning("{} YAML Path matches no nodes: {}".format( section, yaml_path)) self.log.debug("Matched rules to nodes:", prefix="DifferConfig::_prepare_user_rules: ") for node_coord, diff_rule in collector.items(): self.log.debug("... RULE: {}".format(diff_rule), prefix="DifferConfig::_prepare_user_rules: ") self.log.debug("... NODE:", data=node_coord, prefix="DifferConfig::_prepare_user_rules: ")
def test_get_impossible_nodes_error(self, quiet_logger, yamlpath, mustexist): yamldata = """--- ints: - 1 - 2 - 3 - 4 - 5 floats: - 1.1 - 2.2 - 3.3 """ yaml = YAML() processor = Processor(quiet_logger, yaml.load(yamldata)) with pytest.raises(YAMLPathException) as ex: nodes = list(processor.get_nodes(yamlpath, mustexist=mustexist)) assert -1 < str(ex.value).find("does not match any nodes")
def test_nonexistant_path_search_method_error(self, quiet_logger): from enum import Enum from yamlpath.enums import PathSearchMethods names = [m.name for m in PathSearchMethods] + ['DNF'] PathSearchMethods = Enum('PathSearchMethods', names) yamldata = """--- top_scalar: value """ yaml = YAML() data = yaml.load(yamldata) processor = Processor(quiet_logger, data) with pytest.raises(NotImplementedError): nodes = list(processor._get_nodes_by_search( data, SearchTerms(True, PathSearchMethods.DNF, ".", "top_scalar") ))
def test_no_index_to_hashes_error(self, quiet_logger): # Using [#] syntax is a disambiguated INDEX ELEMENT NUMBER. In # DICTIONARY context, this would create an ambiguous request to access # either the #th value or a value whose key is the literal #. As such, # an error is deliberately generated when [#] syntax is used against # dictionaries. When you actually want a DICTIONARY KEY that happens # to be an integer, omit the square braces, []. yamldata = """--- hash: key: value """ yaml = YAML() data = yaml.load(yamldata) processor = Processor(quiet_logger, data) with pytest.raises(YAMLPathException) as ex: nodes = list(processor.get_nodes("hash[6]")) assert -1 < str(ex.value).find("Cannot add")
def test_delete_nodes(self, quiet_logger, delete_yamlpath, pathseperator, old_deleted_nodes, new_flat_data): 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 records: - id: ABC data: 123 - id: BCD data: 987 - id: CDE data: 8B8 """ yaml = YAML() data = yaml.load(yamldata) processor = Processor(quiet_logger, data) # The return set must be received lest no nodes will be deleted deleted_nodes = [] for nc in processor.delete_nodes(delete_yamlpath, pathsep=pathseperator): deleted_nodes.append(nc) for (test_value, verify_node_coord) in zip(old_deleted_nodes, deleted_nodes): assert test_value, unwrap_node_coords(verify_node_coord) for (test_value, verify_node_coord) in zip(new_flat_data, processor.get_nodes("**")): assert test_value, unwrap_node_coords(verify_node_coord)
def test_non_int_array_index_error(self, quiet_logger): from collections import deque yamldata = """--- - 1 """ yaml = YAML() data = yaml.load(yamldata) path = YAMLPath("[0]") processor = Processor(quiet_logger, data) strp = str(path) path._escaped = deque([ (PathSegmentTypes.INDEX, "0F"), ]) path._unescaped = deque([ (PathSegmentTypes.INDEX, "0F"), ]) with pytest.raises(YAMLPathException) as ex: nodes = list(processor._get_nodes_by_index(data, path, 0)) assert -1 < str(ex.value).find("is not an integer array index")
def test_cannot_add_novel_alias_keys(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[&keyThree].subval") newvalue = "Abort" with pytest.raises(YAMLPathException) as ex: nodes = list(processor.get_nodes(yamlpath)) assert -1 < str(ex.value).find("Cannot add")
def test_get_nodes_by_unknown_path_segment_error(self, quiet_logger): from collections import deque from enum import Enum from yamlpath.enums import PathSegmentTypes names = [m.name for m in PathSegmentTypes] + ['DNF'] PathSegmentTypes = Enum('PathSegmentTypes', names) yamldata = """--- key: value """ yaml = YAML() data = yaml.load(yamldata) processor = Processor(quiet_logger, data) path = YAMLPath("abc") stringified = str(path) # Force Path to parse path._escaped = deque([ (PathSegmentTypes.DNF, "abc"), ]) with pytest.raises(NotImplementedError): nodes = list(processor._get_nodes_by_path_segment(data, path, 0))
def test_get_nodes(self, quiet_logger, yamlpath, results, mustexist, default): yamldata = """--- aliases: - &aliasAnchorOne Anchored Scalar Value - &aliasAnchorTwo Hey, Number Two! array_of_hashes: &arrayOfHashes - step: 1 name: one - step: 2 name: two rollback_hashes: on_condition: failure: - step: 3 name: three - step: 4 name: four disabled_steps: - 2 - 3 squads: alpha: 1.1 bravo: 2.2 charlie: 3.3 delta: 4.4 number_keys: 1: one 2: two 3: three """ yaml = YAML() processor = Processor(quiet_logger, yaml.load(yamldata)) matchidx = 0 for node in processor.get_nodes(yamlpath, mustexist=mustexist, default_value=default): assert node == results[matchidx] matchidx += 1 assert len(results) == matchidx
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