diff --git a/src/pipdeptree/__init__.py b/src/pipdeptree/__init__.py index 20fdc3f..63f254d 100644 --- a/src/pipdeptree/__init__.py +++ b/src/pipdeptree/__init__.py @@ -270,7 +270,19 @@ class PackageDAG(Mapping): def from_pkgs(cls, pkgs): pkgs = [DistPackage(p) for p in pkgs] idx = {p.key: p for p in pkgs} - m = {p: [ReqPackage(r, idx.get(r.key)) for r in p.requires()] for p in pkgs} + m = {} + for p in pkgs: + reqs = [] + for r in p.requires(): + d = idx.get(r.key) + # pip's _vendor.packaging.requirements.Requirement uses the exact casing of a dependency's name found in + # a project's build config, which is not ideal when rendering. + # See https://github.com/tox-dev/pipdeptree/issues/242 + r.project_name = d.project_name if d is not None else r.project_name + pkg = ReqPackage(r, d) + reqs.append(pkg) + m[p] = reqs + return cls(m) def __init__(self, m) -> None: diff --git a/tests/test_pipdeptree.py b/tests/test_pipdeptree.py index e66edd1..2b04a67 100644 --- a/tests/test_pipdeptree.py +++ b/tests/test_pipdeptree.py @@ -39,7 +39,7 @@ if TYPE_CHECKING: def mock_pkgs(simple_graph): for node, children in simple_graph.items(): nk, nv = node - m = mock.Mock(key=nk, project_name=nk, version=nv) + m = mock.Mock(key=nk.lower(), project_name=nk, version=nv) as_req = mock.Mock(key=nk, project_name=nk, specs=[("==", nv)]) m.as_requirement = mock.Mock(return_value=as_req) reqs = [] @@ -162,6 +162,20 @@ def test_package_dag_reverse(): assert all(isinstance(v, ReqPackage) for v in chain.from_iterable(t2.values())) +def test_package_dag_from_pkgs(): + # when pip's _vendor.packaging.requirements.Requirement's requires() gives a lowercased package name but the actual + # package name in PyPI is mixed case, expect the mixed case version + graph = { + ("examplePy", "1.2.3"): [("hellopy", [(">=", "2.0.0")])], + ("HelloPy", "2.2.0"): [], + } + t = mock_package_dag(graph) + parent_key = "examplepy" + c = t.get_children(parent_key) + assert len(c) == 1 + assert c[0].project_name == "HelloPy" + + # Tests for Package classes # # Note: For all render methods, we are only testing for frozen=False