From 2d08badbd8c285c073d9cb200e1533cabccf94eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Hovm=C3=B6ller?= Date: Mon, 18 Mar 2019 14:38:33 +0100 Subject: [PATCH] First stab at reducing startup time --- src/pip/_internal/__init__.py | 8 +- src/pip/_internal/cli/autocompletion.py | 1 - src/pip/_internal/cli/base_command.py | 83 +++++++++++++-------- src/pip/_internal/cli/main_parser.py | 26 ++++--- src/pip/_internal/commands/check.py | 6 +- src/pip/_internal/commands/completion.py | 3 +- src/pip/_internal/commands/configuration.py | 21 ++++-- src/pip/_internal/commands/download.py | 19 +++-- src/pip/_internal/commands/freeze.py | 9 ++- src/pip/_internal/commands/hash.py | 9 ++- src/pip/_internal/commands/help.py | 4 +- src/pip/_internal/commands/install.py | 68 ++++++++++------- src/pip/_internal/commands/list.py | 31 +++++--- src/pip/_internal/commands/search.py | 35 +++++---- src/pip/_internal/commands/show.py | 18 +++-- src/pip/_internal/commands/uninstall.py | 13 ++-- src/pip/_internal/commands/wheel.py | 26 ++++--- src/pip/_internal/exceptions.py | 3 +- src/pip/_internal/utils/deprecation.py | 4 +- src/pip/_internal/utils/misc.py | 16 +--- src/pip/_internal/utils/misc_fast_import.py | 15 ++++ tests/functional/test_help.py | 2 +- tests/unit/test_utils.py | 3 +- 23 files changed, 257 insertions(+), 166 deletions(-) create mode 100644 src/pip/_internal/utils/misc_fast_import.py diff --git a/src/pip/_internal/__init__.py b/src/pip/_internal/__init__.py index 9c1637cfc11..52ddeca1ef6 100755 --- a/src/pip/_internal/__init__.py +++ b/src/pip/_internal/__init__.py @@ -37,14 +37,13 @@ else: securetransport.inject_into_urllib3() -from pip._internal.cli.autocompletion import autocomplete from pip._internal.cli.main_parser import parse_command from pip._internal.commands import commands_dict from pip._internal.exceptions import PipError from pip._internal.utils import deprecation + from pip._internal.vcs import git, mercurial, subversion, bazaar # noqa from pip._vendor.urllib3.exceptions import InsecureRequestWarning - logger = logging.getLogger(__name__) # Hide the InsecureRequestWarning from urllib3 @@ -58,7 +57,10 @@ def main(args=None): # Configure our deprecation warnings to be sent through loggers deprecation.install_warning_logger() - autocomplete() + if 'PIP_AUTO_COMPLETE' in os.environ: + from pip._internal.cli.autocompletion import autocomplete + autocomplete() + return 1 try: cmd_name, cmd_args = parse_command(args) diff --git a/src/pip/_internal/cli/autocompletion.py b/src/pip/_internal/cli/autocompletion.py index 0a04199e6dc..7131eacb4a1 100644 --- a/src/pip/_internal/cli/autocompletion.py +++ b/src/pip/_internal/cli/autocompletion.py @@ -98,7 +98,6 @@ def autocomplete(): subcommands = auto_complete_paths(current, completion_type) print(' '.join([x for x in subcommands if x.startswith(current)])) - sys.exit(1) def get_path_completion_type(cwords, cword, opts): diff --git a/src/pip/_internal/cli/base_command.py b/src/pip/_internal/cli/base_command.py index f6108c96eb5..5385e01de27 100644 --- a/src/pip/_internal/cli/base_command.py +++ b/src/pip/_internal/cli/base_command.py @@ -2,38 +2,7 @@ from __future__ import absolute_import, print_function import logging -import logging.config -import optparse -import os -import platform -import sys -import traceback - -from pip._internal.cli import cmdoptions -from pip._internal.cli.parser import ( - ConfigOptionParser, UpdatingDefaultsHelpFormatter, -) -from pip._internal.cli.status_codes import ( - ERROR, PREVIOUS_BUILD_DIR_ERROR, SUCCESS, UNKNOWN_ERROR, - VIRTUALENV_NOT_FOUND, -) -from pip._internal.download import PipSession -from pip._internal.exceptions import ( - BadCommand, CommandError, InstallationError, PreviousBuildDirError, - UninstallationError, -) -from pip._internal.index import PackageFinder -from pip._internal.locations import running_under_virtualenv -from pip._internal.req.constructors import ( - install_req_from_editable, install_req_from_line, -) -from pip._internal.req.req_file import parse_requirements -from pip._internal.utils.deprecation import deprecated -from pip._internal.utils.logging import BrokenStdoutLoggingError, setup_logging -from pip._internal.utils.misc import ( - get_prog, normalize_path, redact_password_from_url, -) -from pip._internal.utils.outdated import pip_version_check + from pip._internal.utils.typing import MYPY_CHECK_RUNNING if MYPY_CHECK_RUNNING: @@ -54,6 +23,15 @@ class Command(object): def __init__(self, isolated=False): # type: (bool) -> None + + import optparse + + from pip._internal.cli import cmdoptions + from pip._internal.cli.parser import ( + ConfigOptionParser, UpdatingDefaultsHelpFormatter, + ) + from pip._internal.utils.misc_fast_import import get_prog + parser_kw = { 'usage': self.usage, 'prog': '%s %s' % (get_prog(), self.name), @@ -83,6 +61,12 @@ def run(self, options, args): def _build_session(self, options, retries=None, timeout=None): # type: (Values, Optional[int], Optional[int]) -> PipSession + import os + + from pip._internal.download import PipSession + from pip._internal.utils.misc import ( + normalize_path, ) + session = PipSession( cache=( normalize_path(os.path.join(options.cache_dir, "http")) @@ -125,6 +109,25 @@ def parse_args(self, args): def main(self, args): # type: (List[str]) -> int + import logging.config + import os + import platform + import sys + import traceback + + from pip._internal.cli.status_codes import ( + ERROR, PREVIOUS_BUILD_DIR_ERROR, SUCCESS, UNKNOWN_ERROR, + VIRTUALENV_NOT_FOUND, + ) + from pip._internal.exceptions import ( + BadCommand, CommandError, InstallationError, PreviousBuildDirError, + UninstallationError, + ) + from pip._internal.locations import running_under_virtualenv + from pip._internal.utils.deprecation import deprecated + from pip._internal.utils.logging import BrokenStdoutLoggingError, setup_logging + from pip._internal.utils.outdated import pip_version_check + options, args = self.parse_args(args) # Set verbosity so that it can be used elsewhere. @@ -253,6 +256,15 @@ def populate_requirement_set(requirement_set, # type: RequirementSet # NOTE: As a side-effect, options.require_hashes and # requirement_set.require_hashes may be updated + from pip._internal.download import PipSession + from pip._internal.exceptions import ( + CommandError, ) + from pip._internal.index import PackageFinder + from pip._internal.req.constructors import ( + install_req_from_editable, install_req_from_line, + ) + from pip._internal.req.req_file import parse_requirements + for filename in options.constraints: for req_to_add in parse_requirements( filename, @@ -314,6 +326,13 @@ def _build_package_finder( implementation=None # type: Optional[str] ): # type: (...) -> PackageFinder + + from pip._internal.download import PipSession + from pip._internal.index import PackageFinder + from pip._internal.utils.misc import ( + redact_password_from_url, + ) + """ Create a package finder appropriate to this requirement command. """ diff --git a/src/pip/_internal/cli/main_parser.py b/src/pip/_internal/cli/main_parser.py index 767f35d50ee..11c24cac94d 100644 --- a/src/pip/_internal/cli/main_parser.py +++ b/src/pip/_internal/cli/main_parser.py @@ -4,18 +4,7 @@ import os import sys -from pip import __version__ -from pip._internal.cli import cmdoptions -from pip._internal.cli.parser import ( - ConfigOptionParser, UpdatingDefaultsHelpFormatter, -) -from pip._internal.commands import ( - commands_dict, get_similar_commands, get_summaries, -) -from pip._internal.exceptions import CommandError -from pip._internal.utils.misc import get_prog from pip._internal.utils.typing import MYPY_CHECK_RUNNING - if MYPY_CHECK_RUNNING: from typing import Tuple, List @@ -28,6 +17,16 @@ def create_main_parser(): """Creates and returns the main parser for pip's CLI """ + from pip import __version__ + from pip._internal.cli import cmdoptions + from pip._internal.cli.parser import ( + ConfigOptionParser, UpdatingDefaultsHelpFormatter, + ) + from pip._internal.commands import ( + get_summaries, + ) + from pip._internal.utils.misc_fast_import import get_prog + parser_kw = { 'usage': '\n%prog [options]', 'add_help_option': False, @@ -63,6 +62,11 @@ def create_main_parser(): def parse_command(args): # type: (List[str]) -> Tuple[str, List[str]] + from pip._internal.commands import ( + commands_dict, get_similar_commands, + ) + from pip._internal.exceptions import CommandError + parser = create_main_parser() # Note: parser calls disable_interspersed_args(), so the result of this diff --git a/src/pip/_internal/commands/check.py b/src/pip/_internal/commands/check.py index 801cecc0b84..1cf1b8527fa 100644 --- a/src/pip/_internal/commands/check.py +++ b/src/pip/_internal/commands/check.py @@ -1,9 +1,6 @@ import logging from pip._internal.cli.base_command import Command -from pip._internal.operations.check import ( - check_package_set, create_package_set_from_installed, -) logger = logging.getLogger(__name__) @@ -16,6 +13,9 @@ class CheckCommand(Command): summary = 'Verify installed packages have compatible dependencies.' def run(self, options, args): + from pip._internal.operations.check import ( + check_package_set, create_package_set_from_installed, + ) package_set, parsing_probs = create_package_set_from_installed() missing, conflicting = check_package_set(package_set) diff --git a/src/pip/_internal/commands/completion.py b/src/pip/_internal/commands/completion.py index 2fcdd393ed6..795cc2beb1f 100644 --- a/src/pip/_internal/commands/completion.py +++ b/src/pip/_internal/commands/completion.py @@ -4,7 +4,8 @@ import textwrap from pip._internal.cli.base_command import Command -from pip._internal.utils.misc import get_prog +from pip._internal.utils.misc_fast_import import get_prog + BASE_COMPLETION = """ # pip %(shell)s completion start%(script)s# pip %(shell)s completion end diff --git a/src/pip/_internal/commands/configuration.py b/src/pip/_internal/commands/configuration.py index 950e2057368..eb8315c7c3f 100644 --- a/src/pip/_internal/commands/configuration.py +++ b/src/pip/_internal/commands/configuration.py @@ -3,12 +3,7 @@ import subprocess from pip._internal.cli.base_command import Command -from pip._internal.cli.status_codes import ERROR, SUCCESS -from pip._internal.configuration import Configuration, kinds -from pip._internal.exceptions import PipError -from pip._internal.locations import running_under_virtualenv, site_config_file -from pip._internal.utils.deprecation import deprecated -from pip._internal.utils.misc import get_prog +from pip._internal.utils.misc_fast_import import get_prog logger = logging.getLogger(__name__) @@ -96,6 +91,10 @@ def __init__(self, *args, **kwargs): self.parser.insert_option_group(0, self.cmd_opts) def run(self, options, args): + from pip._internal.cli.status_codes import ERROR, SUCCESS + from pip._internal.configuration import Configuration + from pip._internal.exceptions import PipError + handlers = { "list": self.list_values, "edit": self.open_in_editor, @@ -139,6 +138,11 @@ def run(self, options, args): return SUCCESS def _determine_file(self, options, need_value): + from pip._internal.configuration import kinds + from pip._internal.exceptions import PipError + from pip._internal.locations import running_under_virtualenv, site_config_file + from pip._internal.utils.deprecation import deprecated + # Convert legacy venv_file option to site_file or error if options.venv_file and not options.site_file: if running_under_virtualenv(): @@ -201,6 +205,8 @@ def unset_name(self, options, args): self._save_configuration() def open_in_editor(self, options, args): + from pip._internal.exceptions import PipError + editor = self._determine_editor(options) fname = self.configuration.get_file_to_edit() @@ -218,6 +224,7 @@ def open_in_editor(self, options, args): def _get_n_args(self, args, example, n): """Helper to make sure the command got the right number of arguments """ + from pip._internal.exceptions import PipError if len(args) != n: msg = ( 'Got unexpected number of arguments, expected {}. ' @@ -231,6 +238,7 @@ def _get_n_args(self, args, example, n): return args def _save_configuration(self): + from pip._internal.exceptions import PipError # We successfully ran a modifying command. Need to save the # configuration. try: @@ -243,6 +251,7 @@ def _save_configuration(self): raise PipError("Internal Error.") def _determine_editor(self, options): + from pip._internal.exceptions import PipError if options.editor is not None: return options.editor elif "VISUAL" in os.environ: diff --git a/src/pip/_internal/commands/download.py b/src/pip/_internal/commands/download.py index a57e4bc4cb8..99fa537842d 100644 --- a/src/pip/_internal/commands/download.py +++ b/src/pip/_internal/commands/download.py @@ -3,15 +3,7 @@ import logging import os -from pip._internal.cli import cmdoptions from pip._internal.cli.base_command import RequirementCommand -from pip._internal.operations.prepare import RequirementPreparer -from pip._internal.req import RequirementSet -from pip._internal.req.req_tracker import RequirementTracker -from pip._internal.resolve import Resolver -from pip._internal.utils.filesystem import check_path_owner -from pip._internal.utils.misc import ensure_dir, normalize_path -from pip._internal.utils.temp_dir import TempDirectory logger = logging.getLogger(__name__) @@ -42,6 +34,8 @@ class DownloadCommand(RequirementCommand): def __init__(self, *args, **kw): super(DownloadCommand, self).__init__(*args, **kw) + from pip._internal.cli import cmdoptions + cmd_opts = self.cmd_opts cmd_opts.add_option(cmdoptions.constraints()) @@ -83,6 +77,15 @@ def __init__(self, *args, **kw): self.parser.insert_option_group(0, cmd_opts) def run(self, options, args): + from pip._internal.cli import cmdoptions + from pip._internal.operations.prepare import RequirementPreparer + from pip._internal.req import RequirementSet + from pip._internal.req.req_tracker import RequirementTracker + from pip._internal.resolve import Resolver + from pip._internal.utils.filesystem import check_path_owner + from pip._internal.utils.misc import ensure_dir, normalize_path + from pip._internal.utils.temp_dir import TempDirectory + options.ignore_installed = True # editable doesn't really make sense for `pip download`, but the bowels # of the RequirementSet code require that property. diff --git a/src/pip/_internal/commands/freeze.py b/src/pip/_internal/commands/freeze.py index dc9c53a6b5d..5befc584e51 100644 --- a/src/pip/_internal/commands/freeze.py +++ b/src/pip/_internal/commands/freeze.py @@ -2,11 +2,7 @@ import sys -from pip._internal.cache import WheelCache from pip._internal.cli.base_command import Command -from pip._internal.models.format_control import FormatControl -from pip._internal.operations.freeze import freeze -from pip._internal.utils.compat import stdlib_pkgs DEV_PKGS = {'pip', 'setuptools', 'distribute', 'wheel'} @@ -71,6 +67,11 @@ def __init__(self, *args, **kw): self.parser.insert_option_group(0, self.cmd_opts) def run(self, options, args): + from pip._internal.cache import WheelCache + from pip._internal.models.format_control import FormatControl + from pip._internal.operations.freeze import freeze + from pip._internal.utils.compat import stdlib_pkgs + format_control = FormatControl(set(), set()) wheel_cache = WheelCache(options.cache_dir, format_control) skip = set(stdlib_pkgs) diff --git a/src/pip/_internal/commands/hash.py b/src/pip/_internal/commands/hash.py index 423440e9c29..c30fe865971 100644 --- a/src/pip/_internal/commands/hash.py +++ b/src/pip/_internal/commands/hash.py @@ -5,9 +5,6 @@ import sys from pip._internal.cli.base_command import Command -from pip._internal.cli.status_codes import ERROR -from pip._internal.utils.hashes import FAVORITE_HASH, STRONG_HASHES -from pip._internal.utils.misc import read_chunks logger = logging.getLogger(__name__) @@ -27,6 +24,8 @@ class HashCommand(Command): def __init__(self, *args, **kw): super(HashCommand, self).__init__(*args, **kw) + + from pip._internal.utils.hashes import FAVORITE_HASH, STRONG_HASHES self.cmd_opts.add_option( '-a', '--algorithm', dest='algorithm', @@ -38,6 +37,8 @@ def __init__(self, *args, **kw): self.parser.insert_option_group(0, self.cmd_opts) def run(self, options, args): + from pip._internal.cli.status_codes import ERROR + if not args: self.parser.print_usage(sys.stderr) return ERROR @@ -50,6 +51,8 @@ def run(self, options, args): def _hash_of_file(path, algorithm): """Return the hash digest of a file.""" + from pip._internal.utils.misc import read_chunks + with open(path, 'rb') as archive: hash = hashlib.new(algorithm) for chunk in read_chunks(archive): diff --git a/src/pip/_internal/commands/help.py b/src/pip/_internal/commands/help.py index 49a81cbb074..05a8a6838f2 100644 --- a/src/pip/_internal/commands/help.py +++ b/src/pip/_internal/commands/help.py @@ -1,8 +1,6 @@ from __future__ import absolute_import from pip._internal.cli.base_command import Command -from pip._internal.cli.status_codes import SUCCESS -from pip._internal.exceptions import CommandError class HelpCommand(Command): @@ -15,6 +13,8 @@ class HelpCommand(Command): def run(self, options, args): from pip._internal.commands import commands_dict, get_similar_commands + from pip._internal.cli.status_codes import SUCCESS + from pip._internal.exceptions import CommandError try: # 'pip help' with no args is handled by pip.__init__.parseopt() diff --git a/src/pip/_internal/commands/install.py b/src/pip/_internal/commands/install.py index c13da44c6c3..124394d9480 100644 --- a/src/pip/_internal/commands/install.py +++ b/src/pip/_internal/commands/install.py @@ -1,34 +1,8 @@ from __future__ import absolute_import -import errno import logging -import operator -import os -import shutil -from optparse import SUPPRESS_HELP -from pip._vendor import pkg_resources - -from pip._internal.cache import WheelCache -from pip._internal.cli import cmdoptions from pip._internal.cli.base_command import RequirementCommand -from pip._internal.cli.status_codes import ERROR -from pip._internal.exceptions import ( - CommandError, InstallationError, PreviousBuildDirError, -) -from pip._internal.locations import distutils_scheme, virtualenv_no_global -from pip._internal.operations.check import check_install_conflicts -from pip._internal.operations.prepare import RequirementPreparer -from pip._internal.req import RequirementSet, install_given_reqs -from pip._internal.req.req_tracker import RequirementTracker -from pip._internal.resolve import Resolver -from pip._internal.utils.filesystem import check_path_owner -from pip._internal.utils.misc import ( - ensure_dir, get_installed_version, - protect_pip_from_modification_on_windows, -) -from pip._internal.utils.temp_dir import TempDirectory -from pip._internal.wheel import WheelBuilder logger = logging.getLogger(__name__) @@ -96,6 +70,10 @@ class InstallCommand(RequirementCommand): def __init__(self, *args, **kw): super(InstallCommand, self).__init__(*args, **kw) + from optparse import SUPPRESS_HELP + + from pip._internal.cli import cmdoptions + cmd_opts = self.cmd_opts cmd_opts.add_option(cmdoptions.requirements()) @@ -241,6 +219,30 @@ def __init__(self, *args, **kw): self.parser.insert_option_group(0, cmd_opts) def run(self, options, args): + import operator + import os + + from pip._vendor import pkg_resources + + from pip._internal.cache import WheelCache + from pip._internal.cli import cmdoptions + from pip._internal.cli.status_codes import ERROR + from pip._internal.exceptions import ( + CommandError, InstallationError, PreviousBuildDirError, + ) + from pip._internal.locations import virtualenv_no_global + from pip._internal.operations.prepare import RequirementPreparer + from pip._internal.req import RequirementSet, install_given_reqs + from pip._internal.req.req_tracker import RequirementTracker + from pip._internal.resolve import Resolver + from pip._internal.utils.filesystem import check_path_owner + from pip._internal.utils.misc import ( + get_installed_version, + protect_pip_from_modification_on_windows, + ) + from pip._internal.utils.temp_dir import TempDirectory + from pip._internal.wheel import WheelBuilder + cmdoptions.check_install_build_global(options) upgrade_strategy = "to-satisfy-only" if options.upgrade: @@ -464,6 +466,13 @@ def run(self, options, args): return requirement_set def _handle_target_dir(self, target_dir, target_temp_dir, upgrade): + import os + import shutil + + from pip._internal.locations import distutils_scheme + from pip._internal.utils.misc import ( + ensure_dir, ) + ensure_dir(target_dir) # Checking both purelib and platlib directories for installed @@ -520,6 +529,9 @@ def _handle_target_dir(self, target_dir, target_temp_dir, upgrade): ) def _warn_about_conflicts(self, to_install): + + from pip._internal.operations.check import check_install_conflicts + try: package_set, _dep_info = check_install_conflicts(to_install) except Exception: @@ -547,6 +559,8 @@ def _warn_about_conflicts(self, to_install): def get_lib_location_guesses(*args, **kwargs): + from pip._internal.locations import distutils_scheme + scheme = distutils_scheme('', *args, **kwargs) return [scheme['purelib'], scheme['platlib']] @@ -556,6 +570,8 @@ def create_env_error_message(error, show_traceback, using_user_site): It may occur anytime during the execution of the install command. """ + import errno + parts = [] # Mention the error if we are not going to show a traceback diff --git a/src/pip/_internal/commands/list.py b/src/pip/_internal/commands/list.py index a6402749207..63af2460196 100644 --- a/src/pip/_internal/commands/list.py +++ b/src/pip/_internal/commands/list.py @@ -3,17 +3,7 @@ import json import logging -from pip._vendor import six -from pip._vendor.six.moves import zip_longest - -from pip._internal.cli import cmdoptions from pip._internal.cli.base_command import Command -from pip._internal.exceptions import CommandError -from pip._internal.index import PackageFinder -from pip._internal.utils.misc import ( - dist_is_editable, get_installed_distributions, -) -from pip._internal.utils.packaging import get_installer logger = logging.getLogger(__name__) @@ -32,6 +22,8 @@ class ListCommand(Command): def __init__(self, *args, **kw): super(ListCommand, self).__init__(*args, **kw) + from pip._internal.cli import cmdoptions + cmd_opts = self.cmd_opts cmd_opts.add_option( @@ -113,6 +105,8 @@ def _build_package_finder(self, options, index_urls, session): """ Create a package finder appropriate to this list command. """ + + from pip._internal.index import PackageFinder return PackageFinder( find_links=options.find_links, index_urls=index_urls, @@ -122,6 +116,12 @@ def _build_package_finder(self, options, index_urls, session): ) def run(self, options, args): + + from pip._internal.exceptions import CommandError + from pip._internal.utils.misc import ( + get_installed_distributions, + ) + if options.outdated and options.uptodate: raise CommandError( "Options --outdated and --uptodate cannot be combined.") @@ -230,6 +230,8 @@ def output_package_listing_columns(self, data, header): def tabulate(vals): + from pip._vendor.six.moves import zip_longest + # From pfmoore on GitHub: # https://github.com/pypa/pip/issues/3651#issuecomment-216932564 assert len(vals) > 0 @@ -252,6 +254,11 @@ def format_for_columns(pkgs, options): Convert the package data into something usable by output_package_listing_columns. """ + + from pip._internal.utils.misc import ( + dist_is_editable, ) + from pip._internal.utils.packaging import get_installer + running_outdated = options.outdated # Adjust the header for the `pip list --outdated` case. if running_outdated: @@ -285,6 +292,10 @@ def format_for_columns(pkgs, options): def format_for_json(packages, options): + from pip._vendor import six + + from pip._internal.utils.packaging import get_installer + data = [] for dist in packages: info = { diff --git a/src/pip/_internal/commands/search.py b/src/pip/_internal/commands/search.py index c157a3128a4..7ebc6dbf010 100644 --- a/src/pip/_internal/commands/search.py +++ b/src/pip/_internal/commands/search.py @@ -2,22 +2,12 @@ import logging import sys -import textwrap -from collections import OrderedDict - -from pip._vendor import pkg_resources -from pip._vendor.packaging.version import parse as parse_version -# NOTE: XMLRPC Client is not annotated in typeshed as on 2017-07-17, which is -# why we ignore the type on this import -from pip._vendor.six.moves import xmlrpc_client # type: ignore from pip._internal.cli.base_command import Command -from pip._internal.cli.status_codes import NO_MATCHES_FOUND, SUCCESS -from pip._internal.download import PipXmlrpcTransport -from pip._internal.exceptions import CommandError from pip._internal.models.index import PyPI -from pip._internal.utils.compat import get_terminal_size -from pip._internal.utils.logging import indent_log + +# NOTE: XMLRPC Client is not annotated in typeshed as on 2017-07-17, which is +# why we ignore the type on this import logger = logging.getLogger(__name__) @@ -42,6 +32,12 @@ def __init__(self, *args, **kw): self.parser.insert_option_group(0, self.cmd_opts) def run(self, options, args): + # NOTE: XMLRPC Client is not annotated in typeshed as on 2017-07-17, which is + # why we ignore the type on this import + from pip._internal.cli.status_codes import NO_MATCHES_FOUND, SUCCESS + from pip._internal.exceptions import CommandError + from pip._internal.utils.compat import get_terminal_size + if not args: raise CommandError('Missing required argument (search query).') query = args @@ -58,6 +54,8 @@ def run(self, options, args): return NO_MATCHES_FOUND def search(self, query, options): + from pip._internal.download import PipXmlrpcTransport + from pip._vendor.six.moves import xmlrpc_client # type: ignore index_url = options.index with self._build_session(options) as session: transport = PipXmlrpcTransport(index_url, session) @@ -72,6 +70,7 @@ def transform_hits(hits): packages with the list of versions stored inline. This converts the list from pypi into one we can use. """ + from collections import OrderedDict packages = OrderedDict() for hit in hits: name = hit['name'] @@ -97,6 +96,13 @@ def transform_hits(hits): def print_results(hits, name_column_width=None, terminal_width=None): if not hits: return + + import textwrap + from pip._vendor import pkg_resources + # NOTE: XMLRPC Client is not annotated in typeshed as on 2017-07-17, which is + # why we ignore the type on this import + from pip._internal.utils.logging import indent_log + if name_column_width is None: name_column_width = max([ len(hit['name']) + len(highest_version(hit.get('versions', ['-']))) @@ -132,4 +138,7 @@ def print_results(hits, name_column_width=None, terminal_width=None): def highest_version(versions): + from pip._vendor.packaging.version import parse as parse_version + # NOTE: XMLRPC Client is not annotated in typeshed as on 2017-07-17, which is + # why we ignore the type on this import return max(versions, key=parse_version) diff --git a/src/pip/_internal/commands/show.py b/src/pip/_internal/commands/show.py index a18a9020c41..35e14f4301d 100644 --- a/src/pip/_internal/commands/show.py +++ b/src/pip/_internal/commands/show.py @@ -1,14 +1,8 @@ from __future__ import absolute_import import logging -import os -from email.parser import FeedParser - -from pip._vendor import pkg_resources -from pip._vendor.packaging.utils import canonicalize_name from pip._internal.cli.base_command import Command -from pip._internal.cli.status_codes import ERROR, SUCCESS logger = logging.getLogger(__name__) @@ -37,6 +31,9 @@ def __init__(self, *args, **kw): self.parser.insert_option_group(0, self.cmd_opts) def run(self, options, args): + + from pip._internal.cli.status_codes import ERROR, SUCCESS + if not args: logger.warning('ERROR: Please provide a package name or names.') return ERROR @@ -56,6 +53,12 @@ def search_packages_info(query): pip generated 'installed-files.txt' in the distributions '.egg-info' directory. """ + import os + from email.parser import FeedParser + + from pip._vendor import pkg_resources + from pip._vendor.packaging.utils import canonicalize_name + installed = {} for p in pkg_resources.working_set: installed[canonicalize_name(p.project_name)] = p @@ -126,6 +129,9 @@ def print_results(distributions, list_files=False, verbose=False): """ Print the informations from installed distributions found. """ + + from pip._vendor import pkg_resources + results_printed = False for i, dist in enumerate(distributions): results_printed = True diff --git a/src/pip/_internal/commands/uninstall.py b/src/pip/_internal/commands/uninstall.py index 0cd6f54bd86..fb6bca20932 100644 --- a/src/pip/_internal/commands/uninstall.py +++ b/src/pip/_internal/commands/uninstall.py @@ -1,12 +1,6 @@ from __future__ import absolute_import -from pip._vendor.packaging.utils import canonicalize_name - from pip._internal.cli.base_command import Command -from pip._internal.exceptions import InstallationError -from pip._internal.req import parse_requirements -from pip._internal.req.constructors import install_req_from_line -from pip._internal.utils.misc import protect_pip_from_modification_on_windows class UninstallCommand(Command): @@ -45,6 +39,13 @@ def __init__(self, *args, **kw): self.parser.insert_option_group(0, self.cmd_opts) def run(self, options, args): + from pip._vendor.packaging.utils import canonicalize_name + + from pip._internal.exceptions import InstallationError + from pip._internal.req import parse_requirements + from pip._internal.req.constructors import install_req_from_line + from pip._internal.utils.misc import protect_pip_from_modification_on_windows + with self._build_session(options) as session: reqs_to_uninstall = {} for name in args: diff --git a/src/pip/_internal/commands/wheel.py b/src/pip/_internal/commands/wheel.py index cd72a3df1a7..576f8c5edb2 100644 --- a/src/pip/_internal/commands/wheel.py +++ b/src/pip/_internal/commands/wheel.py @@ -2,18 +2,8 @@ from __future__ import absolute_import import logging -import os -from pip._internal.cache import WheelCache -from pip._internal.cli import cmdoptions from pip._internal.cli.base_command import RequirementCommand -from pip._internal.exceptions import CommandError, PreviousBuildDirError -from pip._internal.operations.prepare import RequirementPreparer -from pip._internal.req import RequirementSet -from pip._internal.req.req_tracker import RequirementTracker -from pip._internal.resolve import Resolver -from pip._internal.utils.temp_dir import TempDirectory -from pip._internal.wheel import WheelBuilder logger = logging.getLogger(__name__) @@ -46,6 +36,10 @@ class WheelCommand(RequirementCommand): def __init__(self, *args, **kw): super(WheelCommand, self).__init__(*args, **kw) + import os + + from pip._internal.cli import cmdoptions + cmd_opts = self.cmd_opts cmd_opts.add_option( @@ -106,6 +100,18 @@ def __init__(self, *args, **kw): self.parser.insert_option_group(0, cmd_opts) def run(self, options, args): + import os + + from pip._internal.cache import WheelCache + from pip._internal.cli import cmdoptions + from pip._internal.exceptions import CommandError, PreviousBuildDirError + from pip._internal.operations.prepare import RequirementPreparer + from pip._internal.req import RequirementSet + from pip._internal.req.req_tracker import RequirementTracker + from pip._internal.resolve import Resolver + from pip._internal.utils.temp_dir import TempDirectory + from pip._internal.wheel import WheelBuilder + cmdoptions.check_install_build_global(options) index_urls = [options.index_url] + options.extra_index_urls diff --git a/src/pip/_internal/exceptions.py b/src/pip/_internal/exceptions.py index 7b291a1e4e4..08d8cfb630b 100644 --- a/src/pip/_internal/exceptions.py +++ b/src/pip/_internal/exceptions.py @@ -3,8 +3,6 @@ from itertools import chain, groupby, repeat -from pip._vendor.six import iteritems - from pip._internal.utils.typing import MYPY_CHECK_RUNNING if MYPY_CHECK_RUNNING: @@ -240,6 +238,7 @@ def hash_then_or(hash_name): return chain([hash_name], repeat(' or')) lines = [] + from pip._vendor.six import iteritems for hash_name, expecteds in iteritems(self.allowed): prefix = hash_then_or(hash_name) lines.extend((' Expected %s %s' % (next(prefix), e)) diff --git a/src/pip/_internal/utils/deprecation.py b/src/pip/_internal/utils/deprecation.py index 8c896f8cee5..627124056e0 100644 --- a/src/pip/_internal/utils/deprecation.py +++ b/src/pip/_internal/utils/deprecation.py @@ -6,8 +6,6 @@ import logging import warnings -from pip._vendor.packaging.version import parse - from pip import __version__ as current_version from pip._internal.utils.typing import MYPY_CHECK_RUNNING @@ -87,6 +85,8 @@ def deprecated(reason, replacement, gone_in, issue=None): url = "https://github.com/pypa/pip/issues/" + str(issue) message += " You can find discussion regarding this at {}.".format(url) + from pip._vendor.packaging.version import parse + # Raise as an error if it has to be removed. if gone_in is not None and parse(current_version) >= parse(gone_in): raise PipDeprecationWarning(message) diff --git a/src/pip/_internal/utils/misc.py b/src/pip/_internal/utils/misc.py index 42f324bc412..19b921975da 100644 --- a/src/pip/_internal/utils/misc.py +++ b/src/pip/_internal/utils/misc.py @@ -57,8 +57,7 @@ 'is_svn_page', 'file_contents', 'split_leading_dir', 'has_leading_dir', 'normalize_path', - 'renames', 'get_prog', - 'unzip_file', 'untar_file', 'unpack_file', 'call_subprocess', + 'renames', 'unzip_file', 'untar_file', 'unpack_file', 'call_subprocess', 'captured_stdout', 'ensure_dir', 'ARCHIVE_EXTENSIONS', 'SUPPORTED_EXTENSIONS', 'WHEEL_EXTENSION', 'get_installed_version', 'remove_auth_from_url'] @@ -101,19 +100,6 @@ def ensure_dir(path): raise -def get_prog(): - # type: () -> str - try: - prog = os.path.basename(sys.argv[0]) - if prog in ('__main__.py', '-c'): - return "%s -m pip" % sys.executable - else: - return prog - except (AttributeError, TypeError, IndexError): - pass - return 'pip' - - # Retry every half second for up to 3 seconds @retry(stop_max_delay=3000, wait_fixed=500) def rmtree(dir, ignore_errors=False): diff --git a/src/pip/_internal/utils/misc_fast_import.py b/src/pip/_internal/utils/misc_fast_import.py new file mode 100644 index 00000000000..d6b67241eea --- /dev/null +++ b/src/pip/_internal/utils/misc_fast_import.py @@ -0,0 +1,15 @@ +import os +import sys + + +def get_prog(): + # type: () -> str + try: + prog = os.path.basename(sys.argv[0]) + if prog in ('__main__.py', '-c'): + return "%s -m pip" % sys.executable + else: + return prog + except (AttributeError, TypeError, IndexError): + pass + return 'pip' diff --git a/tests/functional/test_help.py b/tests/functional/test_help.py index 9e3dbcde8c9..9ac4bd0bc16 100644 --- a/tests/functional/test_help.py +++ b/tests/functional/test_help.py @@ -1,7 +1,7 @@ import pytest from mock import Mock -from pip._internal.cli.base_command import ERROR, SUCCESS +from pip._internal.cli.status_codes import ERROR, SUCCESS from pip._internal.commands import commands_dict as commands from pip._internal.commands.help import HelpCommand from pip._internal.exceptions import CommandError diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index f1abddd988e..c371109c1ca 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -27,10 +27,11 @@ from pip._internal.utils.hashes import Hashes, MissingHashes from pip._internal.utils.misc import ( call_subprocess, egg_link_path, ensure_dir, format_command_args, - get_installed_distributions, get_prog, normalize_path, redact_netloc, + get_installed_distributions, normalize_path, redact_netloc, redact_password_from_url, remove_auth_from_url, rmtree, split_auth_from_netloc, untar_file, unzip_file, ) +from pip._internal.utils.misc_fast_import import get_prog from pip._internal.utils.packaging import check_dist_requires_python from pip._internal.utils.temp_dir import AdjacentTempDirectory, TempDirectory from pip._internal.utils.ui import SpinnerInterface