Repository: tox-dev/pipdeptree
Pull request #465 (2025-03-20 14:15:06)

Support for License-Expression field
Allows to display a license for projects with a new License-Expression field. For example, pipdeptree itself.

https://peps.python.org/pep-0639/#add-license-expression-field

PalmtopTiger: Done
PalmtopTiger: No, it's not fine, judging by the fact that setuptools v77 [doesn't allow](https://github.com/pypa/setuptools/blob/7c859e017368360ba66c8cc591279d8964c031bc/setuptools/dist.py#L430) you to install a package where license expression and license classifier are specified at the same time.

I also think that only license expression should be returned if it is available. That's why I placed the code for getting it above the old code. Since you think the same, I've reworked the code.


Patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 8032dad..7e911e8 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -23,7 +23,7 @@ repos:
     hooks:
       - id: pyproject-fmt
   - repo: https://github.com/astral-sh/ruff-pre-commit
-    rev: "v0.11.0"
+    rev: "v0.11.2"
     hooks:
       - id: ruff-format
       - id: ruff
diff --git a/src/pipdeptree/_models/package.py b/src/pipdeptree/_models/package.py
index cc3efaf..8d9072a 100644
--- a/src/pipdeptree/_models/package.py
+++ b/src/pipdeptree/_models/package.py
@@ -39,13 +39,15 @@ class Package(ABC):
         except PackageNotFoundError:
             return self.UNKNOWN_LICENSE_STR
 
+        if license_str := dist_metadata.get("License-Expression"):
+            return f"({license_str})"
+
         license_strs: list[str] = []
         classifiers = dist_metadata.get_all("Classifier", [])
-
         for classifier in classifiers:
             line = str(classifier)
             if line.startswith("License"):
-                license_str = line.split(":: ")[-1]
+                license_str = line.rsplit(":: ", 1)[-1]
                 license_strs.append(license_str)
 
         if len(license_strs) == 0:


Test patch:
diff --git a/tests/_models/test_package.py b/tests/_models/test_package.py
index 43f1f4a..831a815 100644
--- a/tests/_models/test_package.py
+++ b/tests/_models/test_package.py
@@ -107,30 +107,53 @@ def test_dist_package_as_dict() -> None:
     ("mocked_metadata", "expected_output"),
     [
         pytest.param(
-            Mock(get_all=lambda *args, **kwargs: []),  # noqa: ARG005
+            Mock(
+                get=lambda *args, **kwargs: None,  # noqa: ARG005
+                get_all=lambda *args, **kwargs: [],  # noqa: ARG005
+            ),
             Package.UNKNOWN_LICENSE_STR,
             id="no-license",
         ),
         pytest.param(
             Mock(
+                get=lambda *args, **kwargs: None,  # noqa: ARG005
                 get_all=lambda *args, **kwargs: [  # noqa: ARG005
                     "License :: OSI Approved :: GNU General Public License v2 (GPLv2)",
                     "Operating System :: OS Independent",
-                ]
+                ],
             ),
             "(GNU General Public License v2 (GPLv2))",
             id="one-license-with-one-non-license",
         ),
         pytest.param(
             Mock(
+                get=lambda *args, **kwargs: None,  # noqa: ARG005
                 get_all=lambda *args, **kwargs: [  # noqa: ARG005
                     "License :: OSI Approved :: GNU General Public License v2 (GPLv2)",
                     "License :: OSI Approved :: Apache Software License",
-                ]
+                ],
             ),
             "(GNU General Public License v2 (GPLv2), Apache Software License)",
             id="more-than-one-license",
         ),
+        pytest.param(
+            Mock(
+                get=lambda *args, **kwargs: "MIT",  # noqa: ARG005
+                get_all=lambda *args, **kwargs: [],  # noqa: ARG005
+            ),
+            "(MIT)",
+            id="license-expression",
+        ),
+        pytest.param(
+            Mock(
+                get=lambda *args, **kwargs: "MIT",  # noqa: ARG005
+                get_all=lambda *args, **kwargs: [  # noqa: ARG005
+                    "License :: OSI Approved :: MIT License",
+                ],
+            ),
+            "(MIT)",
+            id="license-expression-with-license-classifier",
+        ),
     ],
 )
 def test_dist_package_licenses(mocked_metadata: Mock, expected_output: str, monkeypatch: pytest.MonkeyPatch) -> None: