From 4036a5e4bfb8189a8cedc97c421fa19ae6a644c7 Mon Sep 17 00:00:00 2001 From: William Kimball <30981667+wwkimball@users.noreply.github.com> Date: Wed, 7 Apr 2021 14:34:15 -0500 Subject: [PATCH 1/6] Set more enhancement goals for release 3.4.1 --- CHANGES | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES b/CHANGES index 37c2120..85ce03e 100644 --- a/CHANGES +++ b/CHANGES @@ -10,6 +10,12 @@ Enhancements: * The node deletion capability of the yaml-set command is now part of the library. See Processor::delete_nodes(...) and Processor::delete_gathered_nodes(...) for details. +* The node aliasing capability of the yaml-set command is now part of the + library. See Processor::alias_nodes(...) and + Processor::alias_gathered_nodes(...) for details. +* The node tagging capability of the yaml-set command is now part of the + library. See Processor::tag_nodes(...) and + Processor::tag_gathered_nodes(...) for details. * The library now supports loading YAML from String rather than only from file. Simply pass a new `literal=True` keyword parameter to Parsers::get_yaml_data(...) or Parsers::get_yaml_multidoc_data(...) to From a7e3e1ee7b49ffe091a9b3f9943e185782ac16d0 Mon Sep 17 00:00:00 2001 From: William Kimball <30981667+wwkimball@users.noreply.github.com> Date: Fri, 9 Apr 2021 17:16:43 -0500 Subject: [PATCH 2/6] WIP: Shelving to work a different issue --- yamlpath/processor.py | 48 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/yamlpath/processor.py b/yamlpath/processor.py index a759ec1..761e912 100644 --- a/yamlpath/processor.py +++ b/yamlpath/processor.py @@ -191,6 +191,54 @@ def set_value(self, yaml_path: Union[YAMLPath, str], .format(value, value_format, str(vex)) , str(yaml_path)) from vex + def alias_nodes(self, yaml_path: Union[YAMLPath, str], + anchor_path: Union[YAMLPath, str], + **kwargs: Any) -> Generator[NodeCoords, None, None]: + """ + Gather and assign YAML Aliases to nodes at YAML Path in data. + + Parameters: + 1. yaml_path (Union[YAMLPath, str]) The YAML Path to all target nodes + which will become Aliases to the Anchor node specified via + `anchor_path`. + 2. anchor_path (Union[YAMLPath, str]) The YAML Path to a single source + anchor node; specifying any path which points to more than one node + will result in a YAMLPathException because YAML does not define + Aliases for more than one Anchor. + + Keyword Parameters: + * pathsep (PathSeperators) Forced YAML Path segment seperator; set + only when automatic inference fails; + default = PathSeperators.AUTO + * anchor_name (str) Override the Alias name to any non-empty name you + set; attempts to re-use an existing Anchor name will result in a + YAMLPathException. + + Returns: (Generator) Affected NodeCoords after they have become + Aliases + + Raises: + - `YAMLPathException` when YAML Path is invalid + """ + pathsep: PathSeperators = kwargs.pop("pathsep", PathSeperators.AUTO) + anchor_name: str = kwargs.pop("anchor_name", "") + + if self.data is None: + self.logger.debug( + "Refusing to alias nodes in a null document!", + prefix="Processor::alias_nodes: ", data=self.data) + return + + if isinstance(yaml_path, str): + yaml_path = YAMLPath(yaml_path, pathsep) + elif pathsep is not PathSeperators.AUTO: + yaml_path.seperator = pathsep + + if isinstance(anchor_path, str): + anchor_path = YAMLPath(anchor_path, pathsep) + elif pathsep is not PathSeperators.AUTO: + anchor_path.seperator = pathsep + def delete_nodes(self, yaml_path: Union[YAMLPath, str], **kwargs: Any) -> Generator[NodeCoords, None, None]: """ From 0c37bc36f0d388c8574dc3fe2809d20819e67650 Mon Sep 17 00:00:00 2001 From: William Kimball <30981667+wwkimball@users.noreply.github.com> Date: Sun, 11 Apr 2021 00:03:30 -0500 Subject: [PATCH 3/6] Port YAML Alias assignment from CLI to API --- yamlpath/commands/yaml_set.py | 47 ++--------- yamlpath/processor.py | 147 +++++++++++++++++++++++++++++++--- 2 files changed, 141 insertions(+), 53 deletions(-) diff --git a/yamlpath/commands/yaml_set.py b/yamlpath/commands/yaml_set.py index f180ee3..0bba3ab 100644 --- a/yamlpath/commands/yaml_set.py +++ b/yamlpath/commands/yaml_set.py @@ -19,7 +19,6 @@ from os.path import isfile, exists from shutil import copy2, copyfileobj from pathlib import Path -from typing import Any, Dict from yamlpath import __version__ as YAMLPATH_VERSION from yamlpath.common import Anchors, Nodes, Parsers @@ -415,47 +414,11 @@ def _alias_nodes( log, processor, assign_to_nodes, anchor_path, anchor_name ): """Assign YAML Aliases to the target nodes.""" - anchor_node_coordinates = _get_nodes( - log, processor, anchor_path, must_exist=True) - if len(anchor_node_coordinates) > 1: - log.critical( - "It is impossible to Alias more than one Anchor at a time from {}!" - .format(anchor_path), 1) - - anchor_coord = anchor_node_coordinates[0] - anchor_node = anchor_coord.node - if not hasattr(anchor_node, "anchor"): - anchor_coord.parent[anchor_coord.parentref] = Nodes.wrap_type( - anchor_node) - anchor_node = anchor_coord.parent[anchor_coord.parentref] - - known_anchors: Dict[str, Any] = {} - Anchors.scan_for_anchors(processor.data, known_anchors) - - if anchor_name: - # Rename any pre-existing anchor or set an original anchor name; the - # assigned name must be unique! - if anchor_name in known_anchors: - log.critical( - "Anchor names must be unique within YAML documents. Anchor" - " name, {}, is already used.".format(anchor_name)) - anchor_node.yaml_set_anchor(anchor_name, always_dump=True) - elif anchor_node.anchor.value: - # The source node already has an anchor name - anchor_name = anchor_node.anchor.value - else: - # An orignial, unique-to-the-document anchor name must be generated - new_anchor = Anchors.generate_unique_anchor_name( - processor.data, anchor_coord, known_anchors) - anchor_node.yaml_set_anchor(new_anchor, always_dump=True) - - for node_coord in assign_to_nodes: - log.debug( - "Attempting to set the anchor name for node to {}:" - .format(anchor_name), - data=node_coord, - prefix="yaml_set::_alias_nodes: ") - node_coord.parent[node_coord.parentref] = anchor_node + try: + processor.alias_gathered_nodes( + assign_to_nodes, anchor_path, anchor_name=anchor_name) + except YAMLPathException as ex: + log.critical(ex, 1) def _tag_nodes(document, tag, nodes): """Assign a data-type tag to a set of nodes.""" diff --git a/yamlpath/processor.py b/yamlpath/processor.py index 761e912..207cbfa 100644 --- a/yamlpath/processor.py +++ b/yamlpath/processor.py @@ -4,9 +4,9 @@ Copyright 2018, 2019, 2020 William W. Kimball, Jr. MBA MSIS """ -from typing import Any, Generator, List, Union +from typing import Any, Dict, Generator, List, Union -from yamlpath.common import Nodes, Searches +from yamlpath.common import Anchors, Nodes, Searches from yamlpath import YAMLPath from yamlpath.path import SearchTerms, CollectorTerms from yamlpath.wrappers import ConsolePrinter, NodeCoords @@ -191,9 +191,82 @@ def set_value(self, yaml_path: Union[YAMLPath, str], .format(value, value_format, str(vex)) , str(yaml_path)) from vex - def alias_nodes(self, yaml_path: Union[YAMLPath, str], - anchor_path: Union[YAMLPath, str], - **kwargs: Any) -> Generator[NodeCoords, None, None]: + def _get_anchor_node( + self, anchor_path: Union[YAMLPath, str], **kwargs: Any + ) -> Any: + """ + Gather the source YAML Anchor node for an Aliasing operation. + + Parameters: + 1. anchor_path (Union[YAMLPath, str]) The YAML Path to a single source + anchor node; specifying any path which points to more than one node + will result in a YAMLPathException because YAML does not define + Aliases for more than one Anchor. + + Returns: (Any) The source node + + Raises: + - `YAMLPathException` when YAML Path is invalid + """ + pathsep: PathSeperators = kwargs.pop("pathsep", PathSeperators.AUTO) + anchor_name: str = kwargs.pop("anchor_name", "") + + if self.data is None: + self.logger.debug( + "Refusing to alias nodes in a null document!", + prefix="Processor::alias_nodes: ", data=self.data) + return None + + if isinstance(anchor_path, str): + anchor_path = YAMLPath(anchor_path, pathsep) + elif pathsep is not PathSeperators.AUTO: + anchor_path.seperator = pathsep + + anchor_node_coordinates: List[NodeCoords] = [] + for node_coords in self._get_required_nodes(self.data, anchor_path): + self.logger.debug( + "Gathered YAML Anchor node:", + prefix="Processor::alias_nodes: ", data=node_coords) + anchor_node_coordinates.append(node_coords) + if len(anchor_node_coordinates) > 1: + raise YAMLPathException( + "It is impossible to Alias more than one Anchor at a time!", + str(anchor_path)) + + anchor_coord = anchor_node_coordinates[0] + anchor_node = anchor_coord.node + if not hasattr(anchor_node, "anchor"): + anchor_coord.parent[anchor_coord.parentref] = Nodes.wrap_type( + anchor_node) + anchor_node = anchor_coord.parent[anchor_coord.parentref] + + known_anchors: Dict[str, Any] = {} + Anchors.scan_for_anchors(self.data, known_anchors) + + if anchor_name: + # Rename any pre-existing anchor or set an original anchor name; + # the assigned name must be unique! + if anchor_name in known_anchors: + raise YAMLPathException( + "Anchor names must be unique within YAML documents." + " Anchor name, {}, is already used." + .format(anchor_name), str(anchor_path)) + anchor_node.yaml_set_anchor(anchor_name, always_dump=True) + elif anchor_node.anchor.value: + # The source node already has an anchor name + anchor_name = anchor_node.anchor.value + else: + # An orignial, unique-to-the-document anchor name must be generated + new_anchor = Anchors.generate_unique_anchor_name( + self.data, anchor_coord, known_anchors) + anchor_node.yaml_set_anchor(new_anchor, always_dump=True) + + return anchor_node + + def alias_nodes( + self, yaml_path: Union[YAMLPath, str], + anchor_path: Union[YAMLPath, str], **kwargs: Any + ) -> None: """ Gather and assign YAML Aliases to nodes at YAML Path in data. @@ -214,8 +287,7 @@ def alias_nodes(self, yaml_path: Union[YAMLPath, str], set; attempts to re-use an existing Anchor name will result in a YAMLPathException. - Returns: (Generator) Affected NodeCoords after they have become - Aliases + Returns: N/A Raises: - `YAMLPathException` when YAML Path is invalid @@ -234,10 +306,63 @@ def alias_nodes(self, yaml_path: Union[YAMLPath, str], elif pathsep is not PathSeperators.AUTO: yaml_path.seperator = pathsep - if isinstance(anchor_path, str): - anchor_path = YAMLPath(anchor_path, pathsep) - elif pathsep is not PathSeperators.AUTO: - anchor_path.seperator = pathsep + anchor_node = self._get_anchor_node( + anchor_path, pathsep=pathsep, anchor_name=anchor_name) + + gathered_nodes: List[NodeCoords] = [] + for node_coords in self._get_required_nodes(self.data, yaml_path): + self.logger.debug( + "Gathered node for YAML Alias assignment:", + prefix="Processor::delete_nodes: ", data=node_coords) + gathered_nodes.append(node_coords) + + if len(gathered_nodes) > 0: + self._alias_nodes(gathered_nodes, anchor_node) + + def alias_gathered_nodes( + self, gathered_nodes: List[NodeCoords], + anchor_path: Union[YAMLPath, str], **kwargs: Any + ) -> None: + """ + Assign a YAML Anchor to zero or more YAML Alias nodes. + + Parameters: + 1. gathered_nodes (List[NodeCoords]) The pre-gathered nodes to assign. + """ + pathsep: PathSeperators = kwargs.pop("pathsep", PathSeperators.AUTO) + anchor_name: str = kwargs.pop("anchor_name", "") + + if self.data is None: + self.logger.debug( + "Refusing to alias nodes in a null document!", + prefix="Processor::alias_nodes: ", data=self.data) + return + + anchor_node = self._get_anchor_node( + anchor_path, pathsep=pathsep, anchor_name=anchor_name) + + self._alias_nodes(gathered_nodes, anchor_node) + + def _alias_nodes( + self, gathered_nodes: List[NodeCoords], anchor_node: Any + ) -> None: + """ + Assign a YAML Anchor to its various YAML Alias nodes. + + Parameters: + 1. gathered_nodes (List[NodeCoords]) The pre-gathered nodes to assign. + 2. anchor_node (Any) The source YAML Anchor node. + + Returns: N/A + """ + anchor_name = anchor_node.anchor.value + for node_coord in gathered_nodes: + self.logger.debug( + "Attempting to set the anchor name for node to {}:" + .format(anchor_name), + data=node_coord, + prefix="yaml_set::_alias_nodes: ") + node_coord.parent[node_coord.parentref] = anchor_node def delete_nodes(self, yaml_path: Union[YAMLPath, str], **kwargs: Any) -> Generator[NodeCoords, None, None]: From 5f8b0ffd28cff9bb74933e1cb4426bc2a5e6736b Mon Sep 17 00:00:00 2001 From: William Kimball <30981667+wwkimball@users.noreply.github.com> Date: Sun, 11 Apr 2021 18:36:07 -0500 Subject: [PATCH 4/6] Moved node tagging to the library --- yamlpath/commands/yaml_set.py | 18 ++--------- yamlpath/processor.py | 59 +++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 16 deletions(-) diff --git a/yamlpath/commands/yaml_set.py b/yamlpath/commands/yaml_set.py index 0bba3ab..b9c80e0 100644 --- a/yamlpath/commands/yaml_set.py +++ b/yamlpath/commands/yaml_set.py @@ -21,7 +21,7 @@ from pathlib import Path from yamlpath import __version__ as YAMLPATH_VERSION -from yamlpath.common import Anchors, Nodes, Parsers +from yamlpath.common import Nodes, Parsers from yamlpath import YAMLPath from yamlpath.exceptions import YAMLPathException from yamlpath.enums import YAMLValueFormats, PathSeperators @@ -420,20 +420,6 @@ def _alias_nodes( except YAMLPathException as ex: log.critical(ex, 1) -def _tag_nodes(document, tag, nodes): - """Assign a data-type tag to a set of nodes.""" - for node_coord in nodes: - old_node = node_coord.node - if node_coord.parent is None: - node_coord.node.yaml_set_tag(tag) - else: - node_coord.parent[node_coord.parentref] = Nodes.apply_yaml_tag( - node_coord.node, tag) - if Anchors.get_node_anchor(old_node) is not None: - Anchors.replace_anchor( - document, old_node, - node_coord.parent[node_coord.parentref]) - # pylint: disable=locally-disabled,too-many-locals,too-many-branches,too-many-statements def main(): """Main code.""" @@ -605,7 +591,7 @@ def main(): except YAMLPathException as ex: log.critical(ex, 1) elif args.tag: - _tag_nodes(processor.data, args.tag, change_node_coordinates) + processor.tag_gathered_nodes(change_node_coordinates, args.tag) # Write out the result write_output_document(args, log, yaml, yaml_data) diff --git a/yamlpath/processor.py b/yamlpath/processor.py index 207cbfa..3cf81af 100644 --- a/yamlpath/processor.py +++ b/yamlpath/processor.py @@ -364,6 +364,65 @@ def _alias_nodes( prefix="yaml_set::_alias_nodes: ") node_coord.parent[node_coord.parentref] = anchor_node + def tag_nodes( + self, yaml_path: Union[YAMLPath, str], tag: str, **kwargs: Any + ) -> None: + """ + Gather and assign a data-type tag to nodes at YAML Path in data. + + Parameters: + 1. yaml_path (Union[YAMLPath, str]) The YAML Path to evaluate + 2. tag (str) The tag to assign + + Keyword Parameters: + * pathsep (PathSeperators) Forced YAML Path segment seperator; set + only when automatic inference fails; + default = PathSeperators.AUTO + + Returns: N/A + + Raises: + - `YAMLPathException` when YAML Path is invalid + """ + pathsep: PathSeperators = kwargs.pop("pathsep", PathSeperators.AUTO) + + if self.data is None: + self.logger.debug( + "Refusing to tag nodes from a null document!", + prefix="Processor::tag_nodes: ", data=self.data) + return + + if isinstance(yaml_path, str): + yaml_path = YAMLPath(yaml_path, pathsep) + elif pathsep is not PathSeperators.AUTO: + yaml_path.seperator = pathsep + + gathered_nodes: List[NodeCoords] = [] + for node_coords in self._get_required_nodes(self.data, yaml_path): + self.logger.debug( + "Gathered node for tagging:", + prefix="Processor::tag_nodes: ", data=node_coords) + gathered_nodes.append(node_coords) + + if len(gathered_nodes) > 0: + self.tag_gathered_nodes(gathered_nodes, tag) + + def tag_gathered_nodes( + self, gathered_nodes: List[NodeCoords], tag: str + ) -> None: + """Assign a data-type tag to a set of nodes.""" + for node_coord in gathered_nodes: + old_node = node_coord.node + if node_coord.parent is None: + node_coord.node.yaml_set_tag(tag) + else: + node_coord.parent[node_coord.parentref] = Nodes.apply_yaml_tag( + node_coord.node, tag) + if Anchors.get_node_anchor(old_node) is not None: + Anchors.replace_anchor( + self.data, old_node, + node_coord.parent[node_coord.parentref]) + def delete_nodes(self, yaml_path: Union[YAMLPath, str], **kwargs: Any) -> Generator[NodeCoords, None, None]: """ From e376ffb3bc2e018e8f89f077ea96e97055a19198 Mon Sep 17 00:00:00 2001 From: William Kimball <30981667+wwkimball@users.noreply.github.com> Date: Sun, 11 Apr 2021 23:57:55 -0500 Subject: [PATCH 5/6] 100% test coverage for aliasing and tagging --- tests/test_processor.py | 80 +++++++++++++++++++++++++++++++++++++++++ yamlpath/processor.py | 24 +++++++------ 2 files changed, 94 insertions(+), 10 deletions(-) diff --git a/tests/test_processor.py b/tests/test_processor.py index 5598310..2607c73 100644 --- a/tests/test_processor.py +++ b/tests/test_processor.py @@ -3,6 +3,7 @@ from types import SimpleNamespace from ruamel.yaml import YAML +from ruamel.yaml.comments import TaggedScalar from yamlpath.func import unwrap_node_coords from yamlpath.exceptions import YAMLPathException @@ -830,3 +831,82 @@ def test_null_docs_have_nothing_to_delete(self, capsys): console = capsys.readouterr() assert "Refusing to delete nodes from a null document" in console.out + + def test_null_docs_have_nothing_to_gather_and_alias(self, capsys): + args = SimpleNamespace(verbose=False, quiet=False, debug=True) + logger = ConsolePrinter(args) + processor = Processor(logger, None) + + processor.alias_nodes("/alias*", "/anchor") + + console = capsys.readouterr() + assert "Refusing to alias nodes in a null document" in console.out + + def test_null_docs_have_nothing_to_alias(self, capsys): + args = SimpleNamespace(verbose=False, quiet=False, debug=True) + logger = ConsolePrinter(args) + processor = Processor(logger, None) + + processor.alias_gathered_nodes([], "/anchor") + + console = capsys.readouterr() + assert "Refusing to alias nodes in a null document" in console.out + + def test_null_docs_have_nothing_to_tag(self, capsys): + args = SimpleNamespace(verbose=False, quiet=False, debug=True) + logger = ConsolePrinter(args) + processor = Processor(logger, None) + + processor.tag_nodes("/tag_nothing", "tag_this") + + console = capsys.readouterr() + assert "Refusing to tag nodes from a null document" in console.out + + @pytest.mark.parametrize("alias_path,anchor_path,anchor_name,pathseperator", [ + (YAMLPath("/a_hash/a_key"), YAMLPath("/some_key"), "", PathSeperators.FSLASH), + ("a_hash.a_key", "some_key", "", PathSeperators.AUTO), + ]) + def test_anchor_nodes(self, quiet_logger, alias_path, anchor_path, anchor_name, pathseperator): + anchor_value = "This is the Anchored value!" + yamlin = """--- +some_key: {} +a_hash: + a_key: A value +""".format(anchor_value) + + yaml = YAML() + data = yaml.load(yamlin) + processor = Processor(quiet_logger, data) + + processor.alias_nodes( + alias_path, anchor_path, + pathsep=pathseperator, anchor_name=anchor_name) + + match_count = 0 + for node in processor.get_nodes( + alias_path, mustexist=True + ): + match_count += 1 + assert unwrap_node_coords(node) == anchor_value + assert match_count == 1 + + @pytest.mark.parametrize("yaml_path,tag,pathseperator", [ + (YAMLPath("/key"), "!taggidy", PathSeperators.FSLASH), + ("key", "taggidy", PathSeperators.AUTO), + ]) + def test_tag_nodes(self, quiet_logger, yaml_path, tag, pathseperator): + yamlin = """--- +key: value +""" + + yaml = YAML() + data = yaml.load(yamlin) + processor = Processor(quiet_logger, data) + + processor.tag_nodes(yaml_path, tag, pathsep=pathseperator) + + if tag and not tag[0] == "!": + tag = "!{}".format(tag) + + assert isinstance(data['key'], TaggedScalar) + assert data['key'].tag.value == tag diff --git a/yamlpath/processor.py b/yamlpath/processor.py index 3cf81af..8c888e9 100644 --- a/yamlpath/processor.py +++ b/yamlpath/processor.py @@ -203,20 +203,19 @@ def _get_anchor_node( will result in a YAMLPathException because YAML does not define Aliases for more than one Anchor. + Keyword Parameters: + * anchor_name (str) Alternate name to use for the YAML Anchor and its + Aliases. + Returns: (Any) The source node Raises: - - `YAMLPathException` when YAML Path is invalid + - `YAMLPathException` when YAML Path is invalid or a supplied + anchor_name is illegal """ pathsep: PathSeperators = kwargs.pop("pathsep", PathSeperators.AUTO) anchor_name: str = kwargs.pop("anchor_name", "") - if self.data is None: - self.logger.debug( - "Refusing to alias nodes in a null document!", - prefix="Processor::alias_nodes: ", data=self.data) - return None - if isinstance(anchor_path, str): anchor_path = YAMLPath(anchor_path, pathsep) elif pathsep is not PathSeperators.AUTO: @@ -226,7 +225,7 @@ def _get_anchor_node( for node_coords in self._get_required_nodes(self.data, anchor_path): self.logger.debug( "Gathered YAML Anchor node:", - prefix="Processor::alias_nodes: ", data=node_coords) + prefix="Processor::_get_anchor_node: ", data=node_coords) anchor_node_coordinates.append(node_coords) if len(anchor_node_coordinates) > 1: raise YAMLPathException( @@ -335,13 +334,14 @@ def alias_gathered_nodes( if self.data is None: self.logger.debug( "Refusing to alias nodes in a null document!", - prefix="Processor::alias_nodes: ", data=self.data) + prefix="Processor::alias_gathered_nodes: ", data=self.data) return anchor_node = self._get_anchor_node( anchor_path, pathsep=pathsep, anchor_name=anchor_name) - self._alias_nodes(gathered_nodes, anchor_node) + if gathered_nodes: + self._alias_nodes(gathered_nodes, anchor_node) def _alias_nodes( self, gathered_nodes: List[NodeCoords], anchor_node: Any @@ -411,6 +411,10 @@ def tag_gathered_nodes( self, gathered_nodes: List[NodeCoords], tag: str ) -> None: """Assign a data-type tag to a set of nodes.""" + # A YAML tag must be prefixed via at least one bang (!) + if tag and not tag[0] == "!": + tag = "!{}".format(tag) + for node_coord in gathered_nodes: old_node = node_coord.node if node_coord.parent is None: From 919c001bceb4be359b9b7c012fb2a61bd694afb7 Mon Sep 17 00:00:00 2001 From: William Kimball <30981667+wwkimball@users.noreply.github.com> Date: Mon, 12 Apr 2021 11:38:42 -0500 Subject: [PATCH 6/6] Update yaml-set self-documentation output --- README.md | 49 +++++++++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 40d94ea..18e5866 100644 --- a/README.md +++ b/README.md @@ -690,18 +690,19 @@ https://github.com/wwkimball/yamlpath. ```text usage: yaml-set [-h] [-V] -g YAML_PATH - [-a VALUE | -f FILE | -i | -R LENGTH | -D] + [-a VALUE | -A ANCHOR | -f FILE | -i | -R LENGTH | -N | -D] [-F {bare,boolean,default,dquote,float,folded,int,literal,squote}] [-c CHECK] [-s YAML_PATH] [-m] [-b] - [-t ['.', '/', 'auto', 'dot', 'fslash']] [-M CHARS] [-e] - [-x EYAML] [-r PRIVATEKEY] [-u PUBLICKEY] [-S] [-d | -v | -q] + [-t ['.', '/', 'auto', 'dot', 'fslash']] [-M CHARS] [-H ANCHOR] + [-T TAG] [-e] [-x EYAML] [-r PRIVATEKEY] [-u PUBLICKEY] [-S] + [-d | -v | -q] [YAML_FILE] Changes one or more Scalar values in a YAML/JSON/Compatible document at a specified YAML Path. Matched values can be checked before they are replaced to mitigate accidental change. When matching singular results, the value can be -archived to another key before it is replaced. Further, EYAML can be employed -to encrypt the new values and/or decrypt an old value before checking it. +archived to another key before it is replaced. Further, EYAML can be employed to +encrypt the new values and/or decrypt an old value before checking it. positional arguments: YAML_FILE the YAML file to update; omit or use - to read from @@ -722,14 +723,20 @@ optional arguments: -b, --backup save a backup YAML_FILE with an extra .bak file- extension -t ['.', '/', 'auto', 'dot', 'fslash'], --pathsep ['.', '/', 'auto', 'dot', 'fslash'] - indicate which YAML Path seperator to use when - rendering results; default=dot + indicate which YAML Path seperator to use when rendering + results; default=dot -M CHARS, --random-from CHARS characters from which to build a value for --random; - default=all upper- and lower-case letters and all - digits - -S, --nostdin Do not implicitly read from STDIN, even when there is - no YAML_FILE with a non-TTY session + default=all upper- and lower-case letters and all digits + -H ANCHOR, --anchor ANCHOR + when --aliasof|-A points to a value which is not already + Anchored, a new Anchor with this name is created; + renames an existing Anchor if already set + -T TAG, --tag TAG assign a custom YAML (data-type) tag to the changed + nodes; can be used without other input options to assign + or change a tag + -S, --nostdin Do not implicitly read from STDIN, even when there is no + YAML_FILE with a non-TTY session -d, --debug output debugging details -v, --verbose increase output verbosity -q, --quiet suppress all output except errors @@ -740,20 +747,24 @@ required settings: input options: -a VALUE, --value VALUE - set the new value from the command-line instead of - STDIN + set the new value from the command-line instead of STDIN + -A ANCHOR, --aliasof ANCHOR + set the value as a YAML Alias of an existing Anchor, by + name (merely copies the target value for non-YAML files) -f FILE, --file FILE read the new value from file (discarding any trailing new-lines) -i, --stdin accept the new value from STDIN (best for sensitive data) -R LENGTH, --random LENGTH randomly generate a replacement value of a set length - -D, --delete delete rather than change target node(s) + -N, --null sets the value to null + -D, --delete delete rather than change target node(s); implies + --mustexist|-m EYAML options: - Left unset, the EYAML keys will default to your system or user defaults. - You do not need to supply a private key unless you enable --check and the - old value is encrypted. + Left unset, the EYAML keys will default to your system or user defaults. You + do not need to supply a private key unless you enable --check and the old + value is encrypted. -e, --eyamlcrypt encrypt the new value using EYAML -x EYAML, --eyaml EYAML @@ -765,7 +776,9 @@ EYAML options: When no changes are made, no backup is created, even when -b/--backup is specified. For more information about YAML Paths, please visit -https://github.com/wwkimball/yamlpath. +https://github.com/wwkimball/yamlpath/wiki. To report issues with this tool or +to request enhancements, please visit +https://github.com/wwkimball/yamlpath/issues. ``` * [yaml-validate](yamlpath/commands/yaml_validate.py)