1075 lines
		
	
	
	
		
			29 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			1075 lines
		
	
	
	
		
			29 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|   | """
 | ||
|  | 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 | ||
|  | 
 | ||
|  | import importlib.util | ||
|  | import logging | ||
|  | import os | ||
|  | import textwrap | ||
|  | from functools import partial | ||
|  | from optparse import SUPPRESS_HELP, Option, OptionGroup, OptionParser, Values | ||
|  | from textwrap import dedent | ||
|  | from typing import Any, Callable, Dict, Optional, Tuple | ||
|  | 
 | ||
|  | from pip._vendor.packaging.utils import canonicalize_name | ||
|  | 
 | ||
|  | from pip._internal.cli.parser import ConfigOptionParser | ||
|  | 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.misc import strtobool | ||
|  | 
 | ||
|  | logger = logging.getLogger(__name__) | ||
|  | 
 | ||
|  | 
 | ||
|  | def raise_option_error(parser: OptionParser, option: Option, msg: str) -> None: | ||
|  |     """
 | ||
|  |     Raise an option parsing error using parser.error(). | ||
|  | 
 | ||
|  |     Args: | ||
|  |       parser: an OptionParser instance. | ||
|  |       option: an Option instance. | ||
|  |       msg: the error text. | ||
|  |     """
 | ||
|  |     msg = f"{option} error: {msg}" | ||
|  |     msg = textwrap.fill(" ".join(msg.split())) | ||
|  |     parser.error(msg) | ||
|  | 
 | ||
|  | 
 | ||
|  | def make_option_group(group: Dict[str, Any], parser: 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_dist_restriction(options: Values, check_target: bool = False) -> 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.platforms, | ||
|  |             options.abis, | ||
|  |             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 not options.dry_run and dist_restriction_set and not options.target_dir: | ||
|  |             raise CommandError( | ||
|  |                 "Can not use any platform or abi specific options unless " | ||
|  |                 "installing via '--target' or using '--dry-run'" | ||
|  |             ) | ||
|  | 
 | ||
|  | 
 | ||
|  | def _path_option_check(option: Option, opt: str, value: str) -> str: | ||
|  |     return os.path.expanduser(value) | ||
|  | 
 | ||
|  | 
 | ||
|  | def _package_name_option_check(option: Option, opt: str, value: str) -> str: | ||
|  |     return canonicalize_name(value) | ||
|  | 
 | ||
|  | 
 | ||
|  | class PipOption(Option): | ||
|  |     TYPES = Option.TYPES + ("path", "package_name") | ||
|  |     TYPE_CHECKER = Option.TYPE_CHECKER.copy() | ||
|  |     TYPE_CHECKER["package_name"] = _package_name_option_check | ||
|  |     TYPE_CHECKER["path"] = _path_option_check | ||
|  | 
 | ||
|  | 
 | ||
|  | ########### | ||
|  | # options # | ||
|  | ########### | ||
|  | 
 | ||
|  | help_: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "-h", | ||
|  |     "--help", | ||
|  |     dest="help", | ||
|  |     action="help", | ||
|  |     help="Show help.", | ||
|  | ) | ||
|  | 
 | ||
|  | debug_mode: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "--debug", | ||
|  |     dest="debug_mode", | ||
|  |     action="store_true", | ||
|  |     default=False, | ||
|  |     help=( | ||
|  |         "Let unhandled exceptions propagate outside the main subroutine, " | ||
|  |         "instead of logging them to stderr." | ||
|  |     ), | ||
|  | ) | ||
|  | 
 | ||
|  | isolated_mode: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "--isolated", | ||
|  |     dest="isolated_mode", | ||
|  |     action="store_true", | ||
|  |     default=False, | ||
|  |     help=( | ||
|  |         "Run pip in an isolated mode, ignoring environment variables and user " | ||
|  |         "configuration." | ||
|  |     ), | ||
|  | ) | ||
|  | 
 | ||
|  | require_virtualenv: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "--require-virtualenv", | ||
|  |     "--require-venv", | ||
|  |     dest="require_venv", | ||
|  |     action="store_true", | ||
|  |     default=False, | ||
|  |     help=( | ||
|  |         "Allow pip to only run in a virtual environment; " | ||
|  |         "exit with an error otherwise." | ||
|  |     ), | ||
|  | ) | ||
|  | 
 | ||
|  | override_externally_managed: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "--break-system-packages", | ||
|  |     dest="override_externally_managed", | ||
|  |     action="store_true", | ||
|  |     help="Allow pip to modify an EXTERNALLY-MANAGED Python installation", | ||
|  | ) | ||
|  | 
 | ||
|  | python: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "--python", | ||
|  |     dest="python", | ||
|  |     help="Run pip with the specified Python interpreter.", | ||
|  | ) | ||
|  | 
 | ||
|  | verbose: Callable[..., Option] = 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.", | ||
|  | ) | ||
|  | 
 | ||
|  | no_color: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "--no-color", | ||
|  |     dest="no_color", | ||
|  |     action="store_true", | ||
|  |     default=False, | ||
|  |     help="Suppress colored output.", | ||
|  | ) | ||
|  | 
 | ||
|  | version: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "-V", | ||
|  |     "--version", | ||
|  |     dest="version", | ||
|  |     action="store_true", | ||
|  |     help="Show version and exit.", | ||
|  | ) | ||
|  | 
 | ||
|  | quiet: Callable[..., Option] = 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)." | ||
|  |     ), | ||
|  | ) | ||
|  | 
 | ||
|  | progress_bar: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "--progress-bar", | ||
|  |     dest="progress_bar", | ||
|  |     type="choice", | ||
|  |     choices=["on", "off"], | ||
|  |     default="on", | ||
|  |     help="Specify whether the progress bar should be used [on, off] (default: on)", | ||
|  | ) | ||
|  | 
 | ||
|  | log: Callable[..., Option] = partial( | ||
|  |     PipOption, | ||
|  |     "--log", | ||
|  |     "--log-file", | ||
|  |     "--local-log", | ||
|  |     dest="log", | ||
|  |     metavar="path", | ||
|  |     type="path", | ||
|  |     help="Path to a verbose appending log.", | ||
|  | ) | ||
|  | 
 | ||
|  | no_input: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     # Don't ask for input | ||
|  |     "--no-input", | ||
|  |     dest="no_input", | ||
|  |     action="store_true", | ||
|  |     default=False, | ||
|  |     help="Disable prompting for input.", | ||
|  | ) | ||
|  | 
 | ||
|  | keyring_provider: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "--keyring-provider", | ||
|  |     dest="keyring_provider", | ||
|  |     choices=["auto", "disabled", "import", "subprocess"], | ||
|  |     default="auto", | ||
|  |     help=( | ||
|  |         "Enable the credential lookup via the keyring library if user input is allowed." | ||
|  |         " Specify which mechanism to use [disabled, import, subprocess]." | ||
|  |         " (default: disabled)" | ||
|  |     ), | ||
|  | ) | ||
|  | 
 | ||
|  | proxy: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "--proxy", | ||
|  |     dest="proxy", | ||
|  |     type="str", | ||
|  |     default="", | ||
|  |     help="Specify a proxy in the form scheme://[user:passwd@]proxy.server:port.", | ||
|  | ) | ||
|  | 
 | ||
|  | retries: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "--retries", | ||
|  |     dest="retries", | ||
|  |     type="int", | ||
|  |     default=5, | ||
|  |     help="Maximum number of retries each connection should attempt " | ||
|  |     "(default %default times).", | ||
|  | ) | ||
|  | 
 | ||
|  | timeout: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "--timeout", | ||
|  |     "--default-timeout", | ||
|  |     metavar="sec", | ||
|  |     dest="timeout", | ||
|  |     type="float", | ||
|  |     default=15, | ||
|  |     help="Set the socket timeout (default %default seconds).", | ||
|  | ) | ||
|  | 
 | ||
|  | 
 | ||
|  | def exists_action() -> 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: Callable[..., Option] = partial( | ||
|  |     PipOption, | ||
|  |     "--cert", | ||
|  |     dest="cert", | ||
|  |     type="path", | ||
|  |     metavar="path", | ||
|  |     help=( | ||
|  |         "Path to PEM-encoded CA certificate bundle. " | ||
|  |         "If provided, overrides the default. " | ||
|  |         "See 'SSL Certificate Verification' in pip documentation " | ||
|  |         "for more information." | ||
|  |     ), | ||
|  | ) | ||
|  | 
 | ||
|  | client_cert: Callable[..., Option] = 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.", | ||
|  | ) | ||
|  | 
 | ||
|  | index_url: Callable[..., Option] = 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.", | ||
|  | ) | ||
|  | 
 | ||
|  | 
 | ||
|  | def extra_index_url() -> 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: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "--no-index", | ||
|  |     dest="no_index", | ||
|  |     action="store_true", | ||
|  |     default=False, | ||
|  |     help="Ignore package index (only looking at --find-links URLs instead).", | ||
|  | ) | ||
|  | 
 | ||
|  | 
 | ||
|  | def find_links() -> 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() -> 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() -> 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() -> 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() -> 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: Option, opt_str: str, value: str, parser: OptionParser) -> None: | ||
|  |     value = os.path.abspath(value) | ||
|  |     setattr(parser.values, option.dest, value) | ||
|  | 
 | ||
|  | 
 | ||
|  | src: Callable[..., Option] = 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".', | ||
|  | ) | ||
|  | 
 | ||
|  | 
 | ||
|  | def _get_format_control(values: Values, option: Option) -> Any: | ||
|  |     """Get a format_control object.""" | ||
|  |     return getattr(values, option.dest) | ||
|  | 
 | ||
|  | 
 | ||
|  | def _handle_no_binary( | ||
|  |     option: Option, opt_str: str, value: str, parser: 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: Option, opt_str: str, value: str, parser: OptionParser | ||
|  | ) -> None: | ||
|  |     existing = _get_format_control(parser.values, option) | ||
|  |     FormatControl.handle_mutual_excludes( | ||
|  |         value, | ||
|  |         existing.only_binary, | ||
|  |         existing.no_binary, | ||
|  |     ) | ||
|  | 
 | ||
|  | 
 | ||
|  | def no_binary() -> 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() -> 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.", | ||
|  |     ) | ||
|  | 
 | ||
|  | 
 | ||
|  | platforms: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "--platform", | ||
|  |     dest="platforms", | ||
|  |     metavar="platform", | ||
|  |     action="append", | ||
|  |     default=None, | ||
|  |     help=( | ||
|  |         "Only use wheels compatible with <platform>. Defaults to the " | ||
|  |         "platform of the running system. Use this option multiple times to " | ||
|  |         "specify multiple platforms supported by the target interpreter." | ||
|  |     ), | ||
|  | ) | ||
|  | 
 | ||
|  | 
 | ||
|  | # This was made a separate function for unit-testing purposes. | ||
|  | def _convert_python_version(value: 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: Option, opt_str: str, value: str, parser: OptionParser | ||
|  | ) -> None: | ||
|  |     """
 | ||
|  |     Handle a provided --python-version value. | ||
|  |     """
 | ||
|  |     version_info, error_msg = _convert_python_version(value) | ||
|  |     if error_msg is not None: | ||
|  |         msg = f"invalid --python-version value: {value!r}: {error_msg}" | ||
|  |         raise_option_error(parser, option=option, msg=msg) | ||
|  | 
 | ||
|  |     parser.values.python_version = version_info | ||
|  | 
 | ||
|  | 
 | ||
|  | python_version: Callable[..., Option] = 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). | ||
|  |     """
 | ||
|  |     ), | ||
|  | ) | ||
|  | 
 | ||
|  | 
 | ||
|  | implementation: Callable[..., Option] = 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." | ||
|  |     ), | ||
|  | ) | ||
|  | 
 | ||
|  | 
 | ||
|  | abis: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "--abi", | ||
|  |     dest="abis", | ||
|  |     metavar="abi", | ||
|  |     action="append", | ||
|  |     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. " | ||
|  |         "Use this option multiple times to specify multiple abis supported " | ||
|  |         "by the target interpreter. Generally you will need to specify " | ||
|  |         "--implementation, --platform, and --python-version when using this " | ||
|  |         "option." | ||
|  |     ), | ||
|  | ) | ||
|  | 
 | ||
|  | 
 | ||
|  | def add_target_python_options(cmd_opts: OptionGroup) -> None: | ||
|  |     cmd_opts.add_option(platforms()) | ||
|  |     cmd_opts.add_option(python_version()) | ||
|  |     cmd_opts.add_option(implementation()) | ||
|  |     cmd_opts.add_option(abis()) | ||
|  | 
 | ||
|  | 
 | ||
|  | def make_target_python(options: Values) -> TargetPython: | ||
|  |     target_python = TargetPython( | ||
|  |         platforms=options.platforms, | ||
|  |         py_version_info=options.python_version, | ||
|  |         abis=options.abis, | ||
|  |         implementation=options.implementation, | ||
|  |     ) | ||
|  | 
 | ||
|  |     return target_python | ||
|  | 
 | ||
|  | 
 | ||
|  | def prefer_binary() -> Option: | ||
|  |     return Option( | ||
|  |         "--prefer-binary", | ||
|  |         dest="prefer_binary", | ||
|  |         action="store_true", | ||
|  |         default=False, | ||
|  |         help=( | ||
|  |             "Prefer binary packages over source packages, even if the " | ||
|  |             "source packages are newer." | ||
|  |         ), | ||
|  |     ) | ||
|  | 
 | ||
|  | 
 | ||
|  | cache_dir: Callable[..., Option] = partial( | ||
|  |     PipOption, | ||
|  |     "--cache-dir", | ||
|  |     dest="cache_dir", | ||
|  |     default=USER_CACHE_DIR, | ||
|  |     metavar="dir", | ||
|  |     type="path", | ||
|  |     help="Store the cache data in <dir>.", | ||
|  | ) | ||
|  | 
 | ||
|  | 
 | ||
|  | def _handle_no_cache_dir( | ||
|  |     option: Option, opt: str, value: str, parser: 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: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "--no-cache-dir", | ||
|  |     dest="cache_dir", | ||
|  |     action="callback", | ||
|  |     callback=_handle_no_cache_dir, | ||
|  |     help="Disable the cache.", | ||
|  | ) | ||
|  | 
 | ||
|  | no_deps: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "--no-deps", | ||
|  |     "--no-dependencies", | ||
|  |     dest="ignore_dependencies", | ||
|  |     action="store_true", | ||
|  |     default=False, | ||
|  |     help="Don't install package dependencies.", | ||
|  | ) | ||
|  | 
 | ||
|  | ignore_requires_python: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "--ignore-requires-python", | ||
|  |     dest="ignore_requires_python", | ||
|  |     action="store_true", | ||
|  |     help="Ignore the Requires-Python information.", | ||
|  | ) | ||
|  | 
 | ||
|  | no_build_isolation: Callable[..., Option] = 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.", | ||
|  | ) | ||
|  | 
 | ||
|  | check_build_deps: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "--check-build-dependencies", | ||
|  |     dest="check_build_deps", | ||
|  |     action="store_true", | ||
|  |     default=False, | ||
|  |     help="Check the build dependencies when PEP517 is used.", | ||
|  | ) | ||
|  | 
 | ||
|  | 
 | ||
|  | def _handle_no_use_pep517( | ||
|  |     option: Option, opt: str, value: str, parser: 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) | ||
|  | 
 | ||
|  |     # If user doesn't wish to use pep517, we check if setuptools and wheel are installed | ||
|  |     # and raise error if it is not. | ||
|  |     packages = ("setuptools", "wheel") | ||
|  |     if not all(importlib.util.find_spec(package) for package in packages): | ||
|  |         msg = ( | ||
|  |             f"It is not possible to use --no-use-pep517 " | ||
|  |             f"without {' and '.join(packages)} installed." | ||
|  |         ) | ||
|  |         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: Any = 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).", | ||
|  | ) | ||
|  | 
 | ||
|  | no_use_pep517: Any = partial( | ||
|  |     Option, | ||
|  |     "--no-use-pep517", | ||
|  |     dest="use_pep517", | ||
|  |     action="callback", | ||
|  |     callback=_handle_no_use_pep517, | ||
|  |     default=None, | ||
|  |     help=SUPPRESS_HELP, | ||
|  | ) | ||
|  | 
 | ||
|  | 
 | ||
|  | def _handle_config_settings( | ||
|  |     option: Option, opt_str: str, value: str, parser: OptionParser | ||
|  | ) -> None: | ||
|  |     key, sep, val = value.partition("=") | ||
|  |     if sep != "=": | ||
|  |         parser.error(f"Arguments to {opt_str} must be of the form KEY=VAL") | ||
|  |     dest = getattr(parser.values, option.dest) | ||
|  |     if dest is None: | ||
|  |         dest = {} | ||
|  |         setattr(parser.values, option.dest, dest) | ||
|  |     if key in dest: | ||
|  |         if isinstance(dest[key], list): | ||
|  |             dest[key].append(val) | ||
|  |         else: | ||
|  |             dest[key] = [dest[key], val] | ||
|  |     else: | ||
|  |         dest[key] = val | ||
|  | 
 | ||
|  | 
 | ||
|  | config_settings: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "-C", | ||
|  |     "--config-settings", | ||
|  |     dest="config_settings", | ||
|  |     type=str, | ||
|  |     action="callback", | ||
|  |     callback=_handle_config_settings, | ||
|  |     metavar="settings", | ||
|  |     help="Configuration settings to be passed to the PEP 517 build backend. " | ||
|  |     "Settings take the form KEY=VALUE. Use multiple --config-settings options " | ||
|  |     "to pass multiple keys to the backend.", | ||
|  | ) | ||
|  | 
 | ||
|  | build_options: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "--build-option", | ||
|  |     dest="build_options", | ||
|  |     metavar="options", | ||
|  |     action="append", | ||
|  |     help="Extra arguments to be supplied to 'setup.py bdist_wheel'.", | ||
|  | ) | ||
|  | 
 | ||
|  | global_options: Callable[..., Option] = 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 or bdist_wheel command.", | ||
|  | ) | ||
|  | 
 | ||
|  | no_clean: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "--no-clean", | ||
|  |     action="store_true", | ||
|  |     default=False, | ||
|  |     help="Don't clean up build directories.", | ||
|  | ) | ||
|  | 
 | ||
|  | pre: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "--pre", | ||
|  |     action="store_true", | ||
|  |     default=False, | ||
|  |     help="Include pre-release and development versions. By default, " | ||
|  |     "pip only finds stable versions.", | ||
|  | ) | ||
|  | 
 | ||
|  | disable_pip_version_check: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "--disable-pip-version-check", | ||
|  |     dest="disable_pip_version_check", | ||
|  |     action="store_true", | ||
|  |     default=True, | ||
|  |     help="Don't periodically check PyPI to determine whether a new version " | ||
|  |     "of pip is available for download. Implied with --no-index.", | ||
|  | ) | ||
|  | 
 | ||
|  | root_user_action: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "--root-user-action", | ||
|  |     dest="root_user_action", | ||
|  |     default="warn", | ||
|  |     choices=["warn", "ignore"], | ||
|  |     help="Action if pip is run as a root user. By default, a warning message is shown.", | ||
|  | ) | ||
|  | 
 | ||
|  | 
 | ||
|  | def _handle_merge_hash( | ||
|  |     option: Option, opt_str: str, value: str, parser: 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( | ||
|  |             f"Arguments to {opt_str} must be a hash name " | ||
|  |             "followed by a value, like --hash=sha256:" | ||
|  |             "abcde..." | ||
|  |         ) | ||
|  |     if algo not in STRONG_HASHES: | ||
|  |         parser.error( | ||
|  |             "Allowed hash algorithms for {} are {}.".format( | ||
|  |                 opt_str, ", ".join(STRONG_HASHES) | ||
|  |             ) | ||
|  |         ) | ||
|  |     parser.values.hashes.setdefault(algo, []).append(digest) | ||
|  | 
 | ||
|  | 
 | ||
|  | hash: Callable[..., Option] = 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...", | ||
|  | ) | ||
|  | 
 | ||
|  | 
 | ||
|  | require_hashes: Callable[..., Option] = 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.", | ||
|  | ) | ||
|  | 
 | ||
|  | 
 | ||
|  | list_path: Callable[..., Option] = partial( | ||
|  |     PipOption, | ||
|  |     "--path", | ||
|  |     dest="path", | ||
|  |     type="path", | ||
|  |     action="append", | ||
|  |     help="Restrict to the specified installation path for listing " | ||
|  |     "packages (can be used multiple times).", | ||
|  | ) | ||
|  | 
 | ||
|  | 
 | ||
|  | def check_list_path_option(options: Values) -> None: | ||
|  |     if options.path and (options.user or options.local): | ||
|  |         raise CommandError("Cannot combine '--path' with '--user' or '--local'") | ||
|  | 
 | ||
|  | 
 | ||
|  | list_exclude: Callable[..., Option] = partial( | ||
|  |     PipOption, | ||
|  |     "--exclude", | ||
|  |     dest="excludes", | ||
|  |     action="append", | ||
|  |     metavar="package", | ||
|  |     type="package_name", | ||
|  |     help="Exclude specified package from the output", | ||
|  | ) | ||
|  | 
 | ||
|  | 
 | ||
|  | no_python_version_warning: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "--no-python-version-warning", | ||
|  |     dest="no_python_version_warning", | ||
|  |     action="store_true", | ||
|  |     default=False, | ||
|  |     help="Silence deprecation warnings for upcoming unsupported Pythons.", | ||
|  | ) | ||
|  | 
 | ||
|  | 
 | ||
|  | # Features that are now always on. A warning is printed if they are used. | ||
|  | ALWAYS_ENABLED_FEATURES = [ | ||
|  |     "no-binary-enable-wheel-cache",  # always on since 23.1 | ||
|  | ] | ||
|  | 
 | ||
|  | use_new_feature: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "--use-feature", | ||
|  |     dest="features_enabled", | ||
|  |     metavar="feature", | ||
|  |     action="append", | ||
|  |     default=[], | ||
|  |     choices=[ | ||
|  |         "fast-deps", | ||
|  |         "truststore", | ||
|  |     ] | ||
|  |     + ALWAYS_ENABLED_FEATURES, | ||
|  |     help="Enable new functionality, that may be backward incompatible.", | ||
|  | ) | ||
|  | 
 | ||
|  | use_deprecated_feature: Callable[..., Option] = partial( | ||
|  |     Option, | ||
|  |     "--use-deprecated", | ||
|  |     dest="deprecated_features_enabled", | ||
|  |     metavar="feature", | ||
|  |     action="append", | ||
|  |     default=[], | ||
|  |     choices=[ | ||
|  |         "legacy-resolver", | ||
|  |     ], | ||
|  |     help=("Enable deprecated functionality, that will be removed in the future."), | ||
|  | ) | ||
|  | 
 | ||
|  | 
 | ||
|  | ########## | ||
|  | # groups # | ||
|  | ########## | ||
|  | 
 | ||
|  | general_group: Dict[str, Any] = { | ||
|  |     "name": "General Options", | ||
|  |     "options": [ | ||
|  |         help_, | ||
|  |         debug_mode, | ||
|  |         isolated_mode, | ||
|  |         require_virtualenv, | ||
|  |         python, | ||
|  |         verbose, | ||
|  |         version, | ||
|  |         quiet, | ||
|  |         log, | ||
|  |         no_input, | ||
|  |         keyring_provider, | ||
|  |         proxy, | ||
|  |         retries, | ||
|  |         timeout, | ||
|  |         exists_action, | ||
|  |         trusted_host, | ||
|  |         cert, | ||
|  |         client_cert, | ||
|  |         cache_dir, | ||
|  |         no_cache, | ||
|  |         disable_pip_version_check, | ||
|  |         no_color, | ||
|  |         no_python_version_warning, | ||
|  |         use_new_feature, | ||
|  |         use_deprecated_feature, | ||
|  |     ], | ||
|  | } | ||
|  | 
 | ||
|  | index_group: Dict[str, Any] = { | ||
|  |     "name": "Package Index Options", | ||
|  |     "options": [ | ||
|  |         index_url, | ||
|  |         extra_index_url, | ||
|  |         no_index, | ||
|  |         find_links, | ||
|  |     ], | ||
|  | } |