Skip to content

Commit c4ff7f9

Browse files
committed
Add --required-by option to list command
This allows to do something like: ``` $ pip list --required-by mypackage Package Version -------------- -------- asyncpg 0.23.0 httpx 0.18.1 Jinja2 2.11.3 minicli 0.5.0 openpyxl 3.0.7 progressist 0.1.0 PyJWT 2.1.0 roll 0.13.0 ujson 1.35 ``` And yet more usesul in my workflow: ``` $ pip list --required-by mypackage --outdated Package Version Latest Type ---------- ------- ------ ----- Jinja2 2.11.3 3.0.1 wheel ujson 1.35 4.0.2 wheel ```
1 parent 3c1d181 commit c4ff7f9

File tree

3 files changed

+38
-0
lines changed

3 files changed

+38
-0
lines changed

news/10036.feature.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add `required-by` option to the `list` command, in order to restrict the listed
2+
packages to some package requirements (useful for eg. with `--outdated``)

src/pip/_internal/commands/list.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,13 @@ def add_options(self):
9494
"installed packages.",
9595
)
9696

97+
self.cmd_opts.add_option(
98+
'--required-by',
99+
action='store',
100+
dest='required_by',
101+
help="List packages that are dependencies the given package.",
102+
)
103+
97104
self.cmd_opts.add_option(
98105
'--exclude-editable',
99106
action='store_false',
@@ -161,6 +168,9 @@ def run(self, options, args):
161168
if options.not_required:
162169
packages = self.get_not_required(packages, options)
163170

171+
if options.required_by:
172+
packages = self.get_required_by(packages, options)
173+
164174
if options.outdated:
165175
packages = self.get_outdated(packages, options)
166176
elif options.uptodate:
@@ -194,6 +204,18 @@ def get_not_required(self, packages, options):
194204
# get_uptodate
195205
return list({pkg for pkg in packages if pkg.key not in dep_keys})
196206

207+
def get_required_by(self, packages, options):
208+
# type: (List[Distribution], Values) -> List[Distribution]
209+
dep_keys = set() # type: Set[Distribution]
210+
for dist in packages:
211+
if dist.project_name == options.required_by:
212+
dep_keys = set(requirement.key for requirement in dist.requires())
213+
214+
# Create a set to remove duplicate packages, and cast it to a list
215+
# to keep the return type consistent with get_outdated and
216+
# get_uptodate
217+
return list({pkg for pkg in packages if pkg.key in dep_keys})
218+
197219
def iter_packages_latest_infos(self, packages, options):
198220
# type: (List[Distribution], Values) -> Iterator[Distribution]
199221
with self._build_session(options) as session:

tests/functional/test_list.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,20 @@ def test_outdated_not_required_flag(script, data):
403403
assert [] == json.loads(result.stdout)
404404

405405

406+
def test_required_by_flag(script, data):
407+
"""
408+
test the behavior of --required-by flag in the list command
409+
"""
410+
script.pip(
411+
'install', '-f', data.find_links, '--no-index', 'require_simple==1.0'
412+
)
413+
result = script.pip(
414+
'list', '-f', data.find_links, '--no-index',
415+
'--required-by', 'require-simple', '--format=json',
416+
)
417+
assert [{'name': 'simple', 'version': '3.0'}] == json.loads(result.stdout)
418+
419+
406420
def test_outdated_pre(script, data):
407421
script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0')
408422

0 commit comments

Comments
 (0)