2009-11-20 09:47:28 +01:00
|
|
|
"""Base Command class, and related routines"""
|
2018-10-23 10:55:07 +02:00
|
|
|
from __future__ import absolute_import, print_function
|
2009-11-20 09:47:28 +01:00
|
|
|
|
2014-08-31 01:52:28 +02:00
|
|
|
import logging
|
2017-03-18 18:22:44 +01:00
|
|
|
import logging.config
|
2017-05-16 12:16:30 +02:00
|
|
|
import optparse
|
2009-11-20 09:47:28 +01:00
|
|
|
import os
|
2019-01-27 22:05:35 +01:00
|
|
|
import platform
|
2010-08-30 23:53:33 +02:00
|
|
|
import sys
|
2018-10-23 10:55:07 +02:00
|
|
|
import traceback
|
2010-07-26 04:54:16 +02:00
|
|
|
|
2018-07-23 09:31:05 +02:00
|
|
|
from pip._internal.cli import cmdoptions
|
2019-06-21 18:10:03 +02:00
|
|
|
from pip._internal.cli.cmdoptions import make_search_scope
|
2018-07-30 06:05:08 +02:00
|
|
|
from pip._internal.cli.parser import (
|
2017-11-21 08:50:32 +01:00
|
|
|
ConfigOptionParser, UpdatingDefaultsHelpFormatter,
|
2017-08-31 17:48:18 +02:00
|
|
|
)
|
2018-07-30 06:10:59 +02:00
|
|
|
from pip._internal.cli.status_codes import (
|
|
|
|
ERROR, PREVIOUS_BUILD_DIR_ERROR, SUCCESS, UNKNOWN_ERROR,
|
|
|
|
VIRTUALENV_NOT_FOUND,
|
|
|
|
)
|
2017-08-31 17:48:18 +02:00
|
|
|
from pip._internal.download import PipSession
|
|
|
|
from pip._internal.exceptions import (
|
2017-05-16 12:16:30 +02:00
|
|
|
BadCommand, CommandError, InstallationError, PreviousBuildDirError,
|
2017-11-21 08:50:32 +01:00
|
|
|
UninstallationError,
|
2017-05-16 12:16:30 +02:00
|
|
|
)
|
2017-08-31 17:48:18 +02:00
|
|
|
from pip._internal.index import PackageFinder
|
2019-06-28 20:14:55 +02:00
|
|
|
from pip._internal.models.selection_prefs import SelectionPreferences
|
2019-06-09 22:11:16 +02:00
|
|
|
from pip._internal.models.target_python import TargetPython
|
2018-08-21 17:07:40 +02:00
|
|
|
from pip._internal.req.constructors import (
|
|
|
|
install_req_from_editable, install_req_from_line,
|
|
|
|
)
|
2017-09-02 13:22:00 +02:00
|
|
|
from pip._internal.req.req_file import parse_requirements
|
2019-01-11 11:14:25 +01:00
|
|
|
from pip._internal.utils.deprecation import deprecated
|
2018-10-23 10:55:07 +02:00
|
|
|
from pip._internal.utils.logging import BrokenStdoutLoggingError, setup_logging
|
2019-06-21 18:10:03 +02:00
|
|
|
from pip._internal.utils.misc import get_prog, normalize_path
|
2017-08-31 17:48:18 +02:00
|
|
|
from pip._internal.utils.outdated import pip_version_check
|
2017-09-02 12:36:48 +02:00
|
|
|
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
|
2019-07-18 22:39:34 +02:00
|
|
|
from pip._internal.utils.virtualenv import running_under_virtualenv
|
2011-12-04 21:50:34 +01:00
|
|
|
|
2017-06-16 09:32:31 +02:00
|
|
|
if MYPY_CHECK_RUNNING:
|
2019-02-22 12:17:07 +01:00
|
|
|
from typing import Optional, List, Tuple, Any
|
|
|
|
from optparse import Values
|
|
|
|
from pip._internal.cache import WheelCache
|
|
|
|
from pip._internal.req.req_set import RequirementSet
|
2009-11-20 09:47:28 +01:00
|
|
|
|
2017-06-15 21:51:33 +02:00
|
|
|
__all__ = ['Command']
|
2009-11-20 09:47:28 +01:00
|
|
|
|
2014-08-31 01:52:28 +02:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
2009-11-20 09:47:28 +01:00
|
|
|
class Command(object):
|
2017-06-15 21:51:33 +02:00
|
|
|
name = None # type: Optional[str]
|
|
|
|
usage = None # type: Optional[str]
|
|
|
|
ignore_require_venv = False # type: bool
|
2010-06-03 04:25:26 +02:00
|
|
|
|
2014-11-24 03:07:53 +01:00
|
|
|
def __init__(self, isolated=False):
|
2018-10-26 17:07:27 +02:00
|
|
|
# type: (bool) -> None
|
2012-11-10 05:15:45 +01:00
|
|
|
parser_kw = {
|
2013-02-16 19:00:29 +01:00
|
|
|
'usage': self.usage,
|
|
|
|
'prog': '%s %s' % (get_prog(), self.name),
|
|
|
|
'formatter': UpdatingDefaultsHelpFormatter(),
|
|
|
|
'add_help_option': False,
|
|
|
|
'name': self.name,
|
|
|
|
'description': self.__doc__,
|
2014-11-24 03:07:53 +01:00
|
|
|
'isolated': isolated,
|
2012-11-10 05:15:45 +01:00
|
|
|
}
|
2013-09-17 07:21:15 +02:00
|
|
|
|
2012-11-10 05:15:45 +01:00
|
|
|
self.parser = ConfigOptionParser(**parser_kw)
|
|
|
|
|
2012-12-10 02:22:28 +01:00
|
|
|
# Commands should add options to this option group
|
2012-12-10 13:34:29 +01:00
|
|
|
optgroup_name = '%s Options' % self.name.capitalize()
|
|
|
|
self.cmd_opts = optparse.OptionGroup(self.parser, optgroup_name)
|
2012-12-10 02:22:28 +01:00
|
|
|
|
2013-09-17 07:21:15 +02:00
|
|
|
# Add the general options
|
2014-01-27 15:07:10 +01:00
|
|
|
gen_opts = cmdoptions.make_option_group(
|
|
|
|
cmdoptions.general_group,
|
|
|
|
self.parser,
|
|
|
|
)
|
2013-09-17 07:21:15 +02:00
|
|
|
self.parser.add_option_group(gen_opts)
|
2012-11-10 05:15:45 +01:00
|
|
|
|
2018-10-26 17:07:27 +02:00
|
|
|
def run(self, options, args):
|
|
|
|
# type: (Values, List[Any]) -> Any
|
|
|
|
raise NotImplementedError
|
|
|
|
|
2019-02-12 17:52:49 +01:00
|
|
|
@classmethod
|
|
|
|
def _get_index_urls(cls, options):
|
|
|
|
"""Return a list of index urls from user-provided options."""
|
|
|
|
index_urls = []
|
2019-05-07 23:01:41 +02:00
|
|
|
if not getattr(options, "no_index", False):
|
|
|
|
url = getattr(options, "index_url", None)
|
|
|
|
if url:
|
|
|
|
index_urls.append(url)
|
2019-02-12 17:52:49 +01:00
|
|
|
urls = getattr(options, "extra_index_urls", None)
|
|
|
|
if urls:
|
|
|
|
index_urls.extend(urls)
|
|
|
|
# Return None rather than an empty list
|
|
|
|
return index_urls or None
|
|
|
|
|
2014-12-24 01:10:53 +01:00
|
|
|
def _build_session(self, options, retries=None, timeout=None):
|
2018-10-26 17:07:27 +02:00
|
|
|
# type: (Values, Optional[int], Optional[int]) -> PipSession
|
2014-04-24 13:29:57 +02:00
|
|
|
session = PipSession(
|
2014-08-31 22:01:01 +02:00
|
|
|
cache=(
|
|
|
|
normalize_path(os.path.join(options.cache_dir, "http"))
|
|
|
|
if options.cache_dir else None
|
|
|
|
),
|
2014-12-24 01:06:56 +01:00
|
|
|
retries=retries if retries is not None else options.retries,
|
2014-12-20 23:27:11 +01:00
|
|
|
insecure_hosts=options.trusted_hosts,
|
2019-02-12 17:52:49 +01:00
|
|
|
index_urls=self._get_index_urls(options),
|
2014-04-24 13:29:57 +02:00
|
|
|
)
|
2013-08-16 14:04:27 +02:00
|
|
|
|
|
|
|
# Handle custom ca-bundles from the user
|
|
|
|
if options.cert:
|
|
|
|
session.verify = options.cert
|
|
|
|
|
2014-03-07 13:41:15 +01:00
|
|
|
# Handle SSL client certificate
|
|
|
|
if options.client_cert:
|
|
|
|
session.cert = options.client_cert
|
|
|
|
|
2013-08-16 14:04:27 +02:00
|
|
|
# Handle timeouts
|
2014-12-24 01:10:53 +01:00
|
|
|
if options.timeout or timeout:
|
|
|
|
session.timeout = (
|
|
|
|
timeout if timeout is not None else options.timeout
|
|
|
|
)
|
2013-08-16 14:04:27 +02:00
|
|
|
|
|
|
|
# Handle configured proxies
|
|
|
|
if options.proxy:
|
|
|
|
session.proxies = {
|
|
|
|
"http": options.proxy,
|
|
|
|
"https": options.proxy,
|
|
|
|
}
|
|
|
|
|
|
|
|
# Determine if we can prompt the user for authentication or not
|
|
|
|
session.auth.prompting = not options.no_input
|
|
|
|
|
|
|
|
return session
|
2009-11-20 09:47:28 +01:00
|
|
|
|
2013-09-17 07:21:15 +02:00
|
|
|
def parse_args(self, args):
|
2018-10-26 17:07:27 +02:00
|
|
|
# type: (List[str]) -> Tuple
|
2013-09-17 07:21:15 +02:00
|
|
|
# factored out for testability
|
|
|
|
return self.parser.parse_args(args)
|
|
|
|
|
2013-09-10 05:56:46 +02:00
|
|
|
def main(self, args):
|
2018-10-26 17:07:27 +02:00
|
|
|
# type: (List[str]) -> int
|
2013-09-17 07:21:15 +02:00
|
|
|
options, args = self.parse_args(args)
|
2009-11-20 09:47:28 +01:00
|
|
|
|
2018-01-20 11:59:22 +01:00
|
|
|
# Set verbosity so that it can be used elsewhere.
|
|
|
|
self.verbosity = options.verbose - options.quiet
|
|
|
|
|
2019-01-21 13:56:12 +01:00
|
|
|
level_number = setup_logging(
|
2018-06-26 11:45:40 +02:00
|
|
|
verbosity=self.verbosity,
|
|
|
|
no_color=options.no_color,
|
2018-06-26 12:37:58 +02:00
|
|
|
user_log_file=options.log,
|
2018-06-26 11:45:40 +02:00
|
|
|
)
|
|
|
|
|
2019-07-07 10:45:16 +02:00
|
|
|
if sys.version_info[:2] == (2, 7):
|
2019-01-27 22:05:35 +01:00
|
|
|
message = (
|
|
|
|
"A future version of pip will drop support for Python 2.7."
|
2019-01-19 20:48:55 +01:00
|
|
|
)
|
2019-01-27 22:05:35 +01:00
|
|
|
if platform.python_implementation() == "CPython":
|
|
|
|
message = (
|
|
|
|
"Python 2.7 will reach the end of its life on January "
|
|
|
|
"1st, 2020. Please upgrade your Python as Python 2.7 "
|
|
|
|
"won't be maintained after that date. "
|
|
|
|
) + message
|
|
|
|
deprecated(message, replacement=None, gone_in=None)
|
2019-01-11 11:14:25 +01:00
|
|
|
|
2018-06-26 11:45:40 +02:00
|
|
|
# TODO: Try to get these passing down from the command?
|
|
|
|
# without resorting to os.environ to hold these.
|
|
|
|
# This also affects isolated builds and it should.
|
2013-02-19 04:38:26 +01:00
|
|
|
|
2011-10-04 16:06:34 +02:00
|
|
|
if options.no_input:
|
|
|
|
os.environ['PIP_NO_INPUT'] = '1'
|
2012-01-30 07:26:12 +01:00
|
|
|
|
2011-10-04 16:10:46 +02:00
|
|
|
if options.exists_action:
|
2013-09-16 03:09:36 +02:00
|
|
|
os.environ['PIP_EXISTS_ACTION'] = ' '.join(options.exists_action)
|
2010-06-03 04:25:26 +02:00
|
|
|
|
2017-05-18 15:20:01 +02:00
|
|
|
if options.require_venv and not self.ignore_require_venv:
|
2009-11-20 09:47:28 +01:00
|
|
|
# If a venv is required check if it can really be found
|
2013-12-27 07:09:48 +01:00
|
|
|
if not running_under_virtualenv():
|
2014-08-31 01:52:28 +02:00
|
|
|
logger.critical(
|
2014-01-27 15:07:10 +01:00
|
|
|
'Could not find an activated virtualenv (required).'
|
|
|
|
)
|
2011-08-02 20:03:35 +02:00
|
|
|
sys.exit(VIRTUALENV_NOT_FOUND)
|
2009-11-20 09:47:28 +01:00
|
|
|
|
|
|
|
try:
|
2011-08-02 19:16:18 +02:00
|
|
|
status = self.run(options, args)
|
2011-08-02 19:54:18 +02:00
|
|
|
# FIXME: all commands should return an exit status
|
|
|
|
# and when it is done, isinstance is not needed anymore
|
2011-08-02 19:16:18 +02:00
|
|
|
if isinstance(status, int):
|
2014-08-31 01:52:28 +02:00
|
|
|
return status
|
2014-01-27 14:02:10 +01:00
|
|
|
except PreviousBuildDirError as exc:
|
2014-08-31 01:52:28 +02:00
|
|
|
logger.critical(str(exc))
|
2015-07-16 21:21:41 +02:00
|
|
|
logger.debug('Exception information:', exc_info=True)
|
2014-08-31 01:52:28 +02:00
|
|
|
|
|
|
|
return PREVIOUS_BUILD_DIR_ERROR
|
|
|
|
except (InstallationError, UninstallationError, BadCommand) as exc:
|
|
|
|
logger.critical(str(exc))
|
2015-07-16 21:21:41 +02:00
|
|
|
logger.debug('Exception information:', exc_info=True)
|
2014-08-31 01:52:28 +02:00
|
|
|
|
|
|
|
return ERROR
|
2014-01-27 14:02:10 +01:00
|
|
|
except CommandError as exc:
|
2019-01-26 23:20:14 +01:00
|
|
|
logger.critical('%s', exc)
|
2015-07-16 21:21:41 +02:00
|
|
|
logger.debug('Exception information:', exc_info=True)
|
2014-08-31 01:52:28 +02:00
|
|
|
|
2018-10-23 10:55:07 +02:00
|
|
|
return ERROR
|
|
|
|
except BrokenStdoutLoggingError:
|
|
|
|
# Bypass our logger and write any remaining messages to stderr
|
|
|
|
# because stdout no longer works.
|
|
|
|
print('ERROR: Pipe to stdout was broken', file=sys.stderr)
|
2019-01-21 13:56:12 +01:00
|
|
|
if level_number <= logging.DEBUG:
|
2018-10-23 10:55:07 +02:00
|
|
|
traceback.print_exc(file=sys.stderr)
|
|
|
|
|
2014-08-31 01:52:28 +02:00
|
|
|
return ERROR
|
2011-04-16 18:25:18 +02:00
|
|
|
except KeyboardInterrupt:
|
2014-08-31 01:52:28 +02:00
|
|
|
logger.critical('Operation cancelled by user')
|
2015-07-16 21:21:41 +02:00
|
|
|
logger.debug('Exception information:', exc_info=True)
|
2014-08-31 01:52:28 +02:00
|
|
|
|
|
|
|
return ERROR
|
2018-06-27 10:07:18 +02:00
|
|
|
except BaseException:
|
2015-07-16 21:21:41 +02:00
|
|
|
logger.critical('Exception:', exc_info=True)
|
2014-08-31 01:52:28 +02:00
|
|
|
|
|
|
|
return UNKNOWN_ERROR
|
2015-05-27 00:39:26 +02:00
|
|
|
finally:
|
2018-09-05 19:50:08 +02:00
|
|
|
allow_version_check = (
|
|
|
|
# Does this command have the index_group options?
|
|
|
|
hasattr(options, "no_index") and
|
|
|
|
# Is this command allowed to perform this check?
|
|
|
|
not (options.disable_pip_version_check or options.no_index)
|
2018-06-26 11:54:44 +02:00
|
|
|
)
|
2018-09-05 19:50:08 +02:00
|
|
|
# Check if we're using the latest version of pip available
|
|
|
|
if allow_version_check:
|
2018-06-26 11:54:44 +02:00
|
|
|
session = self._build_session(
|
|
|
|
options,
|
|
|
|
retries=0,
|
|
|
|
timeout=min(5, options.timeout)
|
|
|
|
)
|
|
|
|
with session:
|
2017-02-27 05:19:06 +01:00
|
|
|
pip_version_check(session, options)
|
2018-06-26 11:53:19 +02:00
|
|
|
|
|
|
|
# Shutdown the logging module
|
|
|
|
logging.shutdown()
|
2014-08-31 01:52:28 +02:00
|
|
|
|
|
|
|
return SUCCESS
|
2009-11-20 09:47:28 +01:00
|
|
|
|
2010-06-03 04:25:26 +02:00
|
|
|
|
2015-04-07 14:28:10 +02:00
|
|
|
class RequirementCommand(Command):
|
|
|
|
|
|
|
|
@staticmethod
|
2018-10-26 17:07:27 +02:00
|
|
|
def populate_requirement_set(requirement_set, # type: RequirementSet
|
|
|
|
args, # type: List[str]
|
|
|
|
options, # type: Values
|
|
|
|
finder, # type: PackageFinder
|
|
|
|
session, # type: PipSession
|
|
|
|
name, # type: str
|
|
|
|
wheel_cache # type: Optional[WheelCache]
|
|
|
|
):
|
|
|
|
# type: (...) -> None
|
2015-04-07 14:28:10 +02:00
|
|
|
"""
|
|
|
|
Marshal cmd line args into a requirement set.
|
|
|
|
"""
|
2017-07-05 20:41:45 +02:00
|
|
|
# NOTE: As a side-effect, options.require_hashes and
|
|
|
|
# requirement_set.require_hashes may be updated
|
2017-06-27 18:37:38 +02:00
|
|
|
|
2015-06-02 05:39:10 +02:00
|
|
|
for filename in options.constraints:
|
2018-01-20 14:04:42 +01:00
|
|
|
for req_to_add in parse_requirements(
|
2015-06-02 05:39:10 +02:00
|
|
|
filename,
|
|
|
|
constraint=True, finder=finder, options=options,
|
|
|
|
session=session, wheel_cache=wheel_cache):
|
2018-01-20 14:04:42 +01:00
|
|
|
req_to_add.is_direct = True
|
|
|
|
requirement_set.add_requirement(req_to_add)
|
2015-06-02 05:39:10 +02:00
|
|
|
|
2015-04-29 01:00:38 +02:00
|
|
|
for req in args:
|
2018-08-21 17:07:40 +02:00
|
|
|
req_to_add = install_req_from_line(
|
2018-01-20 14:04:42 +01:00
|
|
|
req, None, isolated=options.isolated_mode,
|
2018-10-09 19:16:08 +02:00
|
|
|
use_pep517=options.use_pep517,
|
2018-01-20 14:04:42 +01:00
|
|
|
wheel_cache=wheel_cache
|
2015-04-07 14:28:10 +02:00
|
|
|
)
|
2018-01-20 14:04:42 +01:00
|
|
|
req_to_add.is_direct = True
|
|
|
|
requirement_set.add_requirement(req_to_add)
|
2015-04-07 14:28:10 +02:00
|
|
|
|
2015-04-29 01:00:38 +02:00
|
|
|
for req in options.editables:
|
2018-08-21 16:32:25 +02:00
|
|
|
req_to_add = install_req_from_editable(
|
2018-01-20 14:04:42 +01:00
|
|
|
req,
|
|
|
|
isolated=options.isolated_mode,
|
2018-10-09 19:16:08 +02:00
|
|
|
use_pep517=options.use_pep517,
|
2018-01-20 14:04:42 +01:00
|
|
|
wheel_cache=wheel_cache
|
2015-04-07 14:28:10 +02:00
|
|
|
)
|
2018-01-20 14:04:42 +01:00
|
|
|
req_to_add.is_direct = True
|
|
|
|
requirement_set.add_requirement(req_to_add)
|
2015-04-07 14:28:10 +02:00
|
|
|
|
|
|
|
for filename in options.requirements:
|
2018-01-20 14:04:42 +01:00
|
|
|
for req_to_add in parse_requirements(
|
2015-04-07 14:28:10 +02:00
|
|
|
filename,
|
2015-04-20 06:43:02 +02:00
|
|
|
finder=finder, options=options, session=session,
|
2018-10-09 19:16:08 +02:00
|
|
|
wheel_cache=wheel_cache,
|
|
|
|
use_pep517=options.use_pep517):
|
2018-01-20 14:04:42 +01:00
|
|
|
req_to_add.is_direct = True
|
|
|
|
requirement_set.add_requirement(req_to_add)
|
2015-10-21 21:50:57 +02:00
|
|
|
# If --require-hashes was a line in a requirements file, tell
|
|
|
|
# RequirementSet about it:
|
|
|
|
requirement_set.require_hashes = options.require_hashes
|
2015-04-07 14:28:10 +02:00
|
|
|
|
2017-01-06 23:21:46 +01:00
|
|
|
if not (args or options.editables or options.requirements):
|
2015-04-07 14:28:10 +02:00
|
|
|
opts = {'name': name}
|
|
|
|
if options.find_links:
|
2017-01-06 23:21:46 +01:00
|
|
|
raise CommandError(
|
|
|
|
'You must give at least one requirement to %(name)s '
|
|
|
|
'(maybe you meant "pip %(name)s %(links)s"?)' %
|
|
|
|
dict(opts, links=' '.join(options.find_links)))
|
2015-04-07 14:28:10 +02:00
|
|
|
else:
|
2017-01-06 23:21:46 +01:00
|
|
|
raise CommandError(
|
|
|
|
'You must give at least one requirement to %(name)s '
|
|
|
|
'(see "pip help %(name)s")' % opts)
|
2015-07-29 22:25:13 +02:00
|
|
|
|
2018-10-26 17:07:27 +02:00
|
|
|
def _build_package_finder(
|
|
|
|
self,
|
|
|
|
options, # type: Values
|
|
|
|
session, # type: PipSession
|
2019-06-22 08:30:34 +02:00
|
|
|
target_python=None, # type: Optional[TargetPython]
|
2019-05-21 20:10:59 +02:00
|
|
|
ignore_requires_python=None, # type: Optional[bool]
|
2018-10-26 17:07:27 +02:00
|
|
|
):
|
|
|
|
# type: (...) -> PackageFinder
|
2015-07-29 22:25:13 +02:00
|
|
|
"""
|
|
|
|
Create a package finder appropriate to this requirement command.
|
2019-05-21 20:10:59 +02:00
|
|
|
|
|
|
|
:param ignore_requires_python: Whether to ignore incompatible
|
|
|
|
"Requires-Python" values in links. Defaults to False.
|
2015-07-29 22:25:13 +02:00
|
|
|
"""
|
2019-06-21 18:10:03 +02:00
|
|
|
search_scope = make_search_scope(options)
|
2019-06-28 20:14:55 +02:00
|
|
|
selection_prefs = SelectionPreferences(
|
|
|
|
allow_yanked=True,
|
|
|
|
format_control=options.format_control,
|
|
|
|
allow_all_prereleases=options.pre,
|
|
|
|
prefer_binary=options.prefer_binary,
|
|
|
|
ignore_requires_python=ignore_requires_python,
|
|
|
|
)
|
2015-07-29 22:25:13 +02:00
|
|
|
|
2019-05-17 09:13:40 +02:00
|
|
|
return PackageFinder.create(
|
2019-06-21 18:10:03 +02:00
|
|
|
search_scope=search_scope,
|
2019-06-28 20:14:55 +02:00
|
|
|
selection_prefs=selection_prefs,
|
2015-07-29 22:25:13 +02:00
|
|
|
trusted_hosts=options.trusted_hosts,
|
|
|
|
session=session,
|
2019-06-09 22:11:16 +02:00
|
|
|
target_python=target_python,
|
2015-07-29 22:25:13 +02:00
|
|
|
)
|