Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-pipdeptree for
openSUSE:Factory checked in at 2026-03-17 19:04:31
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pipdeptree (Old)
and /work/SRC/openSUSE:Factory/.python-pipdeptree.new.8177 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pipdeptree"
Tue Mar 17 19:04:31 2026 rev:12 rq:1339441 version:2.31.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-pipdeptree/python-pipdeptree.changes
2026-01-27 16:17:37.671278275 +0100
+++
/work/SRC/openSUSE:Factory/.python-pipdeptree.new.8177/python-pipdeptree.changes
2026-03-17 19:06:08.352753713 +0100
@@ -1,0 +2,7 @@
+Mon Mar 16 21:32:26 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 2.31.0:
+ * Bump packaging to 26
+ * Support --depth for graphviz output format
+
+-------------------------------------------------------------------
Old:
----
pipdeptree-2.30.0.tar.gz
New:
----
pipdeptree-2.31.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-pipdeptree.spec ++++++
--- /var/tmp/diff_new_pack.KRCYj2/_old 2026-03-17 19:06:08.936777916 +0100
+++ /var/tmp/diff_new_pack.KRCYj2/_new 2026-03-17 19:06:08.940778082 +0100
@@ -17,7 +17,7 @@
Name: python-pipdeptree
-Version: 2.30.0
+Version: 2.31.0
Release: 0
Summary: Command line utility to show dependency tree of packages
License: MIT
@@ -28,13 +28,13 @@
BuildRequires: %{python_module hatchling}
BuildRequires: fdupes
BuildRequires: python-rpm-macros
-Requires: python-packaging >= 25
+Requires: python-packaging >= 26
Requires: python-pip >= 25.2
Suggests: python-graphviz >= 0.21
BuildArch: noarch
# SECTION test requirements
BuildRequires: %{python_module graphviz >= 0.21}
-BuildRequires: %{python_module packaging >= 25}
+BuildRequires: %{python_module packaging >= 26}
BuildRequires: %{python_module pip >= 25.2}
BuildRequires: %{python_module pytest-mock}
BuildRequires: %{python_module pytest}
++++++ pipdeptree-2.30.0.tar.gz -> pipdeptree-2.31.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pipdeptree-2.30.0/.github/workflows/check.yaml
new/pipdeptree-2.31.0/.github/workflows/check.yaml
--- old/pipdeptree-2.30.0/.github/workflows/check.yaml 2025-11-12
05:11:56.000000000 +0100
+++ new/pipdeptree-2.31.0/.github/workflows/check.yaml 2026-02-19
06:25:53.000000000 +0100
@@ -31,7 +31,7 @@
steps:
- name: Install OS dependencies
run: sudo apt-get install graphviz -y
- - uses: actions/checkout@v5
+ - uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Install the latest version of uv
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pipdeptree-2.30.0/.github/workflows/release.yaml
new/pipdeptree-2.31.0/.github/workflows/release.yaml
--- old/pipdeptree-2.30.0/.github/workflows/release.yaml 2025-11-12
05:11:56.000000000 +0100
+++ new/pipdeptree-2.31.0/.github/workflows/release.yaml 2026-02-19
06:25:53.000000000 +0100
@@ -10,7 +10,7 @@
build:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v5
+ - uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Install the latest version of uv
@@ -22,7 +22,7 @@
- name: Build package
run: uv build --python 3.14 --python-preference only-managed --sdist
--wheel . --out-dir dist
- name: Store the distribution packages
- uses: actions/upload-artifact@v5
+ uses: actions/upload-artifact@v6
with:
name: ${{ env.dists-artifact-name }}
path: dist/*
@@ -38,7 +38,7 @@
id-token: write
steps:
- name: Download all the dists
- uses: actions/download-artifact@v6
+ uses: actions/download-artifact@v7
with:
name: ${{ env.dists-artifact-name }}
path: dist/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pipdeptree-2.30.0/.pre-commit-config.yaml
new/pipdeptree-2.31.0/.pre-commit-config.yaml
--- old/pipdeptree-2.30.0/.pre-commit-config.yaml 2025-11-12
05:11:56.000000000 +0100
+++ new/pipdeptree-2.31.0/.pre-commit-config.yaml 2026-02-19
06:25:53.000000000 +0100
@@ -5,7 +5,7 @@
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/python-jsonschema/check-jsonschema
- rev: 0.34.1
+ rev: 0.36.2
hooks:
- id: check-github-workflows
args: ["--verbose"]
@@ -15,21 +15,21 @@
- id: codespell
additional_dependencies: ["tomli>=2.3"]
- repo: https://github.com/tox-dev/tox-toml-fmt
- rev: "v1.2.0"
+ rev: "v1.6.0"
hooks:
- id: tox-toml-fmt
- repo: https://github.com/tox-dev/pyproject-fmt
- rev: "v2.11.1"
+ rev: "v2.16.0"
hooks:
- id: pyproject-fmt
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: "v0.14.4"
+ rev: "v0.15.1"
hooks:
- id: ruff-format
- id: ruff-check
args: ["--fix", "--unsafe-fixes", "--exit-non-zero-on-fix"]
- repo: https://github.com/rbubley/mirrors-prettier
- rev: "v3.6.2"
+ rev: "v3.8.1"
hooks:
- id: prettier
additional_dependencies:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pipdeptree-2.30.0/pyproject.toml
new/pipdeptree-2.31.0/pyproject.toml
--- old/pipdeptree-2.30.0/pyproject.toml 2025-11-12 05:11:56.000000000
+0100
+++ new/pipdeptree-2.31.0/pyproject.toml 2026-02-19 06:25:53.000000000
+0100
@@ -22,6 +22,7 @@
]
maintainers = [
{ name = "Bernát Gábor", email = "[email protected]" },
+ { name = "Kemal Zebari", email = "[email protected]" },
{ name = "Vineet Naik", email = "[email protected]" },
]
requires-python = ">=3.10"
@@ -42,7 +43,7 @@
"version",
]
dependencies = [
- "packaging>=25",
+ "packaging>=26",
"pip>=25.2",
]
optional-dependencies.graphviz = [
@@ -88,6 +89,7 @@
"DOC501", # TODO: Remove this once ruff supports Sphinx-style doc-strings;
see https://github.com/astral-sh/ruff/issues/12434
"INP001", # no implicit namespace
"ISC001", # Conflict with formatter
+ "RUF067", # `__init__` module should only contain docstrings and re-exports
"S101", # asserts allowed
"S104", # Possible binding to all interface
]
@@ -117,18 +119,18 @@
max_supported_python = "3.14"
[tool.coverage]
-html.show_contexts = true
-html.skip_covered = false
+run.parallel = true
+run.plugins = [
+ "covdefaults",
+]
paths.source = [
"src",
".tox/*/lib/python*/site-packages",
"*/src",
]
-run.parallel = true
-run.plugins = [
- "covdefaults",
-]
report.fail_under = 88
+html.show_contexts = true
+html.skip_covered = false
subtract_omit = "*/__main__.py"
[tool.mypy]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pipdeptree-2.30.0/src/pipdeptree/_cli.py
new/pipdeptree-2.31.0/src/pipdeptree/_cli.py
--- old/pipdeptree-2.30.0/src/pipdeptree/_cli.py 2025-11-12
05:11:56.000000000 +0100
+++ new/pipdeptree-2.31.0/src/pipdeptree/_cli.py 2026-02-19
06:25:53.000000000 +0100
@@ -121,7 +121,7 @@
"--depth",
type=lambda x: int(x) if x.isdigit() and (int(x) >= 0) else
parser.error("Depth must be a number that is >= 0"),
default=float("inf"),
- help="limit the depth of the tree (text and freeze render only)",
+ help="limit the depth of the tree (text, freeze, and graphviz render
only)",
metavar="D",
)
render.add_argument(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pipdeptree-2.30.0/src/pipdeptree/_freeze.py
new/pipdeptree-2.31.0/src/pipdeptree/_freeze.py
--- old/pipdeptree-2.30.0/src/pipdeptree/_freeze.py 2025-11-12
05:11:56.000000000 +0100
+++ new/pipdeptree-2.31.0/src/pipdeptree/_freeze.py 2026-02-19
06:25:53.000000000 +0100
@@ -5,9 +5,9 @@
from pathlib import Path
from typing import TYPE_CHECKING, Any
-from pip._internal.models.direct_url import (
- DirectUrl, # noqa: PLC2701
- DirectUrlValidationError, # noqa: PLC2701
+from pip._internal.models.direct_url import ( # noqa: PLC2701
+ DirectUrl,
+ DirectUrlValidationError,
)
from pip._internal.utils.egg_link import egg_link_path_from_sys_path # noqa:
PLC2701
from pip._vendor.packaging.version import Version # noqa: PLC2701
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pipdeptree-2.30.0/src/pipdeptree/_render/__init__.py
new/pipdeptree-2.31.0/src/pipdeptree/_render/__init__.py
--- old/pipdeptree-2.30.0/src/pipdeptree/_render/__init__.py 2025-11-12
05:11:56.000000000 +0100
+++ new/pipdeptree-2.31.0/src/pipdeptree/_render/__init__.py 2026-02-19
06:25:53.000000000 +0100
@@ -25,7 +25,9 @@
elif output_format == "freeze":
render_freeze(tree, max_depth=options.depth, list_all=options.all)
elif output_format.startswith("graphviz-"):
- render_graphviz(tree, output_format=output_format[len("graphviz-") :],
reverse=options.reverse)
+ render_graphviz(
+ tree, output_format=output_format[len("graphviz-") :],
reverse=options.reverse, max_depth=options.depth
+ )
else:
render_text(
tree,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pipdeptree-2.30.0/src/pipdeptree/_render/graphviz.py
new/pipdeptree-2.31.0/src/pipdeptree/_render/graphviz.py
--- old/pipdeptree-2.30.0/src/pipdeptree/_render/graphviz.py 2025-11-12
05:11:56.000000000 +0100
+++ new/pipdeptree-2.31.0/src/pipdeptree/_render/graphviz.py 2026-02-19
06:25:53.000000000 +0100
@@ -1,19 +1,117 @@
from __future__ import annotations
+import math
import os
import sys
+from collections import deque
from typing import TYPE_CHECKING
from pipdeptree._models import DistPackage, ReqPackage
if TYPE_CHECKING:
+ from graphviz import Digraph
+
from pipdeptree._models import PackageDAG
-def dump_graphviz( # noqa: C901
+def _get_all_dep_keys(tree: PackageDAG) -> set[str]:
+ """Return the set of keys that appear as dependencies of some package."""
+ dep_keys: set[str] = set()
+ for deps in tree.values():
+ dep_keys.update(dep.key for dep in deps)
+ return dep_keys
+
+
+def _build_reverse_graph(tree: PackageDAG, graph: Digraph, max_depth: float)
-> None: # noqa: C901, PLR0912
+ """Build graphviz nodes and edges for a reversed dependency tree."""
+ if max_depth < math.inf:
+ parent_keys = _get_all_dep_keys(tree)
+ root_keys = {dep_rev.key for dep_rev in tree if dep_rev.key not in
parent_keys}
+ visited: dict[str, int] = {}
+ queue: deque[tuple[str, int]] = deque((k, 0) for k in root_keys)
+ while queue:
+ key, depth = queue.popleft()
+ if key in visited:
+ continue
+ visited[key] = depth
+ if depth < max_depth:
+ for parent in tree.get_children(key):
+ if parent.key not in visited:
+ queue.append((parent.key, depth + 1))
+ for dep_rev, parents in tree.items():
+ if dep_rev.key not in visited:
+ continue
+ 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)
+ if visited[dep_rev.key] < max_depth:
+ for parent in parents:
+ assert isinstance(parent, DistPackage)
+ if parent.key in visited:
+ 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 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:
+ 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)
+
+
+def _build_forward_graph(tree: PackageDAG, graph: Digraph, max_depth: float)
-> None: # noqa: C901, PLR0912
+ """Build graphviz nodes and edges for a forward dependency tree."""
+ if max_depth < math.inf: # noqa: PLR1702
+ dep_keys = _get_all_dep_keys(tree)
+ root_keys = {pkg.key for pkg in tree if pkg.key not in dep_keys}
+ visited: dict[str, int] = {}
+ queue: deque[tuple[str, int]] = deque((k, 0) for k in root_keys)
+ while queue:
+ key, depth = queue.popleft()
+ if key in visited:
+ continue
+ visited[key] = depth
+ if depth < max_depth:
+ children = tree.get_children(key)
+ for dep in children:
+ if dep.key not in visited:
+ queue.append((dep.key, depth + 1))
+ for pkg, deps in tree.items():
+ if pkg.key not in visited:
+ continue
+ pkg_label = f"{pkg.project_name}\\n{pkg.version}"
+ graph.node(pkg.key, label=pkg_label)
+ if visited[pkg.key] < max_depth:
+ for dep in deps:
+ if dep.key in visited:
+ 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)
+ 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)
+
+
+def dump_graphviz(
tree: PackageDAG,
output_format: str = "dot",
is_reverse: bool = False, # noqa: FBT001, FBT002
+ max_depth: float = math.inf,
) -> str | bytes:
"""
Output dependency graph as one of the supported GraphViz output formats.
@@ -21,6 +119,7 @@
:param dict tree: dependency graph
:param string output_format: output format
:param bool is_reverse: reverse or not
+ :param float max_depth: maximum depth of the dependency tree to include
:returns: representation of tree in the specified output format
:rtype: str or binary representation depending on the output format
"""
@@ -45,27 +144,9 @@
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)
+ _build_reverse_graph(tree, graph, max_depth)
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)
+ _build_forward_graph(tree, graph, max_depth)
# Allow output of dot format, even if GraphViz isn't installed.
if output_format == "dot":
@@ -97,8 +178,8 @@
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)
+def render_graphviz(tree: PackageDAG, *, output_format: str, reverse: bool,
max_depth: float = math.inf) -> None:
+ output = dump_graphviz(tree, output_format=output_format,
is_reverse=reverse, max_depth=max_depth)
print_graphviz(output)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pipdeptree-2.30.0/tests/render/test_graphviz.py
new/pipdeptree-2.31.0/tests/render/test_graphviz.py
--- old/pipdeptree-2.30.0/tests/render/test_graphviz.py 2025-11-12
05:11:56.000000000 +0100
+++ new/pipdeptree-2.31.0/tests/render/test_graphviz.py 2026-02-19
06:25:53.000000000 +0100
@@ -68,3 +68,61 @@
assert out.startswith("<?xml")
assert "<svg" in out
assert out.strip().endswith("</svg>")
+
+
+def test_render_dot_with_depth(example_dag: PackageDAG) -> None:
+ output = dump_graphviz(example_dag, output_format="dot", max_depth=1)
+ assert isinstance(output, str)
+ # Roots are a and g (depth 0), their direct deps are at depth 1
+ # Deeper deps (d) should not appear
+ assert "a ->" in output
+ assert "g ->" in output
+ assert 'd [label="d' not in output # d is only reachable at depth 2+
+ # Direct deps of roots should appear
+ assert 'b [label="b' in output
+ assert 'c [label="c' in output
+ assert 'e [label="e' in output
+ assert 'f [label="f' in output
+
+
+def test_render_dot_reverse_with_depth(example_dag: PackageDAG) -> None:
+ reversed_dag = example_dag.reverse()
+ output = dump_graphviz(reversed_dag, output_format="dot", is_reverse=True,
max_depth=1)
+ assert isinstance(output, str)
+ # In reverse, root is e (only leaf in forward tree that's not a parent in
reverse)
+ # At depth 0: e, at depth 1: c, d, g (e's parents)
+ assert 'e [label="e' in output
+ assert 'c [label="c' in output
+ assert 'd [label="d' in output
+ assert 'g [label="g' in output
+ # Deeper nodes should not appear
+ assert 'a [label="a' not in output
+ assert 'b [label="b' not in output
+ assert 'f [label="f' not in output
+ # Edges from e to its parents should exist
+ assert "e -> c" in output
+ assert "e -> d" in output
+ assert "e -> g" in output
+
+
+def test_render_dot_reverse_infinite_depth(example_dag: PackageDAG) -> None:
+ reversed_dag = example_dag.reverse()
+ output = dump_graphviz(reversed_dag, output_format="dot", is_reverse=True)
+ assert isinstance(output, str)
+ # All nodes should appear with no depth limit
+ for node in ("a", "b", "c", "d", "e", "f", "g"):
+ assert f'{node} [label="' in output
+ # Edges should include reverse relationships
+ assert "e -> c" in output
+ assert "e -> d" in output
+ assert "b -> a" in output
+ assert "f -> g" in output
+
+
+def test_render_dot_with_depth_zero(example_dag: PackageDAG) -> None:
+ output = dump_graphviz(example_dag, output_format="dot", max_depth=0)
+ assert isinstance(output, str)
+ # Depth 0 means only root nodes, no edges
+ assert "a [label=" in output
+ assert "g [label=" in output
+ assert "->" not in output
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pipdeptree-2.30.0/tests/render/test_render.py
new/pipdeptree-2.31.0/tests/render/test_render.py
--- old/pipdeptree-2.30.0/tests/render/test_render.py 2025-11-12
05:11:56.000000000 +0100
+++ new/pipdeptree-2.31.0/tests/render/test_render.py 2026-02-19
06:25:53.000000000 +0100
@@ -37,7 +37,7 @@
def test_grahpviz_routing(option: list[str], mocker: MockerFixture) -> None:
render = mocker.patch("pipdeptree._render.render_graphviz")
main(option)
- render.assert_called_once_with(ANY, output_format="dot", reverse=False)
+ render.assert_called_once_with(ANY, output_format="dot", reverse=False,
max_depth=inf)
@pytest.mark.parametrize("option", [[], ["--output", "text"]])