diff --git a/news/4768.feature.rst b/news/4768.feature.rst new file mode 100644 index 00000000000..a41818ad2f8 --- /dev/null +++ b/news/4768.feature.rst @@ -0,0 +1 @@ +Speedup tab autocompletion by lazy-importing certain modules. diff --git a/src/pip/_internal/build_env.py b/src/pip/_internal/build_env.py index b4cf423461d..657c3964ae8 100644 --- a/src/pip/_internal/build_env.py +++ b/src/pip/_internal/build_env.py @@ -4,7 +4,6 @@ import logging import os -import pathlib import site import sys import textwrap @@ -18,7 +17,6 @@ from pip._vendor.packaging.version import Version -from pip import __file__ as pip_location from pip._internal.cli.spinners import open_rich_spinner, open_spinner from pip._internal.exceptions import ( BuildDependencyInstallError, @@ -30,6 +28,7 @@ from pip._internal.metadata import get_default_environment, get_environment from pip._internal.utils.deprecation import deprecated from pip._internal.utils.logging import VERBOSE, capture_logging +from pip._internal.utils.misc import get_runnable_pip from pip._internal.utils.packaging import get_requirement from pip._internal.utils.subprocess import call_subprocess from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds @@ -61,22 +60,6 @@ def __init__(self, path: str) -> None: self.lib_dirs = _dedup(scheme.purelib, scheme.platlib) -def get_runnable_pip() -> str: - """Get a file to pass to a Python executable, to run the currently-running pip. - - This is used to run a pip subprocess, for installing requirements into the build - environment. - """ - source = pathlib.Path(pip_location).resolve().parent - - if not source.is_dir(): - # This would happen if someone is using pip from inside a zip file. In that - # case, we can use that directly. - return str(source) - - return os.fsdecode(source / "__pip-runner__.py") - - def _get_system_sitepackages() -> set[str]: """Get system site packages diff --git a/src/pip/_internal/cli/autocompletion.py b/src/pip/_internal/cli/autocompletion.py index f22cd1159a2..8add031618e 100644 --- a/src/pip/_internal/cli/autocompletion.py +++ b/src/pip/_internal/cli/autocompletion.py @@ -11,7 +11,6 @@ from pip._internal.cli.main_parser import create_main_parser from pip._internal.commands import commands_dict, create_command -from pip._internal.metadata import get_default_environment def autocomplete() -> None: @@ -51,6 +50,8 @@ def autocomplete() -> None: "uninstall", ] if should_list_installed: + from pip._internal.metadata import get_default_environment + env = get_default_environment() lc = current.lower() installed = [ diff --git a/src/pip/_internal/cli/main_parser.py b/src/pip/_internal/cli/main_parser.py index 5ce9f5a02d4..7c7fb1806e0 100644 --- a/src/pip/_internal/cli/main_parser.py +++ b/src/pip/_internal/cli/main_parser.py @@ -6,12 +6,11 @@ import subprocess import sys -from pip._internal.build_env import get_runnable_pip from pip._internal.cli import cmdoptions from pip._internal.cli.parser import ConfigOptionParser, UpdatingDefaultsHelpFormatter from pip._internal.commands import commands_dict, get_similar_commands from pip._internal.exceptions import CommandError -from pip._internal.utils.misc import get_pip_version, get_prog +from pip._internal.utils.misc import get_pip_version, get_prog, get_runnable_pip __all__ = ["create_main_parser", "parse_command"] diff --git a/src/pip/_internal/utils/misc.py b/src/pip/_internal/utils/misc.py index 66e521bbc1b..ac718a2383f 100644 --- a/src/pip/_internal/utils/misc.py +++ b/src/pip/_internal/utils/misc.py @@ -5,6 +5,7 @@ import hashlib import logging import os +import pathlib import posixpath import shutil import stat @@ -31,6 +32,7 @@ from pip._vendor.packaging.requirements import Requirement from pip._vendor.pyproject_hooks import BuildBackendHookCaller +from pip import __file__ as pip_location from pip import __version__ from pip._internal.exceptions import CommandError, ExternallyManagedEnvironment from pip._internal.locations import get_major_minor_version @@ -74,6 +76,22 @@ def get_pip_version() -> str: return f"pip {__version__} from {pip_pkg_dir} (python {get_major_minor_version()})" +def get_runnable_pip() -> str: + """Get a file to pass to a Python executable, to run the currently-running pip. + + This is used to run a pip subprocess, for installing requirements into the build + environment. + """ + source = pathlib.Path(pip_location).resolve().parent + + if not source.is_dir(): + # This would happen if someone is using pip from inside a zip file. In that + # case, we can use that directly. + return str(source) + + return os.fsdecode(source / "__pip-runner__.py") + + def normalize_version_info(py_version_info: tuple[int, ...]) -> tuple[int, int, int]: """ Convert a tuple of ints representing a Python version to one of length