Source code for rez.utils.resolve_graph

# SPDX-License-Identifier: Apache-2.0
# Copyright Contributors to the Rez Project


from rez.utils.graph_utils import _request_from_label


[docs]def failure_detail_from_graph(graph): """Generate detailed resolve failure messages from graph Args: graph (rez.vendor.pygraph.classes.digraph.digraph): context graph object """ # Base on `rez.solver._ResolvePhase.get_graph()` # the failure reason has three types: # # * DependencyConflicts # which have "conflict" edge in graph # # * TotalReduction # which have "conflict" edge and may also have "reduct" edge in graph # # * Cycle # which have "cycle" edge in graph # # so we find cycle edge first, and try other if not found. # # find cycle cycled_edge = next((k for k, v in graph.edge_properties.items() if v["label"] == "CYCLE"), None) if cycled_edge: return _cycled_detail_from_graph(graph, cycled_edge) # find conflict conflicted_edge = next((k for k, v in graph.edge_properties.items() if v["label"] == "CONFLICT"), None) if conflicted_edge: return _conflicted_detail_from_graph(graph, conflicted_edge) # should be a healthy graph return ""
def _cycled_detail_from_graph(graph, cycled_edge): """Find all initial requests, and walk down till circle back""" messages = ["Resolve paths starting from initial requests to cycle:"] for init_request in _iter_init_request_nodes(graph): node = init_request visited = list() while True: visited.append(node) down = next((ne for ne in graph.node_neighbors[node]), None) if down in cycled_edge: visited.append(down) break if down is None: break node = down line = " %s" % _get_node_label(graph, visited[0]) # init request for node in visited[1:]: # should be more readable if opt-out requests if not _is_request_node(graph, node): line += " --> %s" % _get_node_label(graph, node) messages.append(line) return "\n".join(messages) def _conflicted_detail_from_graph(graph, conflicted_edge): """Find all initial requests, and walk down till in conflicted edge""" messages = ["Resolve paths starting from initial requests to conflict:"] for init_request in _iter_init_request_nodes(graph): node = init_request visited = list() while True: visited.append(node) down = next((ne for ne in graph.node_neighbors[node]), None) if down is None: break node = down line = " %s" % _get_node_label(graph, visited[0]) # init request for node in visited[1:]: # should be more readable if opt-out requests if not _is_request_node(graph, node): line += " --> %s" % _get_node_label(graph, node) # show conflicted request at the end if _is_request_node(graph, node) and node in conflicted_edge: line += " --> %s" % _get_node_label(graph, node) break messages.append(line) return "\n".join(messages) def _iter_init_request_nodes(graph): request_color = "#FFFFAA" # NOTE: the color code is hardcoded in `solver` for node, attrs in graph.node_attr.items(): for at in attrs: if at[0] == "fillcolor" and at[1] == request_color: yield node def _get_node_label(graph, node): label_ = next(at[1] for at in graph.node_attr[node] if at[0] == "label") return _request_from_label(label_) def _is_request_node(graph, node): style = next(at[1] for at in graph.node_attr[node] if at[0] == "style") return "dashed" in style def _print_each_graph_edges(graph): """for debug""" for (from_, to_), properties in graph.edge_properties.items(): edge_status = properties["label"] from_name = _get_node_label(graph, from_) to_name = _get_node_label(graph, to_) print("%s -> %s %s" % (from_name, to_name, edge_status))