Skip to content

First stab at reducing startup time #6346

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions src/pip/_internal/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down
1 change: 0 additions & 1 deletion src/pip/_internal/cli/autocompletion.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
83 changes: 51 additions & 32 deletions src/pip/_internal/cli/base_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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),
Expand Down Expand Up @@ -97,6 +75,12 @@ def _get_index_urls(cls, options):

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"))
Expand Down Expand Up @@ -140,6 +124,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.
Expand Down Expand Up @@ -268,6 +271,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,
Expand Down Expand Up @@ -329,6 +341,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.
"""
Expand Down
26 changes: 15 additions & 11 deletions src/pip/_internal/cli/main_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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 <command> [options]',
'add_help_option': False,
Expand Down Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions src/pip/_internal/commands/check.py
Original file line number Diff line number Diff line change
@@ -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__)

Expand All @@ -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)

Expand Down
3 changes: 2 additions & 1 deletion src/pip/_internal/commands/completion.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 15 additions & 6 deletions src/pip/_internal/commands/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__)

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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():
Expand Down Expand Up @@ -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()
Expand All @@ -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 {}. '
Expand All @@ -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:
Expand All @@ -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:
Expand Down
19 changes: 11 additions & 8 deletions src/pip/_internal/commands/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__)

Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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.
Expand Down
9 changes: 5 additions & 4 deletions src/pip/_internal/commands/freeze.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'}

Expand Down Expand Up @@ -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)
Expand Down
Loading