def format_dependency_graph(graph: DirectedGraph) -> str: """Format dependency graph for output.""" content = [] for package in graph.iter_children(None): content.append(format_package(graph, package, prefix="", visited=set())) return "".join(content).strip()
def format_package( graph: DirectedGraph, package: Package, required: str = "", prefix: str = "", visited=None, ) -> str: """Format one package. :param graph: the dependency graph :param package: the package instance :param required: the version required by its parent :param prefix: prefix text for children :param visited: the visited package collection """ if visited is None: visited = set() result = [] version = ( stream.red("[ not installed ]") if not package.version else stream.red(package.version) if required and required != "Any" and not SpecifierSet(required).contains(package.version) else stream.yellow(package.version) ) if package.name in visited: version = stream.red("[circular]") required = f"[ required: {required} ]" if required else "" result.append(f"{stream.green(package.name, bold=True)} {version} {required}\n") if package.name in visited: return "".join(result) visited.add(package.name) try: *children, last = sorted(graph.iter_children(package), key=lambda p: p.name) except ValueError: # No children nodes pass else: for child in children: required = str(package.requirements[child.name].specifier or "Any") result.append( prefix + NON_LAST_CHILD + format_package( graph, child, required, prefix + NON_LAST_PREFIX, visited.copy() ) ) required = str(package.requirements[last.name].specifier or "Any") result.append( prefix + LAST_CHILD + format_package( graph, last, required, prefix + LAST_PREFIX, visited.copy() ) ) return "".join(result)
def format_reverse_dependency_graph(project: Project, graph: DirectedGraph) -> str: """Format reverse dependency graph for output.""" leaf_nodes = sorted( (node for node in graph._vertices if not list(graph.iter_children(node))), key=lambda p: p.name, ) content = [] for node in leaf_nodes: content.append(format_reverse_package(graph, node, prefix="", visited=set())) return "".join(content).strip()
def build_dependency_graph(working_set: WorkingSet) -> DirectedGraph: """Build a dependency graph from locked result.""" graph = DirectedGraph() graph.add(None) # sentinel parent of top nodes. node_with_extras = set() def add_package(key, dist): name, extras = strip_extras(key) extras = extras or () reqs = {} if dist: requirements = [ Requirement.from_pkg_requirement(r) for r in dist.requires(extras) ] for req in requirements: reqs[req.identify()] = req version = dist.version else: version = None node = Package(key, version, reqs) if node not in graph: if extras: node_with_extras.add(name) graph.add(node) for k in reqs: child = add_package(k, working_set.get(strip_extras(k)[0])) graph.connect(node, child) return node for k, dist in working_set.items(): add_package(k, dist) for node in graph._vertices.copy(): if node is not None and not list(graph.iter_parents(node)): # Top requirements if node.name in node_with_extras: # Already included in package[extra], no need to keep the top level # non-extra package. graph.remove(node) else: graph.connect(None, node) return graph
def build_dependency_graph( working_set: WorkingSet, marker_env: dict[str, str] | None = None) -> DirectedGraph: """Build a dependency graph from locked result.""" graph: DirectedGraph[Package | None] = DirectedGraph() graph.add(None) # sentinel parent of top nodes. node_with_extras: set[str] = set() def add_package(key: str, dist: Distribution | None) -> Package: name, extras = strip_extras(key) extras = extras or () reqs: dict[str, Requirement] = {} if dist: requirements = (parse_requirement(r) for r in filter_requirements_with_extras( cast(str, dist.metadata["Name"]), dist.requires or [], extras, include_default=True, )) for req in requirements: if not req.marker or req.marker.evaluate(marker_env): reqs[req.identify()] = req version: str | None = dist.version else: version = None node = Package(key, version, reqs) if node not in graph: if extras: node_with_extras.add(name) graph.add(node) for k in reqs: child = add_package(k, working_set.get(strip_extras(k)[0])) graph.connect(node, child) return node for k, dist in working_set.items(): add_package(k, dist) for node in list(graph): if node is not None and not list(graph.iter_parents(node)): # Top requirements if node.name in node_with_extras: # Already included in package[extra], no need to keep the top level # non-extra package. graph.remove(node) else: graph.connect(None, node) return graph
def format_dependency_graph(project: Project, graph: DirectedGraph) -> str: """Format dependency graph for output.""" content = [] all_dependencies = ChainMap(*project.all_dependencies.values()) for package in sorted(graph.iter_children(None), key=lambda p: p.name): if package.name in all_dependencies: required = str(all_dependencies[package.name].specifier or "Any") elif (not project.environment.is_global and package.name == project.meta.project_name.lower()): required = "This project" else: required = "" content.append(format_package(graph, package, required, "", set())) return "".join(content).strip()
def format_package( graph: DirectedGraph, package: Package, required: str = "", prefix: str = "", visited: Optional[Set[str]] = None, ) -> str: """Format one package. :param graph: the dependency graph :param package: the package instance :param required: the version required by its parent :param prefix: prefix text for children :param visited: the visited package collection """ if visited is None: visited = set() result = [] version = ( termui.red("[ not installed ]") if not package.version else termui.red(package.version) if required and required not in ("Any", "This project") and not SpecifierSet(required).contains(package.version) else termui.yellow(package.version) ) if package.name in visited: version = termui.red("[circular]") required = f"[ required: {required} ]" if required else "[ Not required ]" result.append(f"{termui.green(package.name, bold=True)} {version} {required}\n") if package.name in visited: return "".join(result) visited.add(package.name) children = sorted(graph.iter_children(package), key=lambda p: p.name) for i, child in enumerate(children): is_last = i == len(children) - 1 head = LAST_CHILD if is_last else NON_LAST_CHILD cur_prefix = LAST_PREFIX if is_last else NON_LAST_PREFIX required = str(package.requirements[child.name].specifier or "Any") result.append( prefix + head + format_package( graph, child, required, prefix + cur_prefix, visited.copy() ) ) return "".join(result)
def format_reverse_package( graph: DirectedGraph, package: Package, child: Optional[Package] = None, requires: str = "", prefix: str = "", visited: Optional[Set[str]] = None, ): """Format one package for output reverse dependency graph.""" if visited is None: visited = set() result = [] version = ( termui.red("[ not installed ]") if not package.version else termui.yellow(package.version) ) if package.name in visited: version = termui.red("[circular]") requires = ( f"[ requires: {termui.red(requires)} ]" if requires not in ("Any", "") and child and child.version and not SpecifierSet(requires).contains(child.version) else "" if not requires else f"[ requires: {requires} ]" ) result.append(f"{termui.green(package.name, bold=True)} {version} {requires}\n") if package.name in visited: return "".join(result) visited.add(package.name) parents = sorted(filter(None, graph.iter_parents(package)), key=lambda p: p.name) for i, parent in enumerate(parents): is_last = i == len(parents) - 1 head = LAST_CHILD if is_last else NON_LAST_CHILD cur_prefix = LAST_PREFIX if is_last else NON_LAST_PREFIX requires = str(parent.requirements[package.name].specifier or "Any") result.append( prefix + head + format_reverse_package( graph, parent, package, requires, prefix + cur_prefix, visited.copy() ) ) return "".join(result)
def _format_forward_dependency_graph(project: Project, graph: DirectedGraph) -> str: """Format dependency graph for output.""" content = [] all_dependencies = ChainMap(*project.all_dependencies.values()) top_level_dependencies = sorted(graph.iter_children(None), key=lambda p: p.name) for package in top_level_dependencies: if package.name in all_dependencies: required = specifier_from_requirement( all_dependencies[package.name]) elif package_is_project(package, project): required = "This project" else: required = "" content.append(format_package(graph, package, required, "")) return "".join(content).strip()
def graph(): return DirectedGraph()