def main(): """Main code.""" args = processcli() log = ConsolePrinter(args) validateargs(args, log) yaml_path = YAMLPath(args.query, pathsep=args.pathsep) # Prep the YAML parser yaml = Parsers.get_yaml_editor() # Attempt to open the YAML file; check for parsing errors (yaml_data, doc_loaded) = Parsers.get_yaml_data( yaml, log, args.yaml_file if args.yaml_file else "-") if not doc_loaded: # An error message has already been logged sys.exit(1) # Seek the queried value(s) discovered_nodes = [] processor = EYAMLProcessor(log, yaml_data, binary=args.eyaml, publickey=args.publickey, privatekey=args.privatekey) try: for node in processor.get_eyaml_values(yaml_path, mustexist=True): log.debug("Got node from {}:".format(yaml_path), data=node, prefix="yaml_get::main: ") discovered_nodes.append(NodeCoords.unwrap_node_coords(node)) except YAMLPathException as ex: log.critical(ex, 1) except EYAMLCommandException as ex: log.critical(ex, 2) try: for node in discovered_nodes: if isinstance(node, (dict, list, CommentedSet)): print(json.dumps(Parsers.jsonify_yaml_data(node))) else: if node is None: node = "\x00" print("{}".format(str(node).replace("\n", r"\n"))) except RecursionError: log.critical( "The YAML data contains an infinitely recursing YAML Alias!", 1)
def min(data: Any, invert: bool, parameters: List[str], yaml_path: YAMLPath, **kwargs: Any) -> Generator[NodeCoords, None, None]: """ Find whichever nodes/elements have a minimum value. Parameters: 1. data (Any) The data to evaluate 2. invert (bool) Invert the evaluation 3. parameters (List[str]) Parsed parameters 4. yaml_path (YAMLPath) YAML Path begetting this operation Keyword Arguments: * parent (ruamel.yaml node) The parent node from which this query originates * parentref (Any) The Index or Key of data within parent * relay_segment (PathSegment) YAML Path segment presently under evaluation * translated_path (YAMLPath) YAML Path indicating precisely which node is being evaluated * ancestry (List[AncestryEntry]) Stack of ancestors preceding the present node under evaluation Returns: (Generator[NodeCoords, None, None]) each result as it is generated """ parent: Any = kwargs.pop("parent", None) parentref: Any = kwargs.pop("parentref", None) translated_path: YAMLPath = kwargs.pop("translated_path", YAMLPath("")) ancestry: List[AncestryEntry] = kwargs.pop("ancestry", []) relay_segment: PathSegment = kwargs.pop("relay_segment", None) # There may be 0 or 1 parameters param_count = len(parameters) if param_count > 1: raise YAMLPathException( ("Invalid parameter count to {}([NAME]); up to {} permitted, " " got {} in YAML Path").format(PathSearchKeywords.MIN, 1, param_count), str(yaml_path)) scan_node = parameters[0] if param_count > 0 else None match_value: Any = None match_nodes: List[NodeCoords] = [] discard_nodes: List[NodeCoords] = [] unwrapped_data: Any = NodeCoords.unwrap_node_coords(data) if Nodes.node_is_aoh(unwrapped_data, accept_nulls=True): # A named child node is mandatory if scan_node is None: raise YAMLPathException(( "The {}([NAME]) Search Keyword requires a key name to scan" " when evaluating an Array-of-Hashes in YAML Path").format( PathSearchKeywords.MIN), str(yaml_path)) for idx, wrapped_ele in enumerate(data): ele = NodeCoords.unwrap_node_coords(wrapped_ele) next_path = translated_path + "[{}]".format(idx) next_ancestry = ancestry + [(data, idx)] if ele is not None and scan_node in ele: eval_val = ele[scan_node] if (match_value is None or Searches.search_matches( PathSearchMethods.LESS_THAN, match_value, eval_val)): match_value = eval_val discard_nodes.extend(match_nodes) match_nodes = [ NodeCoords(ele, data, idx, next_path, next_ancestry, relay_segment) ] continue if (match_value is None or Searches.search_matches( PathSearchMethods.EQUALS, match_value, eval_val)): match_nodes.append( NodeCoords(ele, data, idx, next_path, next_ancestry, relay_segment)) continue discard_nodes.append( NodeCoords(ele, data, idx, next_path, next_ancestry, relay_segment)) elif isinstance(data, dict): # A named child node is mandatory if scan_node is None: raise YAMLPathException(( "The {}([NAME]) Search Keyword requires a key name to scan" " when comparing Hash/map/dict children in YAML Path" ).format(PathSearchKeywords.MIN), str(yaml_path)) for key, val in data.items(): next_ancestry = ancestry + [(data, key)] next_path = (translated_path + YAMLPath.escape_path_section( key, translated_path.seperator)) if isinstance(val, dict): if val is not None and scan_node in val: eval_val = val[scan_node] if (match_value is None or Searches.search_matches( PathSearchMethods.LESS_THAN, match_value, eval_val)): match_value = eval_val discard_nodes.extend(match_nodes) match_nodes = [ NodeCoords(val, data, key, next_path, next_ancestry, relay_segment) ] continue if (match_value is None or Searches.search_matches( PathSearchMethods.EQUALS, match_value, eval_val)): match_nodes.append( NodeCoords(val, data, key, next_path, next_ancestry, relay_segment)) continue elif scan_node in data: # The user probably meant to operate against the parent raise YAMLPathException( ("The {}([NAME]) Search Keyword operates against" " collections of data which share a common attribute" " yet there is only a single node to consider. Did" " you mean to evaluate the parent of the selected" " node? Please review your YAML Path").format( PathSearchKeywords.MIN), str(yaml_path)) discard_nodes.append( NodeCoords(val, data, key, next_path, next_ancestry, relay_segment)) elif isinstance(data, list): # A named child node is useless if scan_node is not None: raise YAMLPathException( ("The {}([NAME]) Search Keyword cannot utilize a key name" " when comparing Array/sequence/list elements to one" " another in YAML Path").format(PathSearchKeywords.MIN), str(yaml_path)) for idx, ele in enumerate(data): next_path = translated_path + "[{}]".format(idx) next_ancestry = ancestry + [(data, idx)] if (ele is not None and (match_value is None or Searches.search_matches( PathSearchMethods.LESS_THAN, match_value, ele))): match_value = ele discard_nodes.extend(match_nodes) match_nodes = [ NodeCoords(ele, data, idx, next_path, next_ancestry, relay_segment) ] continue if (ele is not None and Searches.search_matches( PathSearchMethods.EQUALS, match_value, ele)): match_nodes.append( NodeCoords(ele, data, idx, next_path, next_ancestry, relay_segment)) continue discard_nodes.append( NodeCoords(ele, data, idx, next_path, next_ancestry, relay_segment)) else: # Non-complex data is always its own maximum and does not invert match_value = data match_nodes = [ NodeCoords(data, parent, parentref, translated_path, ancestry, relay_segment) ] yield_nodes = discard_nodes if invert else match_nodes for node_coord in yield_nodes: yield node_coord
def unwrap_node_coords(*args): """Relay function call to static method.""" return NodeCoords.unwrap_node_coords(*args)