Repository: pdm-project/pdm
Pull request #2539 (2024-01-05 22:15:48)

Fixed pdm list output containing full license text in some cases
## Pull Request Checklist

- [X] A news fragment is added in `news/` describing what is new.
- [X] Test cases added for changed code.

## Describe what you have changed in this PR.

The full license string can sometimes be contained inside the package metadata license field, for example:

`license = { file="LICENSE" }` in pyproject.toml

This has been fixed by checking for the presence of newlines in the `license` variable, or if it is larger than 50 chars (slightly more than the longest OSI license field).

The tests have been updated.

Note that when exporting the markdown file that contains all the licenses (and full text) the full text license is still not exported.  This is because it will always look inside: `locations = ("**/LICENSE*", "**/LICENCE*", "**/COPYING*", "**/NOTICE*")`

Closes https://github.com/pdm-project/pdm/issues/2538


# Before
```
╭────────────────────────────┬────────────────────────────────┬───────────────────────────────────────────────────────────────────────────────╮
│ name                       │ version                        │ licenses                                                                      │
├────────────────────────────┼────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
│ Arpeggio                   │ 2.0.0                          │ MIT                                                                           │
│ arrow                      │ 1.2.3                          │ Apache 2.0                                                                    │
│ attrs                      │ 23.1.0                         │ MIT License                                                                   │
│ binaryornot                │ 0.4.4                          │ BSD                                                                           │
...
│ platformdirs               │ 3.10.0                         │ MIT License                                                                   │
│ pluggy                     │ 1.2.0                          │ MIT                                                                           │
│ plumbum                    │ 1.8.2                          │ Copyright (c) 2013 Tomer Filiba (tomerfiliba@gmail.com)                       │
│                            │                                │                                                                               │
│                            │                                │ Permission is hereby granted, free of charge, to any person obtaining a copy  │
│                            │                                │ of this software and associated documentation files (the "Software"), to deal │
│                            │                                │ in the Software without restriction, including without limitation the rights  │
│                            │                                │ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell     │
```

# After
```
╭────────────────────────────┬────────────────────────────────┬─────────────────────────────────────────────────╮
│ name                       │ version                        │ licenses                                        │
├────────────────────────────┼────────────────────────────────┼─────────────────────────────────────────────────┤
│ Arpeggio                   │ 2.0.0                          │ MIT                                             │
│ arrow                      │ 1.2.3                          │ Apache 2.0                                      │
│ attrs                      │ 23.1.0                         │ MIT License                                     │
│ binaryornot                │ 0.4.4                          │ BSD                                             │
...
│ platformdirs               │ 3.10.0                         │ MIT License                                     │
│ pluggy                     │ 1.2.0                          │ MIT                                             │
│ plumbum                    │ 1.8.2                          │ MIT License                                     │
│ prompt-toolkit             │ 3.0.38                         │ BSD License                                     │
│ pycomplete                 │ 0.4.0                          │ BSD-3-Clause                                    │

```

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/pdm-project/pdm/pull/2539?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project) Report
Attention: `2 lines` in your changes are missing coverage. Please review.
> Comparison is base [(`ca74c76`)](https://app.codecov.io/gh/pdm-project/pdm/commit/ca74c76dca41a456280df8d05903c36736bc762f?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project) 84.63% compared to head [(`c7f5efb`)](https://app.codecov.io/gh/pdm-project/pdm/pull/2539?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project) 84.62%.

| [Files](https://app.codecov.io/gh/pdm-project/pdm/pull/2539?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project) | Patch % | Lines |
|---|---|---|
| [src/pdm/cli/commands/list.py](https://app.codecov.io/gh/pdm-project/pdm/pull/2539?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project#diff-c3JjL3BkbS9jbGkvY29tbWFuZHMvbGlzdC5weQ==) | 50.00% | [1 Missing and 1 partial :warning: ](https://app.codecov.io/gh/pdm-project/pdm/pull/2539?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #2539      +/-   ##
==========================================
- Coverage   84.63%   84.62%   -0.02%     
==========================================
  Files         104      104              
  Lines       10343    10346       +3     
  Branches     2261     2262       +1     
==========================================
+ Hits         8754     8755       +1     
- Misses       1116     1117       +1     
- Partials      473      474       +1     
```

| [Flag](https://app.codecov.io/gh/pdm-project/pdm/pull/2539/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project) | Coverage Δ | |
|---|---|---|
| [unittests](https://app.codecov.io/gh/pdm-project/pdm/pull/2539/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project) | `84.40% <50.00%> (-0.02%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project#carryforward-flags-in-the-pull-request-comment) to find out more.


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/pdm-project/pdm/pull/2539?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project).


Patch:
diff --git a/news/2538.bugfix.md b/news/2538.bugfix.md
new file mode 100644
index 00000000..d7bea435
--- /dev/null
+++ b/news/2538.bugfix.md
@@ -0,0 +1,1 @@
+Fixed pdm list output containing full license text in some cases
\ No newline at end of file
diff --git a/src/pdm/cli/commands/list.py b/src/pdm/cli/commands/list.py
index 01f0505c..0ed311a4 100644
--- a/src/pdm/cli/commands/list.py
+++ b/src/pdm/cli/commands/list.py
@@ -340,7 +340,15 @@ class Listable:
         # so generate a pipe separated list (to avoid complexity with CSV export).
         self.licenses: str | None = dist.metadata["License"]
         self.licenses = None if self.licenses == "UNKNOWN" else self.licenses
-        if not self.licenses:
+
+        # Sometimes package metadata contains the full license text.
+        # e.g. license = { file="LICENSE" } in pyproject.toml
+        # To identify this, check for newlines or very long strings.
+        # 50 chars is picked because the longest OSI license (WTFPL) full name is 43 characters.
+        is_full_text = self.licenses and "\n" in self.licenses or len(self.licenses or "") > 50
+
+        # If that is the case, look at the classifiers instead.
+        if not self.licenses or is_full_text:
             classifier_licenses = [v for v in dist.metadata.get_all("Classifier", []) if v.startswith("License")]
             alternatives = [parts.split("::") for parts in classifier_licenses]
             alternatives = [part[-1].strip() for part in alternatives if part]


Test patch:
diff --git a/tests/cli/test_list.py b/tests/cli/test_list.py
index 49615b65..3bd56dea 100644
--- a/tests/cli/test_list.py
+++ b/tests/cli/test_list.py
@@ -513,8 +513,16 @@ def fake_working_set(working_set):
         def read_text(self, *args, **kwargs):
             return self.license_text
 
-    # Foo package.
-    foo = Distribution("foo", "0.1.0", metadata={"License": "A License"})
+    # Foo package (adapted to contain newlines in License field)
+    # e.g. via license = { file="LICENSE" }
+    foo = Distribution(
+        "foo",
+        "0.1.0",
+        metadata={
+            "License": "A License\n\nextra\ntext",
+            "Classifier": "License :: A License",
+        },
+    )
     foo_l = _MockPackagePath("foo-0.1.0.dist-info", "LICENSE")
     foo_l.license_text = "license text for foo here"
     foo.files = [foo_l]
@@ -679,6 +687,9 @@ def test_list_json_fields_licences(project, pdm):
 
 @pytest.mark.usefixtures("fake_working_set")
 def test_list_markdown_fields_licences(project, pdm):
+    # Note that in "foo" the "License" metadata field ("License": "A License\n\nextra\ntext")
+    # is ignored, in favour of the classifier and the LICENSE file.
+    # This behaviour could be improved.
     result = pdm(["list", "--markdown", "--fields", "name,version,licenses"], obj=project)
     expected = (
         "# test-project licenses\n"