mirror of
https://github.com/pre-commit/pre-commit-hooks.git
synced 2026-04-11 13:44:17 +00:00
[pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
This commit is contained in:
parent
72ad6dc953
commit
f4cd1ba0d6
813 changed files with 66015 additions and 58839 deletions
|
|
@ -1,4 +1,4 @@
|
|||
"""Subpackage containing all of pip's command line interface related code
|
||||
"""
|
||||
|
||||
# This file intentionally does not import submodules
|
||||
from __future__ import annotations
|
||||
|
|
|
|||
|
|
@ -1,35 +1,40 @@
|
|||
"""Logic that powers autocompletion installed by ``pip completion``.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import optparse
|
||||
import os
|
||||
import sys
|
||||
from itertools import chain
|
||||
from typing import Any, Iterable, List, Optional
|
||||
from typing import Any
|
||||
from typing import Iterable
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
|
||||
from pip._internal.cli.main_parser import create_main_parser
|
||||
from pip._internal.commands import commands_dict, create_command
|
||||
from pip._internal.commands import commands_dict
|
||||
from pip._internal.commands import create_command
|
||||
from pip._internal.metadata import get_default_environment
|
||||
|
||||
|
||||
def autocomplete() -> None:
|
||||
"""Entry Point for completion of main and subcommand options."""
|
||||
# Don't complete if user hasn't sourced bash_completion file.
|
||||
if "PIP_AUTO_COMPLETE" not in os.environ:
|
||||
if 'PIP_AUTO_COMPLETE' not in os.environ:
|
||||
return
|
||||
cwords = os.environ["COMP_WORDS"].split()[1:]
|
||||
cword = int(os.environ["COMP_CWORD"])
|
||||
cwords = os.environ['COMP_WORDS'].split()[1:]
|
||||
cword = int(os.environ['COMP_CWORD'])
|
||||
try:
|
||||
current = cwords[cword - 1]
|
||||
except IndexError:
|
||||
current = ""
|
||||
current = ''
|
||||
|
||||
parser = create_main_parser()
|
||||
subcommands = list(commands_dict)
|
||||
options = []
|
||||
|
||||
# subcommand
|
||||
subcommand_name: Optional[str] = None
|
||||
subcommand_name: str | None = None
|
||||
for word in cwords:
|
||||
if word in subcommands:
|
||||
subcommand_name = word
|
||||
|
|
@ -37,12 +42,12 @@ def autocomplete() -> None:
|
|||
# subcommand options
|
||||
if subcommand_name is not None:
|
||||
# special case: 'help' subcommand has no options
|
||||
if subcommand_name == "help":
|
||||
if subcommand_name == 'help':
|
||||
sys.exit(1)
|
||||
# special case: list locally installed dists for show and uninstall
|
||||
should_list_installed = not current.startswith("-") and subcommand_name in [
|
||||
"show",
|
||||
"uninstall",
|
||||
should_list_installed = not current.startswith('-') and subcommand_name in [
|
||||
'show',
|
||||
'uninstall',
|
||||
]
|
||||
if should_list_installed:
|
||||
env = get_default_environment()
|
||||
|
|
@ -50,8 +55,8 @@ def autocomplete() -> None:
|
|||
installed = [
|
||||
dist.canonical_name
|
||||
for dist in env.iter_installed_distributions(local_only=True)
|
||||
if dist.canonical_name.startswith(lc)
|
||||
and dist.canonical_name not in cwords[1:]
|
||||
if dist.canonical_name.startswith(lc) and
|
||||
dist.canonical_name not in cwords[1:]
|
||||
]
|
||||
# if there are no dists installed, fall back to option completion
|
||||
if installed:
|
||||
|
|
@ -60,10 +65,10 @@ def autocomplete() -> None:
|
|||
sys.exit(1)
|
||||
|
||||
should_list_installables = (
|
||||
not current.startswith("-") and subcommand_name == "install"
|
||||
not current.startswith('-') and subcommand_name == 'install'
|
||||
)
|
||||
if should_list_installables:
|
||||
for path in auto_complete_paths(current, "path"):
|
||||
for path in auto_complete_paths(current, 'path'):
|
||||
print(path)
|
||||
sys.exit(1)
|
||||
|
||||
|
|
@ -75,7 +80,7 @@ def autocomplete() -> None:
|
|||
options.append((opt_str, opt.nargs))
|
||||
|
||||
# filter out previously specified options from available options
|
||||
prev_opts = [x.split("=")[0] for x in cwords[1 : cword - 1]]
|
||||
prev_opts = [x.split('=')[0] for x in cwords[1: cword - 1]]
|
||||
options = [(x, v) for (x, v) in options if x not in prev_opts]
|
||||
# filter options by current input
|
||||
options = [(k, v) for k, v in options if k.startswith(current)]
|
||||
|
|
@ -93,8 +98,8 @@ def autocomplete() -> None:
|
|||
for option in options:
|
||||
opt_label = option[0]
|
||||
# append '=' to options which require args
|
||||
if option[1] and option[0][:2] == "--":
|
||||
opt_label += "="
|
||||
if option[1] and option[0][:2] == '--':
|
||||
opt_label += '='
|
||||
print(opt_label)
|
||||
else:
|
||||
# show main parser options only when necessary
|
||||
|
|
@ -102,7 +107,7 @@ def autocomplete() -> None:
|
|||
opts = [i.option_list for i in parser.option_groups]
|
||||
opts.append(parser.option_list)
|
||||
flattened_opts = chain.from_iterable(opts)
|
||||
if current.startswith("-"):
|
||||
if current.startswith('-'):
|
||||
for opt in flattened_opts:
|
||||
if opt.help != optparse.SUPPRESS_HELP:
|
||||
subcommands += opt._long_opts + opt._short_opts
|
||||
|
|
@ -112,13 +117,13 @@ def autocomplete() -> None:
|
|||
if completion_type:
|
||||
subcommands = list(auto_complete_paths(current, completion_type))
|
||||
|
||||
print(" ".join([x for x in subcommands if x.startswith(current)]))
|
||||
print(' '.join([x for x in subcommands if x.startswith(current)]))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def get_path_completion_type(
|
||||
cwords: List[str], cword: int, opts: Iterable[Any]
|
||||
) -> Optional[str]:
|
||||
cwords: list[str], cword: int, opts: Iterable[Any],
|
||||
) -> str | None:
|
||||
"""Get the type of path completion (``file``, ``dir``, ``path`` or None)
|
||||
|
||||
:param cwords: same as the environmental variable ``COMP_WORDS``
|
||||
|
|
@ -126,15 +131,15 @@ def get_path_completion_type(
|
|||
:param opts: The available options to check
|
||||
:return: path completion type (``file``, ``dir``, ``path`` or None)
|
||||
"""
|
||||
if cword < 2 or not cwords[cword - 2].startswith("-"):
|
||||
if cword < 2 or not cwords[cword - 2].startswith('-'):
|
||||
return None
|
||||
for opt in opts:
|
||||
if opt.help == optparse.SUPPRESS_HELP:
|
||||
continue
|
||||
for o in str(opt).split("/"):
|
||||
if cwords[cword - 2].split("=")[0] == o:
|
||||
for o in str(opt).split('/'):
|
||||
if cwords[cword - 2].split('=')[0] == o:
|
||||
if not opt.metavar or any(
|
||||
x in ("path", "file", "dir") for x in opt.metavar.split("/")
|
||||
x in ('path', 'file', 'dir') for x in opt.metavar.split('/')
|
||||
):
|
||||
return opt.metavar
|
||||
return None
|
||||
|
|
@ -165,7 +170,7 @@ def auto_complete_paths(current: str, completion_type: str) -> Iterable[str]:
|
|||
# complete regular files when there is not ``<dir>`` after option
|
||||
# complete directories when there is ``<file>``, ``<path>`` or
|
||||
# ``<dir>``after option
|
||||
if completion_type != "dir" and os.path.isfile(opt):
|
||||
if completion_type != 'dir' and os.path.isfile(opt):
|
||||
yield comp_file
|
||||
elif os.path.isdir(opt):
|
||||
yield os.path.join(comp_file, "")
|
||||
yield os.path.join(comp_file, '')
|
||||
|
|
|
|||
|
|
@ -1,49 +1,52 @@
|
|||
"""Base Command class, and related routines"""
|
||||
from __future__ import annotations
|
||||
|
||||
import functools
|
||||
import logging
|
||||
import logging.config
|
||||
import optparse
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
from optparse import Values
|
||||
from typing import Any, Callable, List, Optional, Tuple
|
||||
|
||||
from pip._vendor.rich import traceback as rich_traceback
|
||||
from typing import Any
|
||||
from typing import Callable
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Tuple
|
||||
|
||||
from pip._internal.cli import cmdoptions
|
||||
from pip._internal.cli.command_context import CommandContextMixIn
|
||||
from pip._internal.cli.parser import ConfigOptionParser, UpdatingDefaultsHelpFormatter
|
||||
from pip._internal.cli.status_codes import (
|
||||
ERROR,
|
||||
PREVIOUS_BUILD_DIR_ERROR,
|
||||
UNKNOWN_ERROR,
|
||||
VIRTUALENV_NOT_FOUND,
|
||||
)
|
||||
from pip._internal.exceptions import (
|
||||
BadCommand,
|
||||
CommandError,
|
||||
DiagnosticPipError,
|
||||
InstallationError,
|
||||
NetworkConnectionError,
|
||||
PreviousBuildDirError,
|
||||
UninstallationError,
|
||||
)
|
||||
from pip._internal.cli.parser import ConfigOptionParser
|
||||
from pip._internal.cli.parser import UpdatingDefaultsHelpFormatter
|
||||
from pip._internal.cli.status_codes import ERROR
|
||||
from pip._internal.cli.status_codes import PREVIOUS_BUILD_DIR_ERROR
|
||||
from pip._internal.cli.status_codes import UNKNOWN_ERROR
|
||||
from pip._internal.cli.status_codes import VIRTUALENV_NOT_FOUND
|
||||
from pip._internal.exceptions import BadCommand
|
||||
from pip._internal.exceptions import CommandError
|
||||
from pip._internal.exceptions import DiagnosticPipError
|
||||
from pip._internal.exceptions import InstallationError
|
||||
from pip._internal.exceptions import NetworkConnectionError
|
||||
from pip._internal.exceptions import PreviousBuildDirError
|
||||
from pip._internal.exceptions import UninstallationError
|
||||
from pip._internal.utils.filesystem import check_path_owner
|
||||
from pip._internal.utils.logging import BrokenStdoutLoggingError, setup_logging
|
||||
from pip._internal.utils.misc import get_prog, normalize_path
|
||||
from pip._internal.utils.logging import BrokenStdoutLoggingError
|
||||
from pip._internal.utils.logging import setup_logging
|
||||
from pip._internal.utils.misc import get_prog
|
||||
from pip._internal.utils.misc import normalize_path
|
||||
from pip._internal.utils.temp_dir import global_tempdir_manager
|
||||
from pip._internal.utils.temp_dir import tempdir_registry
|
||||
from pip._internal.utils.temp_dir import TempDirectoryTypeRegistry as TempDirRegistry
|
||||
from pip._internal.utils.temp_dir import global_tempdir_manager, tempdir_registry
|
||||
from pip._internal.utils.virtualenv import running_under_virtualenv
|
||||
from pip._vendor.rich import traceback as rich_traceback
|
||||
|
||||
__all__ = ["Command"]
|
||||
__all__ = ['Command']
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Command(CommandContextMixIn):
|
||||
usage: str = ""
|
||||
usage: str = ''
|
||||
ignore_require_venv: bool = False
|
||||
|
||||
def __init__(self, name: str, summary: str, isolated: bool = False) -> None:
|
||||
|
|
@ -53,7 +56,7 @@ class Command(CommandContextMixIn):
|
|||
self.summary = summary
|
||||
self.parser = ConfigOptionParser(
|
||||
usage=self.usage,
|
||||
prog=f"{get_prog()} {name}",
|
||||
prog=f'{get_prog()} {name}',
|
||||
formatter=UpdatingDefaultsHelpFormatter(),
|
||||
add_help_option=False,
|
||||
name=name,
|
||||
|
|
@ -61,10 +64,10 @@ class Command(CommandContextMixIn):
|
|||
isolated=isolated,
|
||||
)
|
||||
|
||||
self.tempdir_registry: Optional[TempDirRegistry] = None
|
||||
self.tempdir_registry: TempDirRegistry | None = None
|
||||
|
||||
# Commands should add options to this option group
|
||||
optgroup_name = f"{self.name.capitalize()} Options"
|
||||
optgroup_name = f'{self.name.capitalize()} Options'
|
||||
self.cmd_opts = optparse.OptionGroup(self.parser, optgroup_name)
|
||||
|
||||
# Add the general options
|
||||
|
|
@ -86,23 +89,23 @@ class Command(CommandContextMixIn):
|
|||
"""
|
||||
# Make sure we do the pip version check if the index_group options
|
||||
# are present.
|
||||
assert not hasattr(options, "no_index")
|
||||
assert not hasattr(options, 'no_index')
|
||||
|
||||
def run(self, options: Values, args: List[str]) -> int:
|
||||
def run(self, options: Values, args: list[str]) -> int:
|
||||
raise NotImplementedError
|
||||
|
||||
def parse_args(self, args: List[str]) -> Tuple[Values, List[str]]:
|
||||
def parse_args(self, args: list[str]) -> tuple[Values, list[str]]:
|
||||
# factored out for testability
|
||||
return self.parser.parse_args(args)
|
||||
|
||||
def main(self, args: List[str]) -> int:
|
||||
def main(self, args: list[str]) -> int:
|
||||
try:
|
||||
with self.main_context():
|
||||
return self._main(args)
|
||||
finally:
|
||||
logging.shutdown()
|
||||
|
||||
def _main(self, args: List[str]) -> int:
|
||||
def _main(self, args: list[str]) -> int:
|
||||
# We must initialize this before the tempdir manager, otherwise the
|
||||
# configuration would not be accessible by the time we clean up the
|
||||
# tempdir manager.
|
||||
|
|
@ -127,15 +130,15 @@ class Command(CommandContextMixIn):
|
|||
# This also affects isolated builds and it should.
|
||||
|
||||
if options.no_input:
|
||||
os.environ["PIP_NO_INPUT"] = "1"
|
||||
os.environ['PIP_NO_INPUT'] = '1'
|
||||
|
||||
if options.exists_action:
|
||||
os.environ["PIP_EXISTS_ACTION"] = " ".join(options.exists_action)
|
||||
os.environ['PIP_EXISTS_ACTION'] = ' '.join(options.exists_action)
|
||||
|
||||
if options.require_venv and not self.ignore_require_venv:
|
||||
# If a venv is required check if it can really be found
|
||||
if not running_under_virtualenv():
|
||||
logger.critical("Could not find an activated virtualenv (required).")
|
||||
logger.critical('Could not find an activated virtualenv (required).')
|
||||
sys.exit(VIRTUALENV_NOT_FOUND)
|
||||
|
||||
if options.cache_dir:
|
||||
|
|
@ -143,23 +146,23 @@ class Command(CommandContextMixIn):
|
|||
if not check_path_owner(options.cache_dir):
|
||||
logger.warning(
|
||||
"The directory '%s' or its parent directory is not owned "
|
||||
"or is not writable by the current user. The cache "
|
||||
"has been disabled. Check the permissions and owner of "
|
||||
"that directory. If executing pip with sudo, you should "
|
||||
'or is not writable by the current user. The cache '
|
||||
'has been disabled. Check the permissions and owner of '
|
||||
'that directory. If executing pip with sudo, you should '
|
||||
"use sudo's -H flag.",
|
||||
options.cache_dir,
|
||||
)
|
||||
options.cache_dir = None
|
||||
|
||||
if "2020-resolver" in options.features_enabled:
|
||||
if '2020-resolver' in options.features_enabled:
|
||||
logger.warning(
|
||||
"--use-feature=2020-resolver no longer has any effect, "
|
||||
"since it is now the default dependency resolver in pip. "
|
||||
"This will become an error in pip 21.0."
|
||||
'--use-feature=2020-resolver no longer has any effect, '
|
||||
'since it is now the default dependency resolver in pip. '
|
||||
'This will become an error in pip 21.0.',
|
||||
)
|
||||
|
||||
def intercepts_unhandled_exc(
|
||||
run_func: Callable[..., int]
|
||||
run_func: Callable[..., int],
|
||||
) -> Callable[..., int]:
|
||||
@functools.wraps(run_func)
|
||||
def exc_logging_wrapper(*args: Any) -> int:
|
||||
|
|
@ -168,13 +171,13 @@ class Command(CommandContextMixIn):
|
|||
assert isinstance(status, int)
|
||||
return status
|
||||
except DiagnosticPipError as exc:
|
||||
logger.error("[present-diagnostic] %s", exc)
|
||||
logger.debug("Exception information:", exc_info=True)
|
||||
logger.error('[present-diagnostic] %s', exc)
|
||||
logger.debug('Exception information:', exc_info=True)
|
||||
|
||||
return ERROR
|
||||
except PreviousBuildDirError as exc:
|
||||
logger.critical(str(exc))
|
||||
logger.debug("Exception information:", exc_info=True)
|
||||
logger.debug('Exception information:', exc_info=True)
|
||||
|
||||
return PREVIOUS_BUILD_DIR_ERROR
|
||||
except (
|
||||
|
|
@ -184,29 +187,29 @@ class Command(CommandContextMixIn):
|
|||
NetworkConnectionError,
|
||||
) as exc:
|
||||
logger.critical(str(exc))
|
||||
logger.debug("Exception information:", exc_info=True)
|
||||
logger.debug('Exception information:', exc_info=True)
|
||||
|
||||
return ERROR
|
||||
except CommandError as exc:
|
||||
logger.critical("%s", exc)
|
||||
logger.debug("Exception information:", exc_info=True)
|
||||
logger.critical('%s', exc)
|
||||
logger.debug('Exception information:', exc_info=True)
|
||||
|
||||
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)
|
||||
print('ERROR: Pipe to stdout was broken', file=sys.stderr)
|
||||
if level_number <= logging.DEBUG:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
|
||||
return ERROR
|
||||
except KeyboardInterrupt:
|
||||
logger.critical("Operation cancelled by user")
|
||||
logger.debug("Exception information:", exc_info=True)
|
||||
logger.critical('Operation cancelled by user')
|
||||
logger.debug('Exception information:', exc_info=True)
|
||||
|
||||
return ERROR
|
||||
except BaseException:
|
||||
logger.critical("Exception:", exc_info=True)
|
||||
logger.critical('Exception:', exc_info=True)
|
||||
|
||||
return UNKNOWN_ERROR
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,7 +1,12 @@
|
|||
from contextlib import ExitStack, contextmanager
|
||||
from typing import ContextManager, Iterator, TypeVar
|
||||
from __future__ import annotations
|
||||
|
||||
_T = TypeVar("_T", covariant=True)
|
||||
from contextlib import contextmanager
|
||||
from contextlib import ExitStack
|
||||
from typing import ContextManager
|
||||
from typing import Iterator
|
||||
from typing import TypeVar
|
||||
|
||||
_T = TypeVar('_T', covariant=True)
|
||||
|
||||
|
||||
class CommandContextMixIn:
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
"""Primary application entrypoint.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import locale
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from typing import List, Optional
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
|
||||
from pip._internal.cli.autocompletion import autocomplete
|
||||
from pip._internal.cli.main_parser import parse_command
|
||||
|
|
@ -42,7 +45,7 @@ logger = logging.getLogger(__name__)
|
|||
# main, this should not be an issue in practice.
|
||||
|
||||
|
||||
def main(args: Optional[List[str]] = None) -> int:
|
||||
def main(args: list[str] | None = None) -> int:
|
||||
if args is None:
|
||||
args = sys.argv[1:]
|
||||
|
||||
|
|
@ -54,17 +57,17 @@ def main(args: Optional[List[str]] = None) -> int:
|
|||
try:
|
||||
cmd_name, cmd_args = parse_command(args)
|
||||
except PipError as exc:
|
||||
sys.stderr.write(f"ERROR: {exc}")
|
||||
sys.stderr.write(f'ERROR: {exc}')
|
||||
sys.stderr.write(os.linesep)
|
||||
sys.exit(1)
|
||||
|
||||
# Needed for locale.getpreferredencoding(False) to work
|
||||
# in pip._internal.utils.encoding.auto_decode
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, "")
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
except locale.Error as e:
|
||||
# setlocale can apparently crash if locale are uninitialized
|
||||
logger.debug("Ignoring error %s when setting locale", e)
|
||||
command = create_command(cmd_name, isolated=("--isolated" in cmd_args))
|
||||
logger.debug('Ignoring error %s when setting locale', e)
|
||||
command = create_command(cmd_name, isolated=('--isolated' in cmd_args))
|
||||
|
||||
return command.main(cmd_args)
|
||||
|
|
|
|||
|
|
@ -1,27 +1,32 @@
|
|||
"""A single place for constructing and exposing the main parser
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
from typing import List, Tuple
|
||||
from typing import List
|
||||
from typing import Tuple
|
||||
|
||||
from pip._internal.cli import cmdoptions
|
||||
from pip._internal.cli.parser import ConfigOptionParser, UpdatingDefaultsHelpFormatter
|
||||
from pip._internal.commands import commands_dict, get_similar_commands
|
||||
from pip._internal.cli.parser import ConfigOptionParser
|
||||
from pip._internal.cli.parser import UpdatingDefaultsHelpFormatter
|
||||
from pip._internal.commands import commands_dict
|
||||
from pip._internal.commands import get_similar_commands
|
||||
from pip._internal.exceptions import CommandError
|
||||
from pip._internal.utils.misc import get_pip_version, get_prog
|
||||
from pip._internal.utils.misc import get_pip_version
|
||||
from pip._internal.utils.misc import get_prog
|
||||
|
||||
__all__ = ["create_main_parser", "parse_command"]
|
||||
__all__ = ['create_main_parser', 'parse_command']
|
||||
|
||||
|
||||
def create_main_parser() -> ConfigOptionParser:
|
||||
"""Creates and returns the main parser for pip's CLI"""
|
||||
|
||||
parser = ConfigOptionParser(
|
||||
usage="\n%prog <command> [options]",
|
||||
usage='\n%prog <command> [options]',
|
||||
add_help_option=False,
|
||||
formatter=UpdatingDefaultsHelpFormatter(),
|
||||
name="global",
|
||||
name='global',
|
||||
prog=get_prog(),
|
||||
)
|
||||
parser.disable_interspersed_args()
|
||||
|
|
@ -36,16 +41,16 @@ def create_main_parser() -> ConfigOptionParser:
|
|||
parser.main = True # type: ignore
|
||||
|
||||
# create command listing for description
|
||||
description = [""] + [
|
||||
f"{name:27} {command_info.summary}"
|
||||
description = [''] + [
|
||||
f'{name:27} {command_info.summary}'
|
||||
for name, command_info in commands_dict.items()
|
||||
]
|
||||
parser.description = "\n".join(description)
|
||||
parser.description = '\n'.join(description)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def parse_command(args: List[str]) -> Tuple[str, List[str]]:
|
||||
def parse_command(args: list[str]) -> tuple[str, list[str]]:
|
||||
parser = create_main_parser()
|
||||
|
||||
# Note: parser calls disable_interspersed_args(), so the result of this
|
||||
|
|
@ -64,7 +69,7 @@ def parse_command(args: List[str]) -> Tuple[str, List[str]]:
|
|||
sys.exit()
|
||||
|
||||
# pip || pip help -> print_help()
|
||||
if not args_else or (args_else[0] == "help" and len(args_else) == 1):
|
||||
if not args_else or (args_else[0] == 'help' and len(args_else) == 1):
|
||||
parser.print_help()
|
||||
sys.exit()
|
||||
|
||||
|
|
@ -78,7 +83,7 @@ def parse_command(args: List[str]) -> Tuple[str, List[str]]:
|
|||
if guess:
|
||||
msg.append(f'maybe you meant "{guess}"')
|
||||
|
||||
raise CommandError(" - ".join(msg))
|
||||
raise CommandError(' - '.join(msg))
|
||||
|
||||
# all the args without the subcommand
|
||||
cmd_args = args[:]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""Base option parser setup"""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import optparse
|
||||
|
|
@ -6,11 +7,17 @@ import shutil
|
|||
import sys
|
||||
import textwrap
|
||||
from contextlib import suppress
|
||||
from typing import Any, Dict, Iterator, List, Tuple
|
||||
from typing import Any
|
||||
from typing import Dict
|
||||
from typing import Iterator
|
||||
from typing import List
|
||||
from typing import Tuple
|
||||
|
||||
from pip._internal.cli.status_codes import UNKNOWN_ERROR
|
||||
from pip._internal.configuration import Configuration, ConfigurationError
|
||||
from pip._internal.utils.misc import redact_auth_from_url, strtobool
|
||||
from pip._internal.configuration import Configuration
|
||||
from pip._internal.configuration import ConfigurationError
|
||||
from pip._internal.utils.misc import redact_auth_from_url
|
||||
from pip._internal.utils.misc import strtobool
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -20,16 +27,16 @@ class PrettyHelpFormatter(optparse.IndentedHelpFormatter):
|
|||
|
||||
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||
# help position must be aligned with __init__.parseopts.description
|
||||
kwargs["max_help_position"] = 30
|
||||
kwargs["indent_increment"] = 1
|
||||
kwargs["width"] = shutil.get_terminal_size()[0] - 2
|
||||
kwargs['max_help_position'] = 30
|
||||
kwargs['indent_increment'] = 1
|
||||
kwargs['width'] = shutil.get_terminal_size()[0] - 2
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def format_option_strings(self, option: optparse.Option) -> str:
|
||||
return self._format_option_strings(option)
|
||||
|
||||
def _format_option_strings(
|
||||
self, option: optparse.Option, mvarfmt: str = " <{}>", optsep: str = ", "
|
||||
self, option: optparse.Option, mvarfmt: str = ' <{}>', optsep: str = ', ',
|
||||
) -> str:
|
||||
"""
|
||||
Return a comma-separated list of option strings and metavars.
|
||||
|
|
@ -52,49 +59,49 @@ class PrettyHelpFormatter(optparse.IndentedHelpFormatter):
|
|||
metavar = option.metavar or option.dest.lower()
|
||||
opts.append(mvarfmt.format(metavar.lower()))
|
||||
|
||||
return "".join(opts)
|
||||
return ''.join(opts)
|
||||
|
||||
def format_heading(self, heading: str) -> str:
|
||||
if heading == "Options":
|
||||
return ""
|
||||
return heading + ":\n"
|
||||
if heading == 'Options':
|
||||
return ''
|
||||
return heading + ':\n'
|
||||
|
||||
def format_usage(self, usage: str) -> str:
|
||||
"""
|
||||
Ensure there is only one newline between usage and the first heading
|
||||
if there is no description.
|
||||
"""
|
||||
msg = "\nUsage: {}\n".format(self.indent_lines(textwrap.dedent(usage), " "))
|
||||
msg = '\nUsage: {}\n'.format(self.indent_lines(textwrap.dedent(usage), ' '))
|
||||
return msg
|
||||
|
||||
def format_description(self, description: str) -> str:
|
||||
# leave full control over description to us
|
||||
if description:
|
||||
if hasattr(self.parser, "main"):
|
||||
label = "Commands"
|
||||
if hasattr(self.parser, 'main'):
|
||||
label = 'Commands'
|
||||
else:
|
||||
label = "Description"
|
||||
label = 'Description'
|
||||
# some doc strings have initial newlines, some don't
|
||||
description = description.lstrip("\n")
|
||||
description = description.lstrip('\n')
|
||||
# some doc strings have final newlines and spaces, some don't
|
||||
description = description.rstrip()
|
||||
# dedent, then reindent
|
||||
description = self.indent_lines(textwrap.dedent(description), " ")
|
||||
description = f"{label}:\n{description}\n"
|
||||
description = self.indent_lines(textwrap.dedent(description), ' ')
|
||||
description = f'{label}:\n{description}\n'
|
||||
return description
|
||||
else:
|
||||
return ""
|
||||
return ''
|
||||
|
||||
def format_epilog(self, epilog: str) -> str:
|
||||
# leave full control over epilog to us
|
||||
if epilog:
|
||||
return epilog
|
||||
else:
|
||||
return ""
|
||||
return ''
|
||||
|
||||
def indent_lines(self, text: str, indent: str) -> str:
|
||||
new_lines = [indent + line for line in text.split("\n")]
|
||||
return "\n".join(new_lines)
|
||||
new_lines = [indent + line for line in text.split('\n')]
|
||||
return '\n'.join(new_lines)
|
||||
|
||||
|
||||
class UpdatingDefaultsHelpFormatter(PrettyHelpFormatter):
|
||||
|
|
@ -115,7 +122,7 @@ class UpdatingDefaultsHelpFormatter(PrettyHelpFormatter):
|
|||
default_values = self.parser.defaults.get(option.dest)
|
||||
help_text = super().expand_default(option)
|
||||
|
||||
if default_values and option.metavar == "URL":
|
||||
if default_values and option.metavar == 'URL':
|
||||
if isinstance(default_values, str):
|
||||
default_values = [default_values]
|
||||
|
||||
|
|
@ -131,7 +138,7 @@ class UpdatingDefaultsHelpFormatter(PrettyHelpFormatter):
|
|||
|
||||
class CustomOptionParser(optparse.OptionParser):
|
||||
def insert_option_group(
|
||||
self, idx: int, *args: Any, **kwargs: Any
|
||||
self, idx: int, *args: Any, **kwargs: Any,
|
||||
) -> optparse.OptionGroup:
|
||||
"""Insert an OptionGroup at a given position."""
|
||||
group = self.add_option_group(*args, **kwargs)
|
||||
|
|
@ -142,7 +149,7 @@ class CustomOptionParser(optparse.OptionParser):
|
|||
return group
|
||||
|
||||
@property
|
||||
def option_list_all(self) -> List[optparse.Option]:
|
||||
def option_list_all(self) -> list[optparse.Option]:
|
||||
"""Get a list of all options, including those in option groups."""
|
||||
res = self.option_list[:]
|
||||
for i in self.option_groups:
|
||||
|
|
@ -172,15 +179,15 @@ class ConfigOptionParser(CustomOptionParser):
|
|||
try:
|
||||
return option.check_value(key, val)
|
||||
except optparse.OptionValueError as exc:
|
||||
print(f"An error occurred during configuration: {exc}")
|
||||
print(f'An error occurred during configuration: {exc}')
|
||||
sys.exit(3)
|
||||
|
||||
def _get_ordered_configuration_items(self) -> Iterator[Tuple[str, Any]]:
|
||||
def _get_ordered_configuration_items(self) -> Iterator[tuple[str, Any]]:
|
||||
# Configuration gives keys in an unordered manner. Order them.
|
||||
override_order = ["global", self.name, ":env:"]
|
||||
override_order = ['global', self.name, ':env:']
|
||||
|
||||
# Pool the options into different groups
|
||||
section_items: Dict[str, List[Tuple[str, Any]]] = {
|
||||
section_items: dict[str, list[tuple[str, Any]]] = {
|
||||
name: [] for name in override_order
|
||||
}
|
||||
for section_key, val in self.config.items():
|
||||
|
|
@ -192,7 +199,7 @@ class ConfigOptionParser(CustomOptionParser):
|
|||
)
|
||||
continue
|
||||
|
||||
section, key = section_key.split(".", 1)
|
||||
section, key = section_key.split('.', 1)
|
||||
if section in override_order:
|
||||
section_items[section].append((key, val))
|
||||
|
||||
|
|
@ -201,7 +208,7 @@ class ConfigOptionParser(CustomOptionParser):
|
|||
for key, val in section_items[section]:
|
||||
yield key, val
|
||||
|
||||
def _update_defaults(self, defaults: Dict[str, Any]) -> Dict[str, Any]:
|
||||
def _update_defaults(self, defaults: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Updates the given defaults with values from the config files and
|
||||
the environ. Does a little special handling for certain types of
|
||||
options (lists)."""
|
||||
|
|
@ -212,7 +219,7 @@ class ConfigOptionParser(CustomOptionParser):
|
|||
# Then set the options with those values
|
||||
for key, val in self._get_ordered_configuration_items():
|
||||
# '--' because configuration supports only long names
|
||||
option = self.get_option("--" + key)
|
||||
option = self.get_option('--' + key)
|
||||
|
||||
# Ignore options not present in this parser. E.g. non-globals put
|
||||
# in [global] by users that want them to apply to all applicable
|
||||
|
|
@ -222,31 +229,31 @@ class ConfigOptionParser(CustomOptionParser):
|
|||
|
||||
assert option.dest is not None
|
||||
|
||||
if option.action in ("store_true", "store_false"):
|
||||
if option.action in ('store_true', 'store_false'):
|
||||
try:
|
||||
val = strtobool(val)
|
||||
except ValueError:
|
||||
self.error(
|
||||
"{} is not a valid value for {} option, " # noqa
|
||||
"please specify a boolean value like yes/no, "
|
||||
"true/false or 1/0 instead.".format(val, key)
|
||||
'{} is not a valid value for {} option, ' # noqa
|
||||
'please specify a boolean value like yes/no, '
|
||||
'true/false or 1/0 instead.'.format(val, key),
|
||||
)
|
||||
elif option.action == "count":
|
||||
elif option.action == 'count':
|
||||
with suppress(ValueError):
|
||||
val = strtobool(val)
|
||||
with suppress(ValueError):
|
||||
val = int(val)
|
||||
if not isinstance(val, int) or val < 0:
|
||||
self.error(
|
||||
"{} is not a valid value for {} option, " # noqa
|
||||
"please instead specify either a non-negative integer "
|
||||
"or a boolean value like yes/no or false/true "
|
||||
"which is equivalent to 1/0.".format(val, key)
|
||||
'{} is not a valid value for {} option, ' # noqa
|
||||
'please instead specify either a non-negative integer '
|
||||
'or a boolean value like yes/no or false/true '
|
||||
'which is equivalent to 1/0.'.format(val, key),
|
||||
)
|
||||
elif option.action == "append":
|
||||
elif option.action == 'append':
|
||||
val = val.split()
|
||||
val = [self.check_default(option, key, v) for v in val]
|
||||
elif option.action == "callback":
|
||||
elif option.action == 'callback':
|
||||
assert option.callback is not None
|
||||
late_eval.add(option.dest)
|
||||
opt_str = option.get_opt_string()
|
||||
|
|
@ -289,4 +296,4 @@ class ConfigOptionParser(CustomOptionParser):
|
|||
|
||||
def error(self, msg: str) -> None:
|
||||
self.print_usage(sys.stderr)
|
||||
self.exit(UNKNOWN_ERROR, f"{msg}\n")
|
||||
self.exit(UNKNOWN_ERROR, f'{msg}\n')
|
||||
|
|
|
|||
|
|
@ -1,27 +1,34 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import functools
|
||||
import itertools
|
||||
import sys
|
||||
from signal import SIGINT, default_int_handler, signal
|
||||
from typing import Any, Callable, Iterator, Optional, Tuple
|
||||
|
||||
from pip._vendor.progress.bar import Bar, FillingCirclesBar, IncrementalBar
|
||||
from pip._vendor.progress.spinner import Spinner
|
||||
from pip._vendor.rich.progress import (
|
||||
BarColumn,
|
||||
DownloadColumn,
|
||||
FileSizeColumn,
|
||||
Progress,
|
||||
ProgressColumn,
|
||||
SpinnerColumn,
|
||||
TextColumn,
|
||||
TimeElapsedColumn,
|
||||
TimeRemainingColumn,
|
||||
TransferSpeedColumn,
|
||||
)
|
||||
from signal import default_int_handler
|
||||
from signal import SIGINT
|
||||
from signal import signal
|
||||
from typing import Any
|
||||
from typing import Callable
|
||||
from typing import Iterator
|
||||
from typing import Optional
|
||||
from typing import Tuple
|
||||
|
||||
from pip._internal.utils.compat import WINDOWS
|
||||
from pip._internal.utils.logging import get_indentation
|
||||
from pip._internal.utils.misc import format_size
|
||||
from pip._vendor.progress.bar import Bar
|
||||
from pip._vendor.progress.bar import FillingCirclesBar
|
||||
from pip._vendor.progress.bar import IncrementalBar
|
||||
from pip._vendor.progress.spinner import Spinner
|
||||
from pip._vendor.rich.progress import BarColumn
|
||||
from pip._vendor.rich.progress import DownloadColumn
|
||||
from pip._vendor.rich.progress import FileSizeColumn
|
||||
from pip._vendor.rich.progress import Progress
|
||||
from pip._vendor.rich.progress import ProgressColumn
|
||||
from pip._vendor.rich.progress import SpinnerColumn
|
||||
from pip._vendor.rich.progress import TextColumn
|
||||
from pip._vendor.rich.progress import TimeElapsedColumn
|
||||
from pip._vendor.rich.progress import TimeRemainingColumn
|
||||
from pip._vendor.rich.progress import TransferSpeedColumn
|
||||
|
||||
try:
|
||||
from pip._vendor import colorama
|
||||
|
|
@ -34,7 +41,7 @@ DownloadProgressRenderer = Callable[[Iterator[bytes]], Iterator[bytes]]
|
|||
|
||||
|
||||
def _select_progress_class(preferred: Bar, fallback: Bar) -> Bar:
|
||||
encoding = getattr(preferred.file, "encoding", None)
|
||||
encoding = getattr(preferred.file, 'encoding', None)
|
||||
|
||||
# If we don't know what encoding this file is in, then we'll just assume
|
||||
# that it doesn't support unicode and use the ASCII bar.
|
||||
|
|
@ -44,16 +51,16 @@ def _select_progress_class(preferred: Bar, fallback: Bar) -> Bar:
|
|||
# Collect all of the possible characters we want to use with the preferred
|
||||
# bar.
|
||||
characters = [
|
||||
getattr(preferred, "empty_fill", ""),
|
||||
getattr(preferred, "fill", ""),
|
||||
getattr(preferred, 'empty_fill', ''),
|
||||
getattr(preferred, 'fill', ''),
|
||||
]
|
||||
characters += list(getattr(preferred, "phases", []))
|
||||
characters += list(getattr(preferred, 'phases', []))
|
||||
|
||||
# Try to decode the characters we're using for the bar using the encoding
|
||||
# of the given file, if this works then we'll assume that we can use the
|
||||
# fancier bar and if not we'll fall back to the plaintext bar.
|
||||
try:
|
||||
"".join(characters).encode(encoding)
|
||||
''.join(characters).encode(encoding)
|
||||
except UnicodeEncodeError:
|
||||
return fallback
|
||||
else:
|
||||
|
|
@ -126,17 +133,17 @@ class SilentBar(Bar):
|
|||
|
||||
class BlueEmojiBar(IncrementalBar):
|
||||
|
||||
suffix = "%(percent)d%%"
|
||||
bar_prefix = " "
|
||||
bar_suffix = " "
|
||||
phases = ("\U0001F539", "\U0001F537", "\U0001F535")
|
||||
suffix = '%(percent)d%%'
|
||||
bar_prefix = ' '
|
||||
bar_suffix = ' '
|
||||
phases = ('\U0001F539', '\U0001F537', '\U0001F535')
|
||||
|
||||
|
||||
class DownloadProgressMixin:
|
||||
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||
# https://github.com/python/mypy/issues/5887
|
||||
super().__init__(*args, **kwargs) # type: ignore
|
||||
self.message: str = (" " * (get_indentation() + 2)) + self.message
|
||||
self.message: str = (' ' * (get_indentation() + 2)) + self.message
|
||||
|
||||
@property
|
||||
def downloaded(self) -> str:
|
||||
|
|
@ -146,14 +153,14 @@ class DownloadProgressMixin:
|
|||
def download_speed(self) -> str:
|
||||
# Avoid zero division errors...
|
||||
if self.avg == 0.0: # type: ignore
|
||||
return "..."
|
||||
return format_size(1 / self.avg) + "/s" # type: ignore
|
||||
return '...'
|
||||
return format_size(1 / self.avg) + '/s' # type: ignore
|
||||
|
||||
@property
|
||||
def pretty_eta(self) -> str:
|
||||
if self.eta: # type: ignore
|
||||
return f"eta {self.eta_td}" # type: ignore
|
||||
return ""
|
||||
return f'eta {self.eta_td}' # type: ignore
|
||||
return ''
|
||||
|
||||
def iter(self, it): # type: ignore
|
||||
for x in it:
|
||||
|
|
@ -196,8 +203,8 @@ class WindowsMixin:
|
|||
class BaseDownloadProgressBar(WindowsMixin, InterruptibleMixin, DownloadProgressMixin):
|
||||
|
||||
file = sys.stdout
|
||||
message = "%(percent)d%%"
|
||||
suffix = "%(downloaded)s %(download_speed)s %(pretty_eta)s"
|
||||
message = '%(percent)d%%'
|
||||
suffix = '%(downloaded)s %(download_speed)s %(pretty_eta)s'
|
||||
|
||||
|
||||
class DefaultDownloadProgressBar(BaseDownloadProgressBar, _BaseBar):
|
||||
|
|
@ -221,14 +228,14 @@ class DownloadBlueEmojiProgressBar(BaseDownloadProgressBar, BlueEmojiBar):
|
|||
|
||||
|
||||
class DownloadProgressSpinner(
|
||||
WindowsMixin, InterruptibleMixin, DownloadProgressMixin, Spinner
|
||||
WindowsMixin, InterruptibleMixin, DownloadProgressMixin, Spinner,
|
||||
):
|
||||
|
||||
file = sys.stdout
|
||||
suffix = "%(downloaded)s %(download_speed)s"
|
||||
suffix = '%(downloaded)s %(download_speed)s'
|
||||
|
||||
def next_phase(self) -> str:
|
||||
if not hasattr(self, "_phaser"):
|
||||
if not hasattr(self, '_phaser'):
|
||||
self._phaser = itertools.cycle(self.phases)
|
||||
return next(self._phaser)
|
||||
|
||||
|
|
@ -236,30 +243,30 @@ class DownloadProgressSpinner(
|
|||
message = self.message % self
|
||||
phase = self.next_phase()
|
||||
suffix = self.suffix % self
|
||||
line = "".join(
|
||||
line = ''.join(
|
||||
[
|
||||
message,
|
||||
" " if message else "",
|
||||
' ' if message else '',
|
||||
phase,
|
||||
" " if suffix else "",
|
||||
' ' if suffix else '',
|
||||
suffix,
|
||||
]
|
||||
],
|
||||
)
|
||||
|
||||
self.writeln(line)
|
||||
|
||||
|
||||
BAR_TYPES = {
|
||||
"off": (DownloadSilentBar, DownloadSilentBar),
|
||||
"on": (DefaultDownloadProgressBar, DownloadProgressSpinner),
|
||||
"ascii": (DownloadBar, DownloadProgressSpinner),
|
||||
"pretty": (DownloadFillingCirclesBar, DownloadProgressSpinner),
|
||||
"emoji": (DownloadBlueEmojiProgressBar, DownloadProgressSpinner),
|
||||
'off': (DownloadSilentBar, DownloadSilentBar),
|
||||
'on': (DefaultDownloadProgressBar, DownloadProgressSpinner),
|
||||
'ascii': (DownloadBar, DownloadProgressSpinner),
|
||||
'pretty': (DownloadFillingCirclesBar, DownloadProgressSpinner),
|
||||
'emoji': (DownloadBlueEmojiProgressBar, DownloadProgressSpinner),
|
||||
}
|
||||
|
||||
|
||||
def _legacy_progress_bar(
|
||||
progress_bar: str, max: Optional[int]
|
||||
progress_bar: str, max: int | None,
|
||||
) -> DownloadProgressRenderer:
|
||||
if max is None or max == 0:
|
||||
return BAR_TYPES[progress_bar][1]().iter # type: ignore
|
||||
|
|
@ -276,13 +283,13 @@ def _rich_progress_bar(
|
|||
bar_type: str,
|
||||
size: int,
|
||||
) -> Iterator[bytes]:
|
||||
assert bar_type == "on", "This should only be used in the default mode."
|
||||
assert bar_type == 'on', 'This should only be used in the default mode.'
|
||||
|
||||
if not size:
|
||||
total = float("inf")
|
||||
columns: Tuple[ProgressColumn, ...] = (
|
||||
TextColumn("[progress.description]{task.description}"),
|
||||
SpinnerColumn("line", speed=1.5),
|
||||
total = float('inf')
|
||||
columns: tuple[ProgressColumn, ...] = (
|
||||
TextColumn('[progress.description]{task.description}'),
|
||||
SpinnerColumn('line', speed=1.5),
|
||||
FileSizeColumn(),
|
||||
TransferSpeedColumn(),
|
||||
TimeElapsedColumn(),
|
||||
|
|
@ -290,16 +297,16 @@ def _rich_progress_bar(
|
|||
else:
|
||||
total = size
|
||||
columns = (
|
||||
TextColumn("[progress.description]{task.description}"),
|
||||
TextColumn('[progress.description]{task.description}'),
|
||||
BarColumn(),
|
||||
DownloadColumn(),
|
||||
TransferSpeedColumn(),
|
||||
TextColumn("eta"),
|
||||
TextColumn('eta'),
|
||||
TimeRemainingColumn(),
|
||||
)
|
||||
|
||||
progress = Progress(*columns, refresh_per_second=30)
|
||||
task_id = progress.add_task(" " * (get_indentation() + 2), total=total)
|
||||
task_id = progress.add_task(' ' * (get_indentation() + 2), total=total)
|
||||
with progress:
|
||||
for chunk in iterable:
|
||||
yield chunk
|
||||
|
|
@ -307,15 +314,15 @@ def _rich_progress_bar(
|
|||
|
||||
|
||||
def get_download_progress_renderer(
|
||||
*, bar_type: str, size: Optional[int] = None
|
||||
*, bar_type: str, size: int | None = None,
|
||||
) -> DownloadProgressRenderer:
|
||||
"""Get an object that can be used to render the download progress.
|
||||
|
||||
Returns a callable, that takes an iterable to "wrap".
|
||||
"""
|
||||
if bar_type == "on":
|
||||
if bar_type == 'on':
|
||||
return functools.partial(_rich_progress_bar, bar_type=bar_type, size=size)
|
||||
elif bar_type == "off":
|
||||
elif bar_type == 'off':
|
||||
return iter # no-op, when passed an iterator
|
||||
else:
|
||||
return _legacy_progress_bar(bar_type, size)
|
||||
|
|
|
|||
|
|
@ -4,42 +4,43 @@ The classes in this module are in a separate module so the commands not
|
|||
needing download / PackageFinder capability don't unnecessarily import the
|
||||
PackageFinder machinery and all its vendored dependencies, etc.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from functools import partial
|
||||
from optparse import Values
|
||||
from typing import Any, List, Optional, Tuple
|
||||
from typing import Any
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Tuple
|
||||
|
||||
from pip._internal.cache import WheelCache
|
||||
from pip._internal.cli import cmdoptions
|
||||
from pip._internal.cli.base_command import Command
|
||||
from pip._internal.cli.command_context import CommandContextMixIn
|
||||
from pip._internal.exceptions import CommandError, PreviousBuildDirError
|
||||
from pip._internal.exceptions import CommandError
|
||||
from pip._internal.exceptions import PreviousBuildDirError
|
||||
from pip._internal.index.collector import LinkCollector
|
||||
from pip._internal.index.package_finder import PackageFinder
|
||||
from pip._internal.models.selection_prefs import SelectionPreferences
|
||||
from pip._internal.models.target_python import TargetPython
|
||||
from pip._internal.network.session import PipSession
|
||||
from pip._internal.operations.prepare import RequirementPreparer
|
||||
from pip._internal.req.constructors import (
|
||||
install_req_from_editable,
|
||||
install_req_from_line,
|
||||
install_req_from_parsed_requirement,
|
||||
install_req_from_req_string,
|
||||
)
|
||||
from pip._internal.req.constructors import install_req_from_editable
|
||||
from pip._internal.req.constructors import install_req_from_line
|
||||
from pip._internal.req.constructors import install_req_from_parsed_requirement
|
||||
from pip._internal.req.constructors import install_req_from_req_string
|
||||
from pip._internal.req.req_file import parse_requirements
|
||||
from pip._internal.req.req_install import InstallRequirement
|
||||
from pip._internal.req.req_tracker import RequirementTracker
|
||||
from pip._internal.resolution.base import BaseResolver
|
||||
from pip._internal.self_outdated_check import pip_self_version_check
|
||||
from pip._internal.utils.deprecation import deprecated
|
||||
from pip._internal.utils.temp_dir import (
|
||||
TempDirectory,
|
||||
TempDirectoryTypeRegistry,
|
||||
tempdir_kinds,
|
||||
)
|
||||
from pip._internal.utils.temp_dir import tempdir_kinds
|
||||
from pip._internal.utils.temp_dir import TempDirectory
|
||||
from pip._internal.utils.temp_dir import TempDirectoryTypeRegistry
|
||||
from pip._internal.utils.virtualenv import running_under_virtualenv
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -53,17 +54,17 @@ class SessionCommandMixin(CommandContextMixIn):
|
|||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self._session: Optional[PipSession] = None
|
||||
self._session: PipSession | None = None
|
||||
|
||||
@classmethod
|
||||
def _get_index_urls(cls, options: Values) -> Optional[List[str]]:
|
||||
def _get_index_urls(cls, options: Values) -> list[str] | None:
|
||||
"""Return a list of index urls from user-provided options."""
|
||||
index_urls = []
|
||||
if not getattr(options, "no_index", False):
|
||||
url = getattr(options, "index_url", None)
|
||||
if not getattr(options, 'no_index', False):
|
||||
url = getattr(options, 'index_url', None)
|
||||
if url:
|
||||
index_urls.append(url)
|
||||
urls = getattr(options, "extra_index_urls", None)
|
||||
urls = getattr(options, 'extra_index_urls', None)
|
||||
if urls:
|
||||
index_urls.extend(urls)
|
||||
# Return None rather than an empty list
|
||||
|
|
@ -82,13 +83,13 @@ class SessionCommandMixin(CommandContextMixIn):
|
|||
def _build_session(
|
||||
self,
|
||||
options: Values,
|
||||
retries: Optional[int] = None,
|
||||
timeout: Optional[int] = None,
|
||||
retries: int | None = None,
|
||||
timeout: int | None = None,
|
||||
) -> PipSession:
|
||||
assert not options.cache_dir or os.path.isabs(options.cache_dir)
|
||||
session = PipSession(
|
||||
cache=(
|
||||
os.path.join(options.cache_dir, "http") if options.cache_dir else None
|
||||
os.path.join(options.cache_dir, 'http') if options.cache_dir else None
|
||||
),
|
||||
retries=retries if retries is not None else options.retries,
|
||||
trusted_hosts=options.trusted_hosts,
|
||||
|
|
@ -110,8 +111,8 @@ class SessionCommandMixin(CommandContextMixIn):
|
|||
# Handle configured proxies
|
||||
if options.proxy:
|
||||
session.proxies = {
|
||||
"http": options.proxy,
|
||||
"https": options.proxy,
|
||||
'http': options.proxy,
|
||||
'https': options.proxy,
|
||||
}
|
||||
|
||||
# Determine if we can prompt the user for authentication or not
|
||||
|
|
@ -135,14 +136,14 @@ class IndexGroupCommand(Command, SessionCommandMixin):
|
|||
This overrides the default behavior of not doing the check.
|
||||
"""
|
||||
# Make sure the index_group options are present.
|
||||
assert hasattr(options, "no_index")
|
||||
assert hasattr(options, 'no_index')
|
||||
|
||||
if options.disable_pip_version_check or options.no_index:
|
||||
return
|
||||
|
||||
# Otherwise, check if we're using the latest version of pip available.
|
||||
session = self._build_session(
|
||||
options, retries=0, timeout=min(5, options.timeout)
|
||||
options, retries=0, timeout=min(5, options.timeout),
|
||||
)
|
||||
with session:
|
||||
pip_self_version_check(session, options)
|
||||
|
|
@ -164,14 +165,14 @@ def warn_if_run_as_root() -> None:
|
|||
"""
|
||||
if running_under_virtualenv():
|
||||
return
|
||||
if not hasattr(os, "getuid"):
|
||||
if not hasattr(os, 'getuid'):
|
||||
return
|
||||
# On Windows, there are no "system managed" Python packages. Installing as
|
||||
# Administrator via pip is the correct way of updating system environments.
|
||||
#
|
||||
# We choose sys.platform over utils.compat.WINDOWS here to enable Mypy platform
|
||||
# checks: https://mypy.readthedocs.io/en/stable/common_issues.html
|
||||
if sys.platform == "win32" or sys.platform == "cygwin":
|
||||
if sys.platform == 'win32' or sys.platform == 'cygwin':
|
||||
return
|
||||
|
||||
if os.getuid() != 0:
|
||||
|
|
@ -179,9 +180,9 @@ def warn_if_run_as_root() -> None:
|
|||
|
||||
logger.warning(
|
||||
"Running pip as the 'root' user can result in broken permissions and "
|
||||
"conflicting behaviour with the system package manager. "
|
||||
"It is recommended to use a virtual environment instead: "
|
||||
"https://pip.pypa.io/warnings/venv"
|
||||
'conflicting behaviour with the system package manager. '
|
||||
'It is recommended to use a virtual environment instead: '
|
||||
'https://pip.pypa.io/warnings/venv',
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -195,8 +196,8 @@ def with_cleanup(func: Any) -> Any:
|
|||
registry.set_delete(t, False)
|
||||
|
||||
def wrapper(
|
||||
self: RequirementCommand, options: Values, args: List[Any]
|
||||
) -> Optional[int]:
|
||||
self: RequirementCommand, options: Values, args: list[Any],
|
||||
) -> int | None:
|
||||
assert self.tempdir_registry is not None
|
||||
if options.no_clean:
|
||||
configure_tempdir_registry(self.tempdir_registry)
|
||||
|
|
@ -222,30 +223,30 @@ class RequirementCommand(IndexGroupCommand):
|
|||
@staticmethod
|
||||
def determine_resolver_variant(options: Values) -> str:
|
||||
"""Determines which resolver should be used, based on the given options."""
|
||||
if "legacy-resolver" in options.deprecated_features_enabled:
|
||||
return "legacy"
|
||||
if 'legacy-resolver' in options.deprecated_features_enabled:
|
||||
return 'legacy'
|
||||
|
||||
return "2020-resolver"
|
||||
return '2020-resolver'
|
||||
|
||||
@staticmethod
|
||||
def determine_build_failure_suppression(options: Values) -> bool:
|
||||
"""Determines whether build failures should be suppressed and backtracked on."""
|
||||
if "backtrack-on-build-failures" not in options.deprecated_features_enabled:
|
||||
if 'backtrack-on-build-failures' not in options.deprecated_features_enabled:
|
||||
return False
|
||||
|
||||
if "legacy-resolver" in options.deprecated_features_enabled:
|
||||
raise CommandError("Cannot backtrack with legacy resolver.")
|
||||
if 'legacy-resolver' in options.deprecated_features_enabled:
|
||||
raise CommandError('Cannot backtrack with legacy resolver.')
|
||||
|
||||
deprecated(
|
||||
reason=(
|
||||
"Backtracking on build failures can mask issues related to how "
|
||||
"a package generates metadata or builds a wheel. This flag will "
|
||||
"be removed in pip 22.2."
|
||||
'Backtracking on build failures can mask issues related to how '
|
||||
'a package generates metadata or builds a wheel. This flag will '
|
||||
'be removed in pip 22.2.'
|
||||
),
|
||||
gone_in=None,
|
||||
replacement=(
|
||||
"avoiding known-bad versions by explicitly telling pip to ignore them "
|
||||
"(either directly as requirements, or via a constraints file)"
|
||||
'avoiding known-bad versions by explicitly telling pip to ignore them '
|
||||
'(either directly as requirements, or via a constraints file)'
|
||||
),
|
||||
feature_flag=None,
|
||||
issue=10655,
|
||||
|
|
@ -261,7 +262,7 @@ class RequirementCommand(IndexGroupCommand):
|
|||
session: PipSession,
|
||||
finder: PackageFinder,
|
||||
use_user_site: bool,
|
||||
download_dir: Optional[str] = None,
|
||||
download_dir: str | None = None,
|
||||
verbosity: int = 0,
|
||||
) -> RequirementPreparer:
|
||||
"""
|
||||
|
|
@ -271,42 +272,42 @@ class RequirementCommand(IndexGroupCommand):
|
|||
assert temp_build_dir_path is not None
|
||||
|
||||
resolver_variant = cls.determine_resolver_variant(options)
|
||||
if resolver_variant == "2020-resolver":
|
||||
lazy_wheel = "fast-deps" in options.features_enabled
|
||||
if resolver_variant == '2020-resolver':
|
||||
lazy_wheel = 'fast-deps' in options.features_enabled
|
||||
if lazy_wheel:
|
||||
logger.warning(
|
||||
"pip is using lazily downloaded wheels using HTTP "
|
||||
"range requests to obtain dependency information. "
|
||||
"This experimental feature is enabled through "
|
||||
"--use-feature=fast-deps and it is not ready for "
|
||||
"production."
|
||||
'pip is using lazily downloaded wheels using HTTP '
|
||||
'range requests to obtain dependency information. '
|
||||
'This experimental feature is enabled through '
|
||||
'--use-feature=fast-deps and it is not ready for '
|
||||
'production.',
|
||||
)
|
||||
else:
|
||||
lazy_wheel = False
|
||||
if "fast-deps" in options.features_enabled:
|
||||
if 'fast-deps' in options.features_enabled:
|
||||
logger.warning(
|
||||
"fast-deps has no effect when used with the legacy resolver."
|
||||
'fast-deps has no effect when used with the legacy resolver.',
|
||||
)
|
||||
|
||||
in_tree_build = "out-of-tree-build" not in options.deprecated_features_enabled
|
||||
if "in-tree-build" in options.features_enabled:
|
||||
in_tree_build = 'out-of-tree-build' not in options.deprecated_features_enabled
|
||||
if 'in-tree-build' in options.features_enabled:
|
||||
deprecated(
|
||||
reason="In-tree builds are now the default.",
|
||||
replacement="to remove the --use-feature=in-tree-build flag",
|
||||
gone_in="22.1",
|
||||
reason='In-tree builds are now the default.',
|
||||
replacement='to remove the --use-feature=in-tree-build flag',
|
||||
gone_in='22.1',
|
||||
)
|
||||
if "out-of-tree-build" in options.deprecated_features_enabled:
|
||||
if 'out-of-tree-build' in options.deprecated_features_enabled:
|
||||
deprecated(
|
||||
reason="Out-of-tree builds are deprecated.",
|
||||
reason='Out-of-tree builds are deprecated.',
|
||||
replacement=None,
|
||||
gone_in="22.1",
|
||||
gone_in='22.1',
|
||||
)
|
||||
|
||||
if options.progress_bar not in {"on", "off"}:
|
||||
if options.progress_bar not in {'on', 'off'}:
|
||||
deprecated(
|
||||
reason="Custom progress bar styles are deprecated",
|
||||
replacement="to use the default progress bar style.",
|
||||
gone_in="22.1",
|
||||
reason='Custom progress bar styles are deprecated',
|
||||
replacement='to use the default progress bar style.',
|
||||
gone_in='22.1',
|
||||
)
|
||||
|
||||
return RequirementPreparer(
|
||||
|
|
@ -331,14 +332,14 @@ class RequirementCommand(IndexGroupCommand):
|
|||
preparer: RequirementPreparer,
|
||||
finder: PackageFinder,
|
||||
options: Values,
|
||||
wheel_cache: Optional[WheelCache] = None,
|
||||
wheel_cache: WheelCache | None = None,
|
||||
use_user_site: bool = False,
|
||||
ignore_installed: bool = True,
|
||||
ignore_requires_python: bool = False,
|
||||
force_reinstall: bool = False,
|
||||
upgrade_strategy: str = "to-satisfy-only",
|
||||
use_pep517: Optional[bool] = None,
|
||||
py_version_info: Optional[Tuple[int, ...]] = None,
|
||||
upgrade_strategy: str = 'to-satisfy-only',
|
||||
use_pep517: bool | None = None,
|
||||
py_version_info: tuple[int, ...] | None = None,
|
||||
) -> BaseResolver:
|
||||
"""
|
||||
Create a Resolver instance for the given parameters.
|
||||
|
|
@ -353,7 +354,7 @@ class RequirementCommand(IndexGroupCommand):
|
|||
# The long import name and duplicated invocation is needed to convince
|
||||
# Mypy into correctly typechecking. Otherwise it would complain the
|
||||
# "Resolver" class being redefined.
|
||||
if resolver_variant == "2020-resolver":
|
||||
if resolver_variant == '2020-resolver':
|
||||
import pip._internal.resolution.resolvelib.resolver
|
||||
|
||||
return pip._internal.resolution.resolvelib.resolver.Resolver(
|
||||
|
|
@ -388,15 +389,15 @@ class RequirementCommand(IndexGroupCommand):
|
|||
|
||||
def get_requirements(
|
||||
self,
|
||||
args: List[str],
|
||||
args: list[str],
|
||||
options: Values,
|
||||
finder: PackageFinder,
|
||||
session: PipSession,
|
||||
) -> List[InstallRequirement]:
|
||||
) -> list[InstallRequirement]:
|
||||
"""
|
||||
Parse command-line arguments into the corresponding requirements.
|
||||
"""
|
||||
requirements: List[InstallRequirement] = []
|
||||
requirements: list[InstallRequirement] = []
|
||||
for filename in options.constraints:
|
||||
for parsed_req in parse_requirements(
|
||||
filename,
|
||||
|
|
@ -434,7 +435,7 @@ class RequirementCommand(IndexGroupCommand):
|
|||
# NOTE: options.require_hashes may be set if --require-hashes is True
|
||||
for filename in options.requirements:
|
||||
for parsed_req in parse_requirements(
|
||||
filename, finder=finder, options=options, session=session
|
||||
filename, finder=finder, options=options, session=session,
|
||||
):
|
||||
req_to_add = install_req_from_parsed_requirement(
|
||||
parsed_req,
|
||||
|
|
@ -449,18 +450,18 @@ class RequirementCommand(IndexGroupCommand):
|
|||
options.require_hashes = True
|
||||
|
||||
if not (args or options.editables or options.requirements):
|
||||
opts = {"name": self.name}
|
||||
opts = {'name': self.name}
|
||||
if options.find_links:
|
||||
raise CommandError(
|
||||
"You must give at least one requirement to {name} "
|
||||
'You must give at least one requirement to {name} '
|
||||
'(maybe you meant "pip {name} {links}"?)'.format(
|
||||
**dict(opts, links=" ".join(options.find_links))
|
||||
)
|
||||
**dict(opts, links=' '.join(options.find_links)),
|
||||
),
|
||||
)
|
||||
else:
|
||||
raise CommandError(
|
||||
"You must give at least one requirement to {name} "
|
||||
'(see "pip help {name}")'.format(**opts)
|
||||
'You must give at least one requirement to {name} '
|
||||
'(see "pip help {name}")'.format(**opts),
|
||||
)
|
||||
|
||||
return requirements
|
||||
|
|
@ -480,8 +481,8 @@ class RequirementCommand(IndexGroupCommand):
|
|||
self,
|
||||
options: Values,
|
||||
session: PipSession,
|
||||
target_python: Optional[TargetPython] = None,
|
||||
ignore_requires_python: Optional[bool] = None,
|
||||
target_python: TargetPython | None = None,
|
||||
ignore_requires_python: bool | None = None,
|
||||
) -> PackageFinder:
|
||||
"""
|
||||
Create a package finder appropriate to this requirement command.
|
||||
|
|
@ -502,5 +503,5 @@ class RequirementCommand(IndexGroupCommand):
|
|||
link_collector=link_collector,
|
||||
selection_prefs=selection_prefs,
|
||||
target_python=target_python,
|
||||
use_deprecated_html5lib="html5lib" in options.deprecated_features_enabled,
|
||||
use_deprecated_html5lib='html5lib' in options.deprecated_features_enabled,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import itertools
|
||||
import logging
|
||||
import sys
|
||||
import time
|
||||
from typing import IO, Iterator
|
||||
|
||||
from pip._vendor.progress import HIDE_CURSOR, SHOW_CURSOR
|
||||
from typing import IO
|
||||
from typing import Iterator
|
||||
|
||||
from pip._internal.utils.compat import WINDOWS
|
||||
from pip._internal.utils.logging import get_indentation
|
||||
from pip._vendor.progress import HIDE_CURSOR
|
||||
from pip._vendor.progress import SHOW_CURSOR
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -26,7 +29,7 @@ class InteractiveSpinner(SpinnerInterface):
|
|||
self,
|
||||
message: str,
|
||||
file: IO[str] = None,
|
||||
spin_chars: str = "-\\|/",
|
||||
spin_chars: str = '-\\|/',
|
||||
# Empirically, 8 updates/second looks nice
|
||||
min_update_interval_seconds: float = 0.125,
|
||||
):
|
||||
|
|
@ -39,15 +42,15 @@ class InteractiveSpinner(SpinnerInterface):
|
|||
|
||||
self._spin_cycle = itertools.cycle(spin_chars)
|
||||
|
||||
self._file.write(" " * get_indentation() + self._message + " ... ")
|
||||
self._file.write(' ' * get_indentation() + self._message + ' ... ')
|
||||
self._width = 0
|
||||
|
||||
def _write(self, status: str) -> None:
|
||||
assert not self._finished
|
||||
# Erase what we wrote before by backspacing to the beginning, writing
|
||||
# spaces to overwrite the old text, and then backspacing again
|
||||
backup = "\b" * self._width
|
||||
self._file.write(backup + " " * self._width + backup)
|
||||
backup = '\b' * self._width
|
||||
self._file.write(backup + ' ' * self._width + backup)
|
||||
# Now we have a blank slate to add our status
|
||||
self._file.write(status)
|
||||
self._width = len(status)
|
||||
|
|
@ -65,7 +68,7 @@ class InteractiveSpinner(SpinnerInterface):
|
|||
if self._finished:
|
||||
return
|
||||
self._write(final_status)
|
||||
self._file.write("\n")
|
||||
self._file.write('\n')
|
||||
self._file.flush()
|
||||
self._finished = True
|
||||
|
||||
|
|
@ -79,19 +82,19 @@ class NonInteractiveSpinner(SpinnerInterface):
|
|||
self._message = message
|
||||
self._finished = False
|
||||
self._rate_limiter = RateLimiter(min_update_interval_seconds)
|
||||
self._update("started")
|
||||
self._update('started')
|
||||
|
||||
def _update(self, status: str) -> None:
|
||||
assert not self._finished
|
||||
self._rate_limiter.reset()
|
||||
logger.info("%s: %s", self._message, status)
|
||||
logger.info('%s: %s', self._message, status)
|
||||
|
||||
def spin(self) -> None:
|
||||
if self._finished:
|
||||
return
|
||||
if not self._rate_limiter.ready():
|
||||
return
|
||||
self._update("still running...")
|
||||
self._update('still running...')
|
||||
|
||||
def finish(self, final_status: str) -> None:
|
||||
if self._finished:
|
||||
|
|
@ -129,13 +132,13 @@ def open_spinner(message: str) -> Iterator[SpinnerInterface]:
|
|||
with hidden_cursor(sys.stdout):
|
||||
yield spinner
|
||||
except KeyboardInterrupt:
|
||||
spinner.finish("canceled")
|
||||
spinner.finish('canceled')
|
||||
raise
|
||||
except Exception:
|
||||
spinner.finish("error")
|
||||
spinner.finish('error')
|
||||
raise
|
||||
else:
|
||||
spinner.finish("done")
|
||||
spinner.finish('done')
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from __future__ import annotations
|
||||
SUCCESS = 0
|
||||
ERROR = 1
|
||||
UNKNOWN_ERROR = 2
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue