diff --git a/news/10036.feature.rst b/news/10036.feature.rst new file mode 100644 index 00000000000..d34441765f0 --- /dev/null +++ b/news/10036.feature.rst @@ -0,0 +1,2 @@ +Add ``required-by`` option to the ``list`` command, in order to restrict the listed +packages to some package requirements (useful for eg. with ``--outdated``) diff --git a/src/pip/_internal/commands/list.py b/src/pip/_internal/commands/list.py index dcf9432638a..743b746ea4c 100644 --- a/src/pip/_internal/commands/list.py +++ b/src/pip/_internal/commands/list.py @@ -94,6 +94,13 @@ def add_options(self): "installed packages.", ) + self.cmd_opts.add_option( + '--required-by', + action='store', + dest='required_by', + help="List packages that are dependencies the given package.", + ) + self.cmd_opts.add_option( '--exclude-editable', action='store_false', @@ -161,6 +168,9 @@ def run(self, options, args): if options.not_required: packages = self.get_not_required(packages, options) + if options.required_by: + packages = self.get_required_by(packages, options) + if options.outdated: packages = self.get_outdated(packages, options) elif options.uptodate: @@ -194,6 +204,18 @@ def get_not_required(self, packages, options): # get_uptodate return list({pkg for pkg in packages if pkg.key not in dep_keys}) + def get_required_by(self, packages, options): + # type: (List[Distribution], Values) -> List[Distribution] + dep_keys = set() # type: Set[Distribution] + for dist in packages: + if dist.project_name == options.required_by: + dep_keys = set(requirement.key for requirement in dist.requires()) + + # Create a set to remove duplicate packages, and cast it to a list + # to keep the return type consistent with get_outdated and + # get_uptodate + return list({pkg for pkg in packages if pkg.key in dep_keys}) + def iter_packages_latest_infos(self, packages, options): # type: (List[Distribution], Values) -> Iterator[Distribution] with self._build_session(options) as session: diff --git a/tests/functional/test_list.py b/tests/functional/test_list.py index 40dfbdea30d..29621a0d083 100644 --- a/tests/functional/test_list.py +++ b/tests/functional/test_list.py @@ -403,6 +403,20 @@ def test_outdated_not_required_flag(script, data): assert [] == json.loads(result.stdout) +def test_required_by_flag(script, data): + """ + test the behavior of --required-by flag in the list command + """ + script.pip( + 'install', '-f', data.find_links, '--no-index', 'require_simple==1.0' + ) + result = script.pip( + 'list', '-f', data.find_links, '--no-index', + '--required-by', 'require-simple', '--format=json', + ) + assert [{'name': 'simple', 'version': '3.0'}] == json.loads(result.stdout) + + def test_outdated_pre(script, data): script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0')