Repository URL to install this package:
Version:
1.10.2 ▾
|
from __future__ import annotations
import os
import sys
from typing import TYPE_CHECKING
from pipdeptree._models import DistPackage, ReqPackage
if TYPE_CHECKING:
from pipdeptree._models import PackageDAG
def dump_graphviz( # noqa: C901, PLR0912
tree: PackageDAG,
output_format: str = "dot",
is_reverse: bool = False, # noqa: FBT001, FBT002
) -> str | bytes:
"""
Output dependency graph as one of the supported GraphViz output formats.
:param dict tree: dependency graph
:param string output_format: output format
:param bool is_reverse: reverse or not
:returns: representation of tree in the specified output format
:rtype: str or binary representation depending on the output format
"""
try:
from graphviz import Digraph
except ImportError as exc:
print( # noqa: T201
"graphviz is not available, but necessary for the output option. Please install it.",
file=sys.stderr,
)
raise SystemExit(1) from exc
try:
from graphviz import parameters
except ImportError:
from graphviz import backend
valid_formats = backend.FORMATS
print( # noqa: T201
"Deprecation warning! Please upgrade graphviz to version >=0.18.0 "
"Support for older versions will be removed in upcoming release",
file=sys.stderr,
)
else:
valid_formats = parameters.FORMATS
if output_format not in valid_formats:
print(f"{output_format} is not a supported output format.", file=sys.stderr) # noqa: T201
print(f"Supported formats are: {', '.join(sorted(valid_formats))}", file=sys.stderr) # noqa: T201
raise SystemExit(1)
graph = Digraph(format=output_format)
if is_reverse:
for dep_rev, parents in tree.items():
assert isinstance(dep_rev, ReqPackage)
dep_label = f"{dep_rev.project_name}\\n{dep_rev.installed_version}"
graph.node(dep_rev.key, label=dep_label)
for parent in parents:
# req reference of the dep associated with this particular parent package
assert isinstance(parent, DistPackage)
edge_label = (parent.req.version_spec if parent.req is not None else None) or "any"
graph.edge(dep_rev.key, parent.key, label=edge_label)
else:
for pkg, deps in tree.items():
pkg_label = f"{pkg.project_name}\\n{pkg.version}"
graph.node(pkg.key, label=pkg_label)
for dep in deps:
edge_label = dep.version_spec or "any"
if dep.is_missing:
dep_label = f"{dep.project_name}\\n(missing)"
graph.node(dep.key, label=dep_label, style="dashed")
graph.edge(pkg.key, dep.key, style="dashed")
else:
graph.edge(pkg.key, dep.key, label=edge_label)
# Allow output of dot format, even if GraphViz isn't installed.
if output_format == "dot":
# Emulates graphviz.dot.Dot.__iter__() to force the sorting of graph.body.
# Fixes https://github.com/tox-dev/pipdeptree/issues/188
# That way we can guarantee the output of the dot format is deterministic
# and stable.
return "".join([next(iter(graph)), *sorted(graph.body), graph._tail]) # noqa: SLF001
# As it's unknown if the selected output format is binary or not, try to
# decode it as UTF8 and only print it out in binary if that's not possible.
try:
return graph.pipe().decode("utf-8") # type: ignore[no-any-return]
except UnicodeDecodeError:
return graph.pipe() # type: ignore[no-any-return]
def print_graphviz(dump_output: str | bytes) -> None:
"""
Dump the data generated by GraphViz to stdout.
:param dump_output: The output from dump_graphviz
"""
if hasattr(dump_output, "encode"):
print(dump_output) # noqa: T201
else:
with os.fdopen(sys.stdout.fileno(), "wb") as bytestream:
bytestream.write(dump_output)
def render_graphviz(tree: PackageDAG, *, output_format: str, reverse: bool) -> None:
output = dump_graphviz(tree, output_format=output_format, is_reverse=reverse)
print_graphviz(output)
__all__ = [
"render_graphviz",
]