Repository: pdm-project/pdm

Issue:
 pdm outdated 打印出的信息 有些反直觉
# pyproject.toml
```
[project]
name = "test2pdm"
version = "0.1.0"
description = "Default template for PDM package"

dependencies = [
    "django>=5.1.2",
    "requests==2.25.1",
]
requires-python = "==3.12.*"
readme = "README.md"
license = {text = "MIT"}


[tool.pdm]
distribution = false

```
# 情况描述

 我执行 ` pdm outdated `,  输出如下：
![image](https://github.com/user-attachments/assets/b61edbf9-2b2e-4bb3-90f0-1cac868d902c)

在我 `pyproject.toml` 文件 的 `dependencies`配置里，只有 `django,requests`  ， 但是  ` pdm outdated `  还额外列出了别的

这让我觉得迷糊

如果我这时候，升级了其他几个包， 有没有可能导致我原本的  `django,requests`  出错？

-----

在我 `pyproject.toml` 文件 的 `dependencies`配置里，目前只有 `django,requests`  ，如果未来有几十个包，那 ` pdm outdated `  是不是 会列出上百个包？  到时候我 想检测哪些包 过期了， 是不是看的眼花缭乱


-----
# 期待的解决方案

 ` pdm outdated `   只检测出 在我 `pyproject.toml` 文件 的 `dependencies`配置里 过期的包









Discussion:
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/pdm-project/pdm/pull/3219?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project) Report
Attention: Patch coverage is `75.00000%` with `4 lines` in your changes missing coverage. Please review.
> Project coverage is 85.55%. Comparing base [(`d85760a`)](https://app.codecov.io/gh/pdm-project/pdm/commit/d85760a525a80693bb9e4005632f01a377d9858c?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project) to head [(`25314b2`)](https://app.codecov.io/gh/pdm-project/pdm/commit/25314b278fea4640eb697524e3fcb63e02dbaee3?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project).

| [Files with missing lines](https://app.codecov.io/gh/pdm-project/pdm/pull/3219?dropdown=coverage&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/outdated.py](https://app.codecov.io/gh/pdm-project/pdm/pull/3219?src=pr&el=tree&filepath=src%2Fpdm%2Fcli%2Fcommands%2Foutdated.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project#diff-c3JjL3BkbS9jbGkvY29tbWFuZHMvb3V0ZGF0ZWQucHk=) | 75.00% | [4 Missing :warning: ](https://app.codecov.io/gh/pdm-project/pdm/pull/3219?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    #3219      +/-   ##
==========================================
- Coverage   85.59%   85.55%   -0.04%     
==========================================
  Files         112      112              
  Lines       11257    11269      +12     
  Branches     2442     2446       +4     
==========================================
+ Hits         9635     9641       +6     
- Misses       1105     1109       +4     
- Partials      517      519       +2     
```

| [Flag](https://app.codecov.io/gh/pdm-project/pdm/pull/3219/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/3219/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project) | `85.35% <75.00%> (-0.04%)` | :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/3219?dropdown=coverage&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/3218.bugfix.md b/news/3218.bugfix.md
new file mode 100644
index 00000000..ad73ddaa
--- /dev/null
+++ b/news/3218.bugfix.md
@@ -0,0 +1,1 @@
+By default, `pdm outdated` will only list direct dependencies. This can be changed by adding the `--include-sub` option.
diff --git a/src/pdm/cli/commands/outdated.py b/src/pdm/cli/commands/outdated.py
index 81534337..d28bc8cf 100644
--- a/src/pdm/cli/commands/outdated.py
+++ b/src/pdm/cli/commands/outdated.py
@@ -6,7 +6,7 @@ from concurrent.futures import ThreadPoolExecutor
 from dataclasses import asdict, dataclass
 from fnmatch import fnmatch
 from itertools import zip_longest
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, cast
 
 from pdm.cli.commands.base import BaseCommand
 from pdm.cli.utils import normalize_pattern
@@ -24,6 +24,7 @@ if TYPE_CHECKING:
 @dataclass
 class ListPackage:
     package: str
+    groups: list[str]
     installed_version: str
     pinned_version: str
     latest_version: str = ""
@@ -46,6 +47,7 @@ class Command(BaseCommand):
         parser.add_argument(
             "--json", action="store_const", const="json", dest="format", default="table", help="Output in JSON format"
         )
+        parser.add_argument("--include-sub", action="store_true", help="Include sub-dependencies")
         parser.add_argument("patterns", nargs="*", help="The packages to check", type=normalize_pattern)
 
     @staticmethod
@@ -92,6 +94,10 @@ class Command(BaseCommand):
         resolved = {strip_extras(k)[0]: v for k, v in project.get_locked_repository().candidates.items()}
 
         collected: list[ListPackage] = []
+        project_dependencies: dict[str, list[str]] = {}
+        for group, dependencies in project.all_dependencies.items():
+            for dep in dependencies:
+                project_dependencies.setdefault(cast(str, dep.key), []).append(group)
 
         for name, distribution in installed.items():
             if not self._match_pattern(name, options.patterns):
@@ -99,14 +105,20 @@ class Command(BaseCommand):
             if project.name and name == normalize_name(project.name):
                 continue
             constrained_version = resolved.pop(name).version or "" if name in resolved else ""
-            collected.append(ListPackage(name, distribution.version or "", constrained_version))
+            if not options.include_sub and name not in project_dependencies:
+                continue
+            groups = project_dependencies.get(name, [])
+            collected.append(ListPackage(name, groups, distribution.version or "", constrained_version))
 
         for name, candidate in resolved.items():
             if not self._match_pattern(name, options.patterns):
                 continue
             if candidate.req.marker and not candidate.req.marker.matches(environment.spec):
                 continue
-            collected.append(ListPackage(name, "", candidate.version or ""))
+            if not options.include_sub and name not in project_dependencies:
+                continue
+            groups = project_dependencies.get(name, [])
+            collected.append(ListPackage(name, groups, "", candidate.version or ""))
 
         with environment.get_finder() as finder, ThreadPoolExecutor() as executor:
             for package in collected:
@@ -121,11 +133,12 @@ class Command(BaseCommand):
         else:
             rows = [
                 (
-                    package.package,
+                    f"[bold]{package.package}[/]",
+                    ", ".join(package.groups),
                     package.installed_version,
                     self._render_version(package.pinned_version, package.installed_version),
                     self._render_version(package.latest_version, package.installed_version),
                 )
                 for package in collected
             ]
-            project.core.ui.display_columns(rows, header=["Package", "Installed", "Pinned", "Latest"])
+            project.core.ui.display_columns(rows, header=["Package", "Groups", "Installed", "Pinned", "Latest"])


Test patch:
diff --git a/tests/cli/test_outdated.py b/tests/cli/test_outdated.py
index 4c6aa4df..541f0d00 100644
--- a/tests/cli/test_outdated.py
+++ b/tests/cli/test_outdated.py
@@ -29,13 +29,19 @@ def test_outdated(project, pdm, index):
 """
 
     result = pdm(["outdated"], obj=project, strict=True, cleanup=False)
-    assert "| requests | 2.19.1    | 2.19.1 | 2.20.0 |" in result.stdout
+    assert "| requests | default | 2.19.1    | 2.19.1 | 2.20.0 |" in result.stdout
 
     result = pdm(["outdated", "re*"], obj=project, strict=True, cleanup=False)
-    assert "| requests | 2.19.1    | 2.19.1 | 2.20.0 |" in result.stdout
+    assert "| requests | default | 2.19.1    | 2.19.1 | 2.20.0 |" in result.stdout
 
     result = pdm(["outdated", "--json"], obj=project, strict=True, cleanup=False)
     json_output = json.loads(result.stdout)
     assert json_output == [
-        {"package": "requests", "installed_version": "2.19.1", "pinned_version": "2.19.1", "latest_version": "2.20.0"}
+        {
+            "package": "requests",
+            "groups": ["default"],
+            "installed_version": "2.19.1",
+            "pinned_version": "2.19.1",
+            "latest_version": "2.20.0",
+        }
     ]