Skip to content

Commit 1071614

Browse files
committed
Avoid heavy index imports for pip uninstall & list
pip uninstall and list currently depend on req_install.py which always imports the expensive network and index machinery. However, it's only in rare situations that these commands actually hit the network: - `pip list --outdated` - `pip list --uptodate` - `pip uninstall --requirement <url>` This commit builds on the previous two refactoring commits and modifies these commands to avoid the expensive imports unless truly necessary.
1 parent 55cfa54 commit 1071614

File tree

5 files changed

+21
-9
lines changed

5 files changed

+21
-9
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
pip uninstall and list currently depend on req_install.py which always imports
2+
the expensive network and index machinery. However, it's only in rare situations
3+
that these commands actually hit the network:
4+
5+
- ``pip list --outdated``
6+
- ``pip list --uptodate``
7+
- ``pip uninstall --requirement <url>``
8+
9+
This patch refactors req_install.py so these commands can avoid the expensive
10+
imports unless truly necessary.

src/pip/_internal/commands/list.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,14 @@
1010
from pip._internal.cli.index_command import IndexGroupCommand
1111
from pip._internal.cli.status_codes import SUCCESS
1212
from pip._internal.exceptions import CommandError
13-
from pip._internal.index.collector import LinkCollector
14-
from pip._internal.index.package_finder import PackageFinder
1513
from pip._internal.metadata import BaseDistribution, get_environment
1614
from pip._internal.models.selection_prefs import SelectionPreferences
17-
from pip._internal.network.session import PipSession
1815
from pip._internal.utils.compat import stdlib_pkgs
1916
from pip._internal.utils.misc import tabulate, write_output
2017

2118
if TYPE_CHECKING:
19+
from pip._internal.index.package_finder import PackageFinder
20+
from pip._internal.network.session import PipSession
2221

2322
class _DistWithLatestInfo(BaseDistribution):
2423
"""Give the distribution object a couple of extra fields.
@@ -140,11 +139,15 @@ def handle_pip_version_check(self, options: Values) -> None:
140139
super().handle_pip_version_check(options)
141140

142141
def _build_package_finder(
143-
self, options: Values, session: PipSession
144-
) -> PackageFinder:
142+
self, options: Values, session: "PipSession"
143+
) -> "PackageFinder":
145144
"""
146145
Create a package finder appropriate to this list command.
147146
"""
147+
# Lazy import the heavy index modules as most list invocations won't need 'em.
148+
from pip._internal.index.collector import LinkCollector
149+
from pip._internal.index.package_finder import PackageFinder
150+
148151
link_collector = LinkCollector.create(session, options=options)
149152

150153
# Pass allow_yanked=False to ignore yanked versions.

src/pip/_internal/commands/uninstall.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from pip._internal.cli import cmdoptions
88
from pip._internal.cli.base_command import Command
9-
from pip._internal.cli.req_command import SessionCommandMixin
9+
from pip._internal.cli.index_command import SessionCommandMixin
1010
from pip._internal.cli.status_codes import SUCCESS
1111
from pip._internal.exceptions import InstallationError
1212
from pip._internal.req import parse_requirements

tests/functional/test_cli.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,7 @@ def test_entrypoints_work(entrypoint: str, script: PipTestEnvironment) -> None:
5757
sorted(
5858
set(commands_dict).symmetric_difference(
5959
# Exclude commands that are expected to use the network.
60-
# TODO: uninstall and list should only import network modules as needed
61-
{"install", "uninstall", "download", "search", "index", "wheel", "list"}
60+
{"install", "download", "search", "index", "wheel"}
6261
)
6362
),
6463
)

tests/unit/test_commands.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ def is_requirement_command(command: Command) -> bool:
128128

129129

130130
@pytest.mark.parametrize("flag", ["", "--outdated", "--uptodate"])
131-
@mock.patch("pip._internal.cli.req_command.pip_self_version_check")
131+
@mock.patch("pip._internal.cli.index_command._pip_self_version_check")
132132
@mock.patch.dict(os.environ, {"PIP_DISABLE_PIP_VERSION_CHECK": "no"})
133133
def test_list_pip_version_check(version_check_mock: mock.Mock, flag: str) -> None:
134134
"""

0 commit comments

Comments
 (0)