| """ |
| shared options and groups |
| |
| The principle here is to define options once, but *not* instantiate them |
| globally. One reason being that options with action='append' can carry state |
| between parses. pip parses general options twice internally, and shouldn't |
| pass on state. To be consistent, all options will follow this design. |
| """ |
| |
| # The following comment should be removed at some point in the future. |
| # mypy: strict-optional=False |
| |
| from __future__ import absolute_import |
| |
| import os |
| import textwrap |
| import warnings |
| from distutils.util import strtobool |
| from functools import partial |
| from optparse import SUPPRESS_HELP, Option, OptionGroup |
| from textwrap import dedent |
| |
| from pip._internal.cli.progress_bars import BAR_TYPES |
| from pip._internal.exceptions import CommandError |
| from pip._internal.locations import USER_CACHE_DIR, get_src_prefix |
| from pip._internal.models.format_control import FormatControl |
| from pip._internal.models.index import PyPI |
| from pip._internal.models.target_python import TargetPython |
| from pip._internal.utils.hashes import STRONG_HASHES |
| from pip._internal.utils.typing import MYPY_CHECK_RUNNING |
| |
| if MYPY_CHECK_RUNNING: |
| from typing import Any, Callable, Dict, Optional, Tuple |
| from optparse import OptionParser, Values |
| from pip._internal.cli.parser import ConfigOptionParser |
| |
| |
| def raise_option_error(parser, option, msg): |
| # type: (OptionParser, Option, str) -> None |
| """ |
| Raise an option parsing error using parser.error(). |
| |
| Args: |
| parser: an OptionParser instance. |
| option: an Option instance. |
| msg: the error text. |
| """ |
| msg = '{} error: {}'.format(option, msg) |
| msg = textwrap.fill(' '.join(msg.split())) |
| parser.error(msg) |
| |
| |
| def make_option_group(group, parser): |
| # type: (Dict[str, Any], ConfigOptionParser) -> OptionGroup |
| """ |
| Return an OptionGroup object |
| group -- assumed to be dict with 'name' and 'options' keys |
| parser -- an optparse Parser |
| """ |
| option_group = OptionGroup(parser, group['name']) |
| for option in group['options']: |
| option_group.add_option(option()) |
| return option_group |
| |
| |
| def check_install_build_global(options, check_options=None): |
| # type: (Values, Optional[Values]) -> None |
| """Disable wheels if per-setup.py call options are set. |
| |
| :param options: The OptionParser options to update. |
| :param check_options: The options to check, if not supplied defaults to |
| options. |
| """ |
| if check_options is None: |
| check_options = options |
| |
| def getname(n): |
| # type: (str) -> Optional[Any] |
| return getattr(check_options, n, None) |
| names = ["build_options", "global_options", "install_options"] |
| if any(map(getname, names)): |
| control = options.format_control |
| control.disallow_binaries() |
| warnings.warn( |
| 'Disabling all use of wheels due to the use of --build-option ' |
| '/ --global-option / --install-option.', stacklevel=2, |
| ) |
| |
| |
| def check_dist_restriction(options, check_target=False): |
| # type: (Values, bool) -> None |
| """Function for determining if custom platform options are allowed. |
| |
| :param options: The OptionParser options. |
| :param check_target: Whether or not to check if --target is being used. |
| """ |
| dist_restriction_set = any([ |
| options.python_version, |
| options.platform, |
| options.abi, |
| options.implementation, |
| ]) |
| |
| binary_only = FormatControl(set(), {':all:'}) |
| sdist_dependencies_allowed = ( |
| options.format_control != binary_only and |
| not options.ignore_dependencies |
| ) |
| |
| # Installations or downloads using dist restrictions must not combine |
| # source distributions and dist-specific wheels, as they are not |
| # guaranteed to be locally compatible. |
| if dist_restriction_set and sdist_dependencies_allowed: |
| raise CommandError( |
| "When restricting platform and interpreter constraints using " |
| "--python-version, --platform, --abi, or --implementation, " |
| "either --no-deps must be set, or --only-binary=:all: must be " |
| "set and --no-binary must not be set (or must be set to " |
| ":none:)." |
| ) |
| |
| if check_target: |
| if dist_restriction_set and not options.target_dir: |
| raise CommandError( |
| "Can not use any platform or abi specific options unless " |
| "installing via '--target'" |
| ) |
| |
| |
| def _path_option_check(option, opt, value): |
| # type: (Option, str, str) -> str |
| return os.path.expanduser(value) |
| |
| |
| class PipOption(Option): |
| TYPES = Option.TYPES + ("path",) |
| TYPE_CHECKER = Option.TYPE_CHECKER.copy() |
| TYPE_CHECKER["path"] = _path_option_check |
| |
| |
| ########### |
| # options # |
| ########### |
| |
| help_ = partial( |
| Option, |
| '-h', '--help', |
| dest='help', |
| action='help', |
| help='Show help.', |
| ) # type: Callable[..., Option] |
| |
| isolated_mode = partial( |
| Option, |
| "--isolated", |
| dest="isolated_mode", |
| action="store_true", |
| default=False, |
| help=( |
| "Run pip in an isolated mode, ignoring environment variables and user " |
| "configuration." |
| ), |
| ) # type: Callable[..., Option] |
| |
| require_virtualenv = partial( |
| Option, |
| # Run only if inside a virtualenv, bail if not. |
| '--require-virtualenv', '--require-venv', |
| dest='require_venv', |
| action='store_true', |
| default=False, |
| help=SUPPRESS_HELP |
| ) # type: Callable[..., Option] |
| |
| verbose = partial( |
| Option, |
| '-v', '--verbose', |
| dest='verbose', |
| action='count', |
| default=0, |
| help='Give more output. Option is additive, and can be used up to 3 times.' |
| ) # type: Callable[..., Option] |
| |
| no_color = partial( |
| Option, |
| '--no-color', |
| dest='no_color', |
| action='store_true', |
| default=False, |
| help="Suppress colored output", |
| ) # type: Callable[..., Option] |
| |
| version = partial( |
| Option, |
| '-V', '--version', |
| dest='version', |
| action='store_true', |
| help='Show version and exit.', |
| ) # type: Callable[..., Option] |
| |
| quiet = partial( |
| Option, |
| '-q', '--quiet', |
| dest='quiet', |
| action='count', |
| default=0, |
| help=( |
| 'Give less output. Option is additive, and can be used up to 3' |
| ' times (corresponding to WARNING, ERROR, and CRITICAL logging' |
| ' levels).' |
| ), |
| ) # type: Callable[..., Option] |
| |
| progress_bar = partial( |
| Option, |
| '--progress-bar', |
| dest='progress_bar', |
| type='choice', |
| choices=list(BAR_TYPES.keys()), |
| default='on', |
| help=( |
| 'Specify type of progress to be displayed [' + |
| '|'.join(BAR_TYPES.keys()) + '] (default: %default)' |
| ), |
| ) # type: Callable[..., Option] |
| |
| log = partial( |
| PipOption, |
| "--log", "--log-file", "--local-log", |
| dest="log", |
| metavar="path", |
| type="path", |
| help="Path to a verbose appending log." |
| ) # type: Callable[..., Option] |
| |
| no_input = partial( |
| Option, |
| # Don't ask for input |
| '--no-input', |
| dest='no_input', |
| action='store_true', |
| default=False, |
| help="Disable prompting for input." |
| ) # type: Callable[..., Option] |
| |
| proxy = partial( |
| Option, |
| '--proxy', |
| dest='proxy', |
| type='str', |
| default='', |
| help="Specify a proxy in the form [user:passwd@]proxy.server:port." |
| ) # type: Callable[..., Option] |
| |
| retries = partial( |
| Option, |
| '--retries', |
| dest='retries', |
| type='int', |
| default=5, |
| help="Maximum number of retries each connection should attempt " |
| "(default %default times).", |
| ) # type: Callable[..., Option] |
| |
| timeout = partial( |
| Option, |
| '--timeout', '--default-timeout', |
| metavar='sec', |
| dest='timeout', |
| type='float', |
| default=15, |
| help='Set the socket timeout (default %default seconds).', |
| ) # type: Callable[..., Option] |
| |
| |
| def exists_action(): |
| # type: () -> Option |
| return Option( |
| # Option when path already exist |
| '--exists-action', |
| dest='exists_action', |
| type='choice', |
| choices=['s', 'i', 'w', 'b', 'a'], |
| default=[], |
| action='append', |
| metavar='action', |
| help="Default action when a path already exists: " |
| "(s)witch, (i)gnore, (w)ipe, (b)ackup, (a)bort.", |
| ) |
| |
| |
| cert = partial( |
| PipOption, |
| '--cert', |
| dest='cert', |
| type='path', |
| metavar='path', |
| help="Path to alternate CA bundle.", |
| ) # type: Callable[..., Option] |
| |
| client_cert = partial( |
| PipOption, |
| '--client-cert', |
| dest='client_cert', |
| type='path', |
| default=None, |
| metavar='path', |
| help="Path to SSL client certificate, a single file containing the " |
| "private key and the certificate in PEM format.", |
| ) # type: Callable[..., Option] |
| |
| index_url = partial( |
| Option, |
| '-i', '--index-url', '--pypi-url', |
| dest='index_url', |
| metavar='URL', |
| default=PyPI.simple_url, |
| help="Base URL of the Python Package Index (default %default). " |
| "This should point to a repository compliant with PEP 503 " |
| "(the simple repository API) or a local directory laid out " |
| "in the same format.", |
| ) # type: Callable[..., Option] |
| |
| |
| def extra_index_url(): |
| # type: () -> Option |
| return Option( |
| '--extra-index-url', |
| dest='extra_index_urls', |
| metavar='URL', |
| action='append', |
| default=[], |
| help="Extra URLs of package indexes to use in addition to " |
| "--index-url. Should follow the same rules as " |
| "--index-url.", |
| ) |
| |
| |
| no_index = partial( |
| Option, |
| '--no-index', |
| dest='no_index', |
| action='store_true', |
| default=False, |
| help='Ignore package index (only looking at --find-links URLs instead).', |
| ) # type: Callable[..., Option] |
| |
| |
| def find_links(): |
| # type: () -> Option |
| return Option( |
| '-f', '--find-links', |
| dest='find_links', |
| action='append', |
| default=[], |
| metavar='url', |
| help="If a URL or path to an html file, then parse for links to " |
| "archives such as sdist (.tar.gz) or wheel (.whl) files. " |
| "If a local path or file:// URL that's a directory, " |
| "then look for archives in the directory listing. " |
| "Links to VCS project URLs are not supported.", |
| ) |
| |
| |
| def trusted_host(): |
| # type: () -> Option |
| return Option( |
| "--trusted-host", |
| dest="trusted_hosts", |
| action="append", |
| metavar="HOSTNAME", |
| default=[], |
| help="Mark this host or host:port pair as trusted, even though it " |
| "does not have valid or any HTTPS.", |
| ) |
| |
| |
| def constraints(): |
| # type: () -> Option |
| return Option( |
| '-c', '--constraint', |
| dest='constraints', |
| action='append', |
| default=[], |
| metavar='file', |
| help='Constrain versions using the given constraints file. ' |
| 'This option can be used multiple times.' |
| ) |
| |
| |
| def requirements(): |
| # type: () -> Option |
| return Option( |
| '-r', '--requirement', |
| dest='requirements', |
| action='append', |
| default=[], |
| metavar='file', |
| help='Install from the given requirements file. ' |
| 'This option can be used multiple times.' |
| ) |
| |
| |
| def editable(): |
| # type: () -> Option |
| return Option( |
| '-e', '--editable', |
| dest='editables', |
| action='append', |
| default=[], |
| metavar='path/url', |
| help=('Install a project in editable mode (i.e. setuptools ' |
| '"develop mode") from a local project path or a VCS url.'), |
| ) |
| |
| |
| def _handle_src(option, opt_str, value, parser): |
| # type: (Option, str, str, OptionParser) -> None |
| value = os.path.abspath(value) |
| setattr(parser.values, option.dest, value) |
| |
| |
| src = partial( |
| PipOption, |
| '--src', '--source', '--source-dir', '--source-directory', |
| dest='src_dir', |
| type='path', |
| metavar='dir', |
| default=get_src_prefix(), |
| action='callback', |
| callback=_handle_src, |
| help='Directory to check out editable projects into. ' |
| 'The default in a virtualenv is "<venv path>/src". ' |
| 'The default for global installs is "<current dir>/src".' |
| ) # type: Callable[..., Option] |
| |
| |
| def _get_format_control(values, option): |
| # type: (Values, Option) -> Any |
| """Get a format_control object.""" |
| return getattr(values, option.dest) |
| |
| |
| def _handle_no_binary(option, opt_str, value, parser): |
| # type: (Option, str, str, OptionParser) -> None |
| existing = _get_format_control(parser.values, option) |
| FormatControl.handle_mutual_excludes( |
| value, existing.no_binary, existing.only_binary, |
| ) |
| |
| |
| def _handle_only_binary(option, opt_str, value, parser): |
| # type: (Option, str, str, OptionParser) -> None |
| existing = _get_format_control(parser.values, option) |
| FormatControl.handle_mutual_excludes( |
| value, existing.only_binary, existing.no_binary, |
| ) |
| |
| |
| def no_binary(): |
| # type: () -> Option |
| format_control = FormatControl(set(), set()) |
| return Option( |
| "--no-binary", dest="format_control", action="callback", |
| callback=_handle_no_binary, type="str", |
| default=format_control, |
| help='Do not use binary packages. Can be supplied multiple times, and ' |
| 'each time adds to the existing value. Accepts either ":all:" to ' |
| 'disable all binary packages, ":none:" to empty the set (notice ' |
| 'the colons), or one or more package names with commas between ' |
| 'them (no colons). Note that some packages are tricky to compile ' |
| 'and may fail to install when this option is used on them.', |
| ) |
| |
| |
| def only_binary(): |
| # type: () -> Option |
| format_control = FormatControl(set(), set()) |
| return Option( |
| "--only-binary", dest="format_control", action="callback", |
| callback=_handle_only_binary, type="str", |
| default=format_control, |
| help='Do not use source packages. Can be supplied multiple times, and ' |
| 'each time adds to the existing value. Accepts either ":all:" to ' |
| 'disable all source packages, ":none:" to empty the set, or one ' |
| 'or more package names with commas between them. Packages ' |
| 'without binary distributions will fail to install when this ' |
| 'option is used on them.', |
| ) |
| |
| |
| platform = partial( |
| Option, |
| '--platform', |
| dest='platform', |
| metavar='platform', |
| default=None, |
| help=("Only use wheels compatible with <platform>. " |
| "Defaults to the platform of the running system."), |
| ) # type: Callable[..., Option] |
| |
| |
| # This was made a separate function for unit-testing purposes. |
| def _convert_python_version(value): |
| # type: (str) -> Tuple[Tuple[int, ...], Optional[str]] |
| """ |
| Convert a version string like "3", "37", or "3.7.3" into a tuple of ints. |
| |
| :return: A 2-tuple (version_info, error_msg), where `error_msg` is |
| non-None if and only if there was a parsing error. |
| """ |
| if not value: |
| # The empty string is the same as not providing a value. |
| return (None, None) |
| |
| parts = value.split('.') |
| if len(parts) > 3: |
| return ((), 'at most three version parts are allowed') |
| |
| if len(parts) == 1: |
| # Then we are in the case of "3" or "37". |
| value = parts[0] |
| if len(value) > 1: |
| parts = [value[0], value[1:]] |
| |
| try: |
| version_info = tuple(int(part) for part in parts) |
| except ValueError: |
| return ((), 'each version part must be an integer') |
| |
| return (version_info, None) |
| |
| |
| def _handle_python_version(option, opt_str, value, parser): |
| # type: (Option, str, str, OptionParser) -> None |
| """ |
| Handle a provided --python-version value. |
| """ |
| version_info, error_msg = _convert_python_version(value) |
| if error_msg is not None: |
| msg = ( |
| 'invalid --python-version value: {!r}: {}'.format( |
| value, error_msg, |
| ) |
| ) |
| raise_option_error(parser, option=option, msg=msg) |
| |
| parser.values.python_version = version_info |
| |
| |
| python_version = partial( |
| Option, |
| '--python-version', |
| dest='python_version', |
| metavar='python_version', |
| action='callback', |
| callback=_handle_python_version, type='str', |
| default=None, |
| help=dedent("""\ |
| The Python interpreter version to use for wheel and "Requires-Python" |
| compatibility checks. Defaults to a version derived from the running |
| interpreter. The version can be specified using up to three dot-separated |
| integers (e.g. "3" for 3.0.0, "3.7" for 3.7.0, or "3.7.3"). A major-minor |
| version can also be given as a string without dots (e.g. "37" for 3.7.0). |
| """), |
| ) # type: Callable[..., Option] |
| |
| |
| implementation = partial( |
| Option, |
| '--implementation', |
| dest='implementation', |
| metavar='implementation', |
| default=None, |
| help=("Only use wheels compatible with Python " |
| "implementation <implementation>, e.g. 'pp', 'jy', 'cp', " |
| " or 'ip'. If not specified, then the current " |
| "interpreter implementation is used. Use 'py' to force " |
| "implementation-agnostic wheels."), |
| ) # type: Callable[..., Option] |
| |
| |
| abi = partial( |
| Option, |
| '--abi', |
| dest='abi', |
| metavar='abi', |
| default=None, |
| help=("Only use wheels compatible with Python " |
| "abi <abi>, e.g. 'pypy_41'. If not specified, then the " |
| "current interpreter abi tag is used. Generally " |
| "you will need to specify --implementation, " |
| "--platform, and --python-version when using " |
| "this option."), |
| ) # type: Callable[..., Option] |
| |
| |
| def add_target_python_options(cmd_opts): |
| # type: (OptionGroup) -> None |
| cmd_opts.add_option(platform()) |
| cmd_opts.add_option(python_version()) |
| cmd_opts.add_option(implementation()) |
| cmd_opts.add_option(abi()) |
| |
| |
| def make_target_python(options): |
| # type: (Values) -> TargetPython |
| target_python = TargetPython( |
| platform=options.platform, |
| py_version_info=options.python_version, |
| abi=options.abi, |
| implementation=options.implementation, |
| ) |
| |
| return target_python |
| |
| |
| def prefer_binary(): |
| # type: () -> Option |
| return Option( |
| "--prefer-binary", |
| dest="prefer_binary", |
| action="store_true", |
| default=False, |
| help="Prefer older binary packages over newer source packages." |
| ) |
| |
| |
| cache_dir = partial( |
| PipOption, |
| "--cache-dir", |
| dest="cache_dir", |
| default=USER_CACHE_DIR, |
| metavar="dir", |
| type='path', |
| help="Store the cache data in <dir>." |
| ) # type: Callable[..., Option] |
| |
| |
| def _handle_no_cache_dir(option, opt, value, parser): |
| # type: (Option, str, str, OptionParser) -> None |
| """ |
| Process a value provided for the --no-cache-dir option. |
| |
| This is an optparse.Option callback for the --no-cache-dir option. |
| """ |
| # The value argument will be None if --no-cache-dir is passed via the |
| # command-line, since the option doesn't accept arguments. However, |
| # the value can be non-None if the option is triggered e.g. by an |
| # environment variable, like PIP_NO_CACHE_DIR=true. |
| if value is not None: |
| # Then parse the string value to get argument error-checking. |
| try: |
| strtobool(value) |
| except ValueError as exc: |
| raise_option_error(parser, option=option, msg=str(exc)) |
| |
| # Originally, setting PIP_NO_CACHE_DIR to a value that strtobool() |
| # converted to 0 (like "false" or "no") caused cache_dir to be disabled |
| # rather than enabled (logic would say the latter). Thus, we disable |
| # the cache directory not just on values that parse to True, but (for |
| # backwards compatibility reasons) also on values that parse to False. |
| # In other words, always set it to False if the option is provided in |
| # some (valid) form. |
| parser.values.cache_dir = False |
| |
| |
| no_cache = partial( |
| Option, |
| "--no-cache-dir", |
| dest="cache_dir", |
| action="callback", |
| callback=_handle_no_cache_dir, |
| help="Disable the cache.", |
| ) # type: Callable[..., Option] |
| |
| no_deps = partial( |
| Option, |
| '--no-deps', '--no-dependencies', |
| dest='ignore_dependencies', |
| action='store_true', |
| default=False, |
| help="Don't install package dependencies.", |
| ) # type: Callable[..., Option] |
| |
| |
| def _handle_build_dir(option, opt, value, parser): |
| # type: (Option, str, str, OptionParser) -> None |
| if value: |
| value = os.path.abspath(value) |
| setattr(parser.values, option.dest, value) |
| |
| |
| build_dir = partial( |
| PipOption, |
| '-b', '--build', '--build-dir', '--build-directory', |
| dest='build_dir', |
| type='path', |
| metavar='dir', |
| action='callback', |
| callback=_handle_build_dir, |
| help='(DEPRECATED) ' |
| 'Directory to unpack packages into and build in. Note that ' |
| 'an initial build still takes place in a temporary directory. ' |
| 'The location of temporary directories can be controlled by setting ' |
| 'the TMPDIR environment variable (TEMP on Windows) appropriately. ' |
| 'When passed, build directories are not cleaned in case of failures.' |
| ) # type: Callable[..., Option] |
| |
| ignore_requires_python = partial( |
| Option, |
| '--ignore-requires-python', |
| dest='ignore_requires_python', |
| action='store_true', |
| help='Ignore the Requires-Python information.' |
| ) # type: Callable[..., Option] |
| |
| no_build_isolation = partial( |
| Option, |
| '--no-build-isolation', |
| dest='build_isolation', |
| action='store_false', |
| default=True, |
| help='Disable isolation when building a modern source distribution. ' |
| 'Build dependencies specified by PEP 518 must be already installed ' |
| 'if this option is used.' |
| ) # type: Callable[..., Option] |
| |
| |
| def _handle_no_use_pep517(option, opt, value, parser): |
| # type: (Option, str, str, OptionParser) -> None |
| """ |
| Process a value provided for the --no-use-pep517 option. |
| |
| This is an optparse.Option callback for the no_use_pep517 option. |
| """ |
| # Since --no-use-pep517 doesn't accept arguments, the value argument |
| # will be None if --no-use-pep517 is passed via the command-line. |
| # However, the value can be non-None if the option is triggered e.g. |
| # by an environment variable, for example "PIP_NO_USE_PEP517=true". |
| if value is not None: |
| msg = """A value was passed for --no-use-pep517, |
| probably using either the PIP_NO_USE_PEP517 environment variable |
| or the "no-use-pep517" config file option. Use an appropriate value |
| of the PIP_USE_PEP517 environment variable or the "use-pep517" |
| config file option instead. |
| """ |
| raise_option_error(parser, option=option, msg=msg) |
| |
| # Otherwise, --no-use-pep517 was passed via the command-line. |
| parser.values.use_pep517 = False |
| |
| |
| use_pep517 = partial( |
| Option, |
| '--use-pep517', |
| dest='use_pep517', |
| action='store_true', |
| default=None, |
| help='Use PEP 517 for building source distributions ' |
| '(use --no-use-pep517 to force legacy behaviour).' |
| ) # type: Any |
| |
| no_use_pep517 = partial( |
| Option, |
| '--no-use-pep517', |
| dest='use_pep517', |
| action='callback', |
| callback=_handle_no_use_pep517, |
| default=None, |
| help=SUPPRESS_HELP |
| ) # type: Any |
| |
| install_options = partial( |
| Option, |
| '--install-option', |
| dest='install_options', |
| action='append', |
| metavar='options', |
| help="Extra arguments to be supplied to the setup.py install " |
| "command (use like --install-option=\"--install-scripts=/usr/local/" |
| "bin\"). Use multiple --install-option options to pass multiple " |
| "options to setup.py install. If you are using an option with a " |
| "directory path, be sure to use absolute path.", |
| ) # type: Callable[..., Option] |
| |
| global_options = partial( |
| Option, |
| '--global-option', |
| dest='global_options', |
| action='append', |
| metavar='options', |
| help="Extra global options to be supplied to the setup.py " |
| "call before the install command.", |
| ) # type: Callable[..., Option] |
| |
| no_clean = partial( |
| Option, |
| '--no-clean', |
| action='store_true', |
| default=False, |
| help="Don't clean up build directories." |
| ) # type: Callable[..., Option] |
| |
| pre = partial( |
| Option, |
| '--pre', |
| action='store_true', |
| default=False, |
| help="Include pre-release and development versions. By default, " |
| "pip only finds stable versions.", |
| ) # type: Callable[..., Option] |
| |
| disable_pip_version_check = partial( |
| Option, |
| "--disable-pip-version-check", |
| dest="disable_pip_version_check", |
| action="store_true", |
| default=False, |
| help="Don't periodically check PyPI to determine whether a new version " |
| "of pip is available for download. Implied with --no-index.", |
| ) # type: Callable[..., Option] |
| |
| |
| def _handle_merge_hash(option, opt_str, value, parser): |
| # type: (Option, str, str, OptionParser) -> None |
| """Given a value spelled "algo:digest", append the digest to a list |
| pointed to in a dict by the algo name.""" |
| if not parser.values.hashes: |
| parser.values.hashes = {} |
| try: |
| algo, digest = value.split(':', 1) |
| except ValueError: |
| parser.error('Arguments to {} must be a hash name ' # noqa |
| 'followed by a value, like --hash=sha256:' |
| 'abcde...'.format(opt_str)) |
| if algo not in STRONG_HASHES: |
| parser.error('Allowed hash algorithms for {} are {}.'.format( # noqa |
| opt_str, ', '.join(STRONG_HASHES))) |
| parser.values.hashes.setdefault(algo, []).append(digest) |
| |
| |
| hash = partial( |
| Option, |
| '--hash', |
| # Hash values eventually end up in InstallRequirement.hashes due to |
| # __dict__ copying in process_line(). |
| dest='hashes', |
| action='callback', |
| callback=_handle_merge_hash, |
| type='string', |
| help="Verify that the package's archive matches this " |
| 'hash before installing. Example: --hash=sha256:abcdef...', |
| ) # type: Callable[..., Option] |
| |
| |
| require_hashes = partial( |
| Option, |
| '--require-hashes', |
| dest='require_hashes', |
| action='store_true', |
| default=False, |
| help='Require a hash to check each requirement against, for ' |
| 'repeatable installs. This option is implied when any package in a ' |
| 'requirements file has a --hash option.', |
| ) # type: Callable[..., Option] |
| |
| |
| list_path = partial( |
| PipOption, |
| '--path', |
| dest='path', |
| type='path', |
| action='append', |
| help='Restrict to the specified installation path for listing ' |
| 'packages (can be used multiple times).' |
| ) # type: Callable[..., Option] |
| |
| |
| def check_list_path_option(options): |
| # type: (Values) -> None |
| if options.path and (options.user or options.local): |
| raise CommandError( |
| "Cannot combine '--path' with '--user' or '--local'" |
| ) |
| |
| |
| no_python_version_warning = partial( |
| Option, |
| '--no-python-version-warning', |
| dest='no_python_version_warning', |
| action='store_true', |
| default=False, |
| help='Silence deprecation warnings for upcoming unsupported Pythons.', |
| ) # type: Callable[..., Option] |
| |
| |
| unstable_feature = partial( |
| Option, |
| '--unstable-feature', |
| dest='unstable_features', |
| metavar='feature', |
| action='append', |
| default=[], |
| choices=['resolver'], |
| help=SUPPRESS_HELP, # TODO: drop this in pip 20.3 |
| ) # type: Callable[..., Option] |
| |
| use_new_feature = partial( |
| Option, |
| '--use-feature', |
| dest='features_enabled', |
| metavar='feature', |
| action='append', |
| default=[], |
| choices=['2020-resolver', 'fast-deps'], |
| help='Enable new functionality, that may be backward incompatible.', |
| ) # type: Callable[..., Option] |
| |
| use_deprecated_feature = partial( |
| Option, |
| '--use-deprecated', |
| dest='deprecated_features_enabled', |
| metavar='feature', |
| action='append', |
| default=[], |
| choices=[], |
| help=( |
| 'Enable deprecated functionality, that will be removed in the future.' |
| ), |
| ) # type: Callable[..., Option] |
| |
| |
| ########## |
| # groups # |
| ########## |
| |
| general_group = { |
| 'name': 'General Options', |
| 'options': [ |
| help_, |
| isolated_mode, |
| require_virtualenv, |
| verbose, |
| version, |
| quiet, |
| log, |
| no_input, |
| proxy, |
| retries, |
| timeout, |
| exists_action, |
| trusted_host, |
| cert, |
| client_cert, |
| cache_dir, |
| no_cache, |
| disable_pip_version_check, |
| no_color, |
| no_python_version_warning, |
| unstable_feature, |
| use_new_feature, |
| use_deprecated_feature, |
| ] |
| } # type: Dict[str, Any] |
| |
| index_group = { |
| 'name': 'Package Index Options', |
| 'options': [ |
| index_url, |
| extra_index_url, |
| no_index, |
| find_links, |
| ] |
| } # type: Dict[str, Any] |