[pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci
This commit is contained in:
pre-commit-ci[bot] 2024-04-13 00:00:18 +00:00
parent 72ad6dc953
commit f4cd1ba0d6
813 changed files with 66015 additions and 58839 deletions

View file

@ -1,14 +1,17 @@
"""
Package containing all pip commands
"""
from __future__ import annotations
import importlib
from collections import namedtuple
from typing import Any, Dict, Optional
from typing import Any
from typing import Dict
from typing import Optional
from pip._internal.cli.base_command import Command
CommandInfo = namedtuple("CommandInfo", "module_path, class_name, summary")
CommandInfo = namedtuple('CommandInfo', 'module_path, class_name, summary')
# This dictionary does a bunch of heavy lifting for help output:
# - Enables avoiding additional (costly) imports for presenting `--help`.
@ -17,86 +20,86 @@ CommandInfo = namedtuple("CommandInfo", "module_path, class_name, summary")
# Even though the module path starts with the same "pip._internal.commands"
# prefix, the full path makes testing easier (specifically when modifying
# `commands_dict` in test setup / teardown).
commands_dict: Dict[str, CommandInfo] = {
"install": CommandInfo(
"pip._internal.commands.install",
"InstallCommand",
"Install packages.",
commands_dict: dict[str, CommandInfo] = {
'install': CommandInfo(
'pip._internal.commands.install',
'InstallCommand',
'Install packages.',
),
"download": CommandInfo(
"pip._internal.commands.download",
"DownloadCommand",
"Download packages.",
'download': CommandInfo(
'pip._internal.commands.download',
'DownloadCommand',
'Download packages.',
),
"uninstall": CommandInfo(
"pip._internal.commands.uninstall",
"UninstallCommand",
"Uninstall packages.",
'uninstall': CommandInfo(
'pip._internal.commands.uninstall',
'UninstallCommand',
'Uninstall packages.',
),
"freeze": CommandInfo(
"pip._internal.commands.freeze",
"FreezeCommand",
"Output installed packages in requirements format.",
'freeze': CommandInfo(
'pip._internal.commands.freeze',
'FreezeCommand',
'Output installed packages in requirements format.',
),
"list": CommandInfo(
"pip._internal.commands.list",
"ListCommand",
"List installed packages.",
'list': CommandInfo(
'pip._internal.commands.list',
'ListCommand',
'List installed packages.',
),
"show": CommandInfo(
"pip._internal.commands.show",
"ShowCommand",
"Show information about installed packages.",
'show': CommandInfo(
'pip._internal.commands.show',
'ShowCommand',
'Show information about installed packages.',
),
"check": CommandInfo(
"pip._internal.commands.check",
"CheckCommand",
"Verify installed packages have compatible dependencies.",
'check': CommandInfo(
'pip._internal.commands.check',
'CheckCommand',
'Verify installed packages have compatible dependencies.',
),
"config": CommandInfo(
"pip._internal.commands.configuration",
"ConfigurationCommand",
"Manage local and global configuration.",
'config': CommandInfo(
'pip._internal.commands.configuration',
'ConfigurationCommand',
'Manage local and global configuration.',
),
"search": CommandInfo(
"pip._internal.commands.search",
"SearchCommand",
"Search PyPI for packages.",
'search': CommandInfo(
'pip._internal.commands.search',
'SearchCommand',
'Search PyPI for packages.',
),
"cache": CommandInfo(
"pip._internal.commands.cache",
"CacheCommand",
'cache': CommandInfo(
'pip._internal.commands.cache',
'CacheCommand',
"Inspect and manage pip's wheel cache.",
),
"index": CommandInfo(
"pip._internal.commands.index",
"IndexCommand",
"Inspect information available from package indexes.",
'index': CommandInfo(
'pip._internal.commands.index',
'IndexCommand',
'Inspect information available from package indexes.',
),
"wheel": CommandInfo(
"pip._internal.commands.wheel",
"WheelCommand",
"Build wheels from your requirements.",
'wheel': CommandInfo(
'pip._internal.commands.wheel',
'WheelCommand',
'Build wheels from your requirements.',
),
"hash": CommandInfo(
"pip._internal.commands.hash",
"HashCommand",
"Compute hashes of package archives.",
'hash': CommandInfo(
'pip._internal.commands.hash',
'HashCommand',
'Compute hashes of package archives.',
),
"completion": CommandInfo(
"pip._internal.commands.completion",
"CompletionCommand",
"A helper command used for command completion.",
'completion': CommandInfo(
'pip._internal.commands.completion',
'CompletionCommand',
'A helper command used for command completion.',
),
"debug": CommandInfo(
"pip._internal.commands.debug",
"DebugCommand",
"Show information useful for debugging.",
'debug': CommandInfo(
'pip._internal.commands.debug',
'DebugCommand',
'Show information useful for debugging.',
),
"help": CommandInfo(
"pip._internal.commands.help",
"HelpCommand",
"Show help for commands.",
'help': CommandInfo(
'pip._internal.commands.help',
'HelpCommand',
'Show help for commands.',
),
}
@ -113,7 +116,7 @@ def create_command(name: str, **kwargs: Any) -> Command:
return command
def get_similar_commands(name: str) -> Optional[str]:
def get_similar_commands(name: str) -> str | None:
"""Command name auto-correct."""
from difflib import get_close_matches

View file

@ -1,12 +1,17 @@
from __future__ import annotations
import os
import textwrap
from optparse import Values
from typing import Any, List
from typing import Any
from typing import List
import pip._internal.utils.filesystem as filesystem
from pip._internal.cli.base_command import Command
from pip._internal.cli.status_codes import ERROR, SUCCESS
from pip._internal.exceptions import CommandError, PipError
from pip._internal.cli.status_codes import ERROR
from pip._internal.cli.status_codes import SUCCESS
from pip._internal.exceptions import CommandError
from pip._internal.exceptions import PipError
from pip._internal.utils.logging import getLogger
logger = getLogger(__name__)
@ -39,34 +44,34 @@ class CacheCommand(Command):
def add_options(self) -> None:
self.cmd_opts.add_option(
"--format",
action="store",
dest="list_format",
default="human",
choices=("human", "abspath"),
help="Select the output format among: human (default) or abspath",
'--format',
action='store',
dest='list_format',
default='human',
choices=('human', 'abspath'),
help='Select the output format among: human (default) or abspath',
)
self.parser.insert_option_group(0, self.cmd_opts)
def run(self, options: Values, args: List[str]) -> int:
def run(self, options: Values, args: list[str]) -> int:
handlers = {
"dir": self.get_cache_dir,
"info": self.get_cache_info,
"list": self.list_cache_items,
"remove": self.remove_cache_items,
"purge": self.purge_cache,
'dir': self.get_cache_dir,
'info': self.get_cache_info,
'list': self.list_cache_items,
'remove': self.remove_cache_items,
'purge': self.purge_cache,
}
if not options.cache_dir:
logger.error("pip cache commands can not function since cache is disabled.")
logger.error('pip cache commands can not function since cache is disabled.')
return ERROR
# Determine action
if not args or args[0] not in handlers:
logger.error(
"Need an action (%s) to perform.",
", ".join(sorted(handlers)),
'Need an action (%s) to perform.',
', '.join(sorted(handlers)),
)
return ERROR
@ -81,21 +86,21 @@ class CacheCommand(Command):
return SUCCESS
def get_cache_dir(self, options: Values, args: List[Any]) -> None:
def get_cache_dir(self, options: Values, args: list[Any]) -> None:
if args:
raise CommandError("Too many arguments")
raise CommandError('Too many arguments')
logger.info(options.cache_dir)
def get_cache_info(self, options: Values, args: List[Any]) -> None:
def get_cache_info(self, options: Values, args: list[Any]) -> None:
if args:
raise CommandError("Too many arguments")
raise CommandError('Too many arguments')
num_http_files = len(self._find_http_files(options))
num_packages = len(self._find_wheels(options, "*"))
num_packages = len(self._find_wheels(options, '*'))
http_cache_location = self._cache_dir(options, "http")
wheels_cache_location = self._cache_dir(options, "wheels")
http_cache_location = self._cache_dir(options, 'http')
wheels_cache_location = self._cache_dir(options, 'wheels')
http_cache_size = filesystem.format_directory_size(http_cache_location)
wheels_cache_size = filesystem.format_directory_size(wheels_cache_location)
@ -108,7 +113,7 @@ class CacheCommand(Command):
Wheels location: {wheels_cache_location}
Wheels size: {wheels_cache_size}
Number of wheels: {package_count}
"""
""",
)
.format(
http_cache_location=http_cache_location,
@ -123,35 +128,35 @@ class CacheCommand(Command):
logger.info(message)
def list_cache_items(self, options: Values, args: List[Any]) -> None:
def list_cache_items(self, options: Values, args: list[Any]) -> None:
if len(args) > 1:
raise CommandError("Too many arguments")
raise CommandError('Too many arguments')
if args:
pattern = args[0]
else:
pattern = "*"
pattern = '*'
files = self._find_wheels(options, pattern)
if options.list_format == "human":
if options.list_format == 'human':
self.format_for_human(files)
else:
self.format_for_abspath(files)
def format_for_human(self, files: List[str]) -> None:
def format_for_human(self, files: list[str]) -> None:
if not files:
logger.info("Nothing cached.")
logger.info('Nothing cached.')
return
results = []
for filename in files:
wheel = os.path.basename(filename)
size = filesystem.format_file_size(filename)
results.append(f" - {wheel} ({size})")
logger.info("Cache contents:\n")
logger.info("\n".join(sorted(results)))
results.append(f' - {wheel} ({size})')
logger.info('Cache contents:\n')
logger.info('\n'.join(sorted(results)))
def format_for_abspath(self, files: List[str]) -> None:
def format_for_abspath(self, files: list[str]) -> None:
if not files:
return
@ -159,48 +164,48 @@ class CacheCommand(Command):
for filename in files:
results.append(filename)
logger.info("\n".join(sorted(results)))
logger.info('\n'.join(sorted(results)))
def remove_cache_items(self, options: Values, args: List[Any]) -> None:
def remove_cache_items(self, options: Values, args: list[Any]) -> None:
if len(args) > 1:
raise CommandError("Too many arguments")
raise CommandError('Too many arguments')
if not args:
raise CommandError("Please provide a pattern")
raise CommandError('Please provide a pattern')
files = self._find_wheels(options, args[0])
no_matching_msg = "No matching packages"
if args[0] == "*":
no_matching_msg = 'No matching packages'
if args[0] == '*':
# Only fetch http files if no specific pattern given
files += self._find_http_files(options)
else:
# Add the pattern to the log message
no_matching_msg += ' for pattern "{}"'.format(args[0])
no_matching_msg += f' for pattern "{args[0]}"'
if not files:
logger.warning(no_matching_msg)
for filename in files:
os.unlink(filename)
logger.verbose("Removed %s", filename)
logger.info("Files removed: %s", len(files))
logger.verbose('Removed %s', filename)
logger.info('Files removed: %s', len(files))
def purge_cache(self, options: Values, args: List[Any]) -> None:
def purge_cache(self, options: Values, args: list[Any]) -> None:
if args:
raise CommandError("Too many arguments")
raise CommandError('Too many arguments')
return self.remove_cache_items(options, ["*"])
return self.remove_cache_items(options, ['*'])
def _cache_dir(self, options: Values, subdir: str) -> str:
return os.path.join(options.cache_dir, subdir)
def _find_http_files(self, options: Values) -> List[str]:
http_dir = self._cache_dir(options, "http")
return filesystem.find_files(http_dir, "*")
def _find_http_files(self, options: Values) -> list[str]:
http_dir = self._cache_dir(options, 'http')
return filesystem.find_files(http_dir, '*')
def _find_wheels(self, options: Values, pattern: str) -> List[str]:
wheel_dir = self._cache_dir(options, "wheels")
def _find_wheels(self, options: Values, pattern: str) -> list[str]:
wheel_dir = self._cache_dir(options, 'wheels')
# The wheel filename format, as specified in PEP 427, is:
# {distribution}-{version}(-{build})?-{python}-{abi}-{platform}.whl
@ -218,6 +223,6 @@ class CacheCommand(Command):
# match the hyphen before the version, followed by anything else.
#
# PEP 427: https://www.python.org/dev/peps/pep-0427/
pattern = pattern + ("*.whl" if "-" in pattern else "-*.whl")
pattern = pattern + ('*.whl' if '-' in pattern else '-*.whl')
return filesystem.find_files(wheel_dir, pattern)

View file

@ -1,13 +1,14 @@
from __future__ import annotations
import logging
from optparse import Values
from typing import List
from pip._internal.cli.base_command import Command
from pip._internal.cli.status_codes import ERROR, SUCCESS
from pip._internal.operations.check import (
check_package_set,
create_package_set_from_installed,
)
from pip._internal.cli.status_codes import ERROR
from pip._internal.cli.status_codes import SUCCESS
from pip._internal.operations.check import check_package_set
from pip._internal.operations.check import create_package_set_from_installed
from pip._internal.utils.misc import write_output
logger = logging.getLogger(__name__)
@ -19,7 +20,7 @@ class CheckCommand(Command):
usage = """
%prog [options]"""
def run(self, options: Values, args: List[str]) -> int:
def run(self, options: Values, args: list[str]) -> int:
package_set, parsing_probs = create_package_set_from_installed()
missing, conflicting = check_package_set(package_set)
@ -28,7 +29,7 @@ class CheckCommand(Command):
version = package_set[project_name].version
for dependency in missing[project_name]:
write_output(
"%s %s requires %s, which is not installed.",
'%s %s requires %s, which is not installed.',
project_name,
version,
dependency[0],
@ -38,7 +39,7 @@ class CheckCommand(Command):
version = package_set[project_name].version
for dep_name, dep_version, req in conflicting[project_name]:
write_output(
"%s %s has requirement %s, but you have %s %s.",
'%s %s has requirement %s, but you have %s %s.',
project_name,
version,
req,
@ -49,5 +50,5 @@ class CheckCommand(Command):
if missing or conflicting or parsing_probs:
return ERROR
else:
write_output("No broken requirements found.")
write_output('No broken requirements found.')
return SUCCESS

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import sys
import textwrap
from optparse import Values
@ -12,7 +14,7 @@ BASE_COMPLETION = """
"""
COMPLETION_SCRIPTS = {
"bash": """
'bash': """
_pip_completion()
{{
COMPREPLY=( $( COMP_WORDS="${{COMP_WORDS[*]}}" \\
@ -21,7 +23,7 @@ COMPLETION_SCRIPTS = {
}}
complete -o default -F _pip_completion {prog}
""",
"zsh": """
'zsh': """
function _pip_completion {{
local words cword
read -Ac words
@ -32,7 +34,7 @@ COMPLETION_SCRIPTS = {
}}
compctl -K _pip_completion {prog}
""",
"fish": """
'fish': """
function __fish_complete_pip
set -lx COMP_WORDS (commandline -o) ""
set -lx COMP_CWORD ( \\
@ -53,44 +55,44 @@ class CompletionCommand(Command):
def add_options(self) -> None:
self.cmd_opts.add_option(
"--bash",
"-b",
action="store_const",
const="bash",
dest="shell",
help="Emit completion code for bash",
'--bash',
'-b',
action='store_const',
const='bash',
dest='shell',
help='Emit completion code for bash',
)
self.cmd_opts.add_option(
"--zsh",
"-z",
action="store_const",
const="zsh",
dest="shell",
help="Emit completion code for zsh",
'--zsh',
'-z',
action='store_const',
const='zsh',
dest='shell',
help='Emit completion code for zsh',
)
self.cmd_opts.add_option(
"--fish",
"-f",
action="store_const",
const="fish",
dest="shell",
help="Emit completion code for fish",
'--fish',
'-f',
action='store_const',
const='fish',
dest='shell',
help='Emit completion code for fish',
)
self.parser.insert_option_group(0, self.cmd_opts)
def run(self, options: Values, args: List[str]) -> int:
def run(self, options: Values, args: list[str]) -> int:
"""Prints the completion code of the given shell"""
shells = COMPLETION_SCRIPTS.keys()
shell_options = ["--" + shell for shell in sorted(shells)]
shell_options = ['--' + shell for shell in sorted(shells)]
if options.shell in shells:
script = textwrap.dedent(
COMPLETION_SCRIPTS.get(options.shell, "").format(prog=get_prog())
COMPLETION_SCRIPTS.get(options.shell, '').format(prog=get_prog()),
)
print(BASE_COMPLETION.format(script=script, shell=options.shell))
return SUCCESS
else:
sys.stderr.write(
"ERROR: You must pass {}\n".format(" or ".join(shell_options))
'ERROR: You must pass {}\n'.format(' or '.join(shell_options)),
)
return SUCCESS

View file

@ -1,20 +1,24 @@
from __future__ import annotations
import logging
import os
import subprocess
from optparse import Values
from typing import Any, List, Optional
from typing import Any
from typing import List
from typing import Optional
from pip._internal.cli.base_command import Command
from pip._internal.cli.status_codes import ERROR, SUCCESS
from pip._internal.configuration import (
Configuration,
Kind,
get_configuration_files,
kinds,
)
from pip._internal.cli.status_codes import ERROR
from pip._internal.cli.status_codes import SUCCESS
from pip._internal.configuration import Configuration
from pip._internal.configuration import get_configuration_files
from pip._internal.configuration import Kind
from pip._internal.configuration import kinds
from pip._internal.exceptions import PipError
from pip._internal.utils.logging import indent_log
from pip._internal.utils.misc import get_prog, write_output
from pip._internal.utils.misc import get_prog
from pip._internal.utils.misc import write_output
logger = logging.getLogger(__name__)
@ -51,57 +55,57 @@ class ConfigurationCommand(Command):
def add_options(self) -> None:
self.cmd_opts.add_option(
"--editor",
dest="editor",
action="store",
'--editor',
dest='editor',
action='store',
default=None,
help=(
"Editor to use to edit the file. Uses VISUAL or EDITOR "
"environment variables if not provided."
'Editor to use to edit the file. Uses VISUAL or EDITOR '
'environment variables if not provided.'
),
)
self.cmd_opts.add_option(
"--global",
dest="global_file",
action="store_true",
'--global',
dest='global_file',
action='store_true',
default=False,
help="Use the system-wide configuration file only",
help='Use the system-wide configuration file only',
)
self.cmd_opts.add_option(
"--user",
dest="user_file",
action="store_true",
'--user',
dest='user_file',
action='store_true',
default=False,
help="Use the user configuration file only",
help='Use the user configuration file only',
)
self.cmd_opts.add_option(
"--site",
dest="site_file",
action="store_true",
'--site',
dest='site_file',
action='store_true',
default=False,
help="Use the current environment configuration file only",
help='Use the current environment configuration file only',
)
self.parser.insert_option_group(0, self.cmd_opts)
def run(self, options: Values, args: List[str]) -> int:
def run(self, options: Values, args: list[str]) -> int:
handlers = {
"list": self.list_values,
"edit": self.open_in_editor,
"get": self.get_name,
"set": self.set_name_value,
"unset": self.unset_name,
"debug": self.list_config_values,
'list': self.list_values,
'edit': self.open_in_editor,
'get': self.get_name,
'set': self.set_name_value,
'unset': self.unset_name,
'debug': self.list_config_values,
}
# Determine action
if not args or args[0] not in handlers:
logger.error(
"Need an action (%s) to perform.",
", ".join(sorted(handlers)),
'Need an action (%s) to perform.',
', '.join(sorted(handlers)),
)
return ERROR
@ -111,7 +115,7 @@ class ConfigurationCommand(Command):
# Depends on whether the command is modifying.
try:
load_only = self._determine_file(
options, need_value=(action in ["get", "set", "unset", "edit"])
options, need_value=(action in ['get', 'set', 'unset', 'edit']),
)
except PipError as e:
logger.error(e.args[0])
@ -119,7 +123,7 @@ class ConfigurationCommand(Command):
# Load a new configuration
self.configuration = Configuration(
isolated=options.isolated_mode, load_only=load_only
isolated=options.isolated_mode, load_only=load_only,
)
self.configuration.load()
@ -132,7 +136,7 @@ class ConfigurationCommand(Command):
return SUCCESS
def _determine_file(self, options: Values, need_value: bool) -> Optional[Kind]:
def _determine_file(self, options: Values, need_value: bool) -> Kind | None:
file_options = [
key
for key, value in (
@ -158,47 +162,47 @@ class ConfigurationCommand(Command):
return file_options[0]
raise PipError(
"Need exactly one file to operate upon "
"(--user, --site, --global) to perform."
'Need exactly one file to operate upon '
'(--user, --site, --global) to perform.',
)
def list_values(self, options: Values, args: List[str]) -> None:
self._get_n_args(args, "list", n=0)
def list_values(self, options: Values, args: list[str]) -> None:
self._get_n_args(args, 'list', n=0)
for key, value in sorted(self.configuration.items()):
write_output("%s=%r", key, value)
write_output('%s=%r', key, value)
def get_name(self, options: Values, args: List[str]) -> None:
key = self._get_n_args(args, "get [name]", n=1)
def get_name(self, options: Values, args: list[str]) -> None:
key = self._get_n_args(args, 'get [name]', n=1)
value = self.configuration.get_value(key)
write_output("%s", value)
write_output('%s', value)
def set_name_value(self, options: Values, args: List[str]) -> None:
key, value = self._get_n_args(args, "set [name] [value]", n=2)
def set_name_value(self, options: Values, args: list[str]) -> None:
key, value = self._get_n_args(args, 'set [name] [value]', n=2)
self.configuration.set_value(key, value)
self._save_configuration()
def unset_name(self, options: Values, args: List[str]) -> None:
key = self._get_n_args(args, "unset [name]", n=1)
def unset_name(self, options: Values, args: list[str]) -> None:
key = self._get_n_args(args, 'unset [name]', n=1)
self.configuration.unset_value(key)
self._save_configuration()
def list_config_values(self, options: Values, args: List[str]) -> None:
def list_config_values(self, options: Values, args: list[str]) -> None:
"""List config key-value pairs across different config files"""
self._get_n_args(args, "debug", n=0)
self._get_n_args(args, 'debug', n=0)
self.print_env_var_values()
# Iterate over config files and print if they exist, and the
# key-value pairs present in them if they do
for variant, files in sorted(self.configuration.iter_config_files()):
write_output("%s:", variant)
write_output('%s:', variant)
for fname in files:
with indent_log():
file_exists = os.path.exists(fname)
write_output("%s, exists: %r", fname, file_exists)
write_output('%s, exists: %r', fname, file_exists)
if file_exists:
self.print_config_file_values(variant)
@ -206,35 +210,35 @@ class ConfigurationCommand(Command):
"""Get key-value pairs from the file of a variant"""
for name, value in self.configuration.get_values_in_config(variant).items():
with indent_log():
write_output("%s: %s", name, value)
write_output('%s: %s', name, value)
def print_env_var_values(self) -> None:
"""Get key-values pairs present as environment variables"""
write_output("%s:", "env_var")
write_output('%s:', 'env_var')
with indent_log():
for key, value in sorted(self.configuration.get_environ_vars()):
env_var = f"PIP_{key.upper()}"
write_output("%s=%r", env_var, value)
env_var = f'PIP_{key.upper()}'
write_output('%s=%r', env_var, value)
def open_in_editor(self, options: Values, args: List[str]) -> None:
def open_in_editor(self, options: Values, args: list[str]) -> None:
editor = self._determine_editor(options)
fname = self.configuration.get_file_to_edit()
if fname is None:
raise PipError("Could not determine appropriate file.")
raise PipError('Could not determine appropriate file.')
try:
subprocess.check_call([editor, fname])
except subprocess.CalledProcessError as e:
raise PipError(
"Editor Subprocess exited with exit code {}".format(e.returncode)
f'Editor Subprocess exited with exit code {e.returncode}',
)
def _get_n_args(self, args: List[str], example: str, n: int) -> Any:
def _get_n_args(self, args: list[str], example: str, n: int) -> Any:
"""Helper to make sure the command got the right number of arguments"""
if len(args) != n:
msg = (
"Got unexpected number of arguments, expected {}. "
'Got unexpected number of arguments, expected {}. '
'(example: "{} config {}")'
).format(n, get_prog(), example)
raise PipError(msg)
@ -251,16 +255,16 @@ class ConfigurationCommand(Command):
self.configuration.save()
except Exception:
logger.exception(
"Unable to save configuration. Please report this as a bug."
'Unable to save configuration. Please report this as a bug.',
)
raise PipError("Internal Error.")
raise PipError('Internal Error.')
def _determine_editor(self, options: Values) -> str:
if options.editor is not None:
return options.editor
elif "VISUAL" in os.environ:
return os.environ["VISUAL"]
elif "EDITOR" in os.environ:
return os.environ["EDITOR"]
elif 'VISUAL' in os.environ:
return os.environ['VISUAL']
elif 'EDITOR' in os.environ:
return os.environ['EDITOR']
else:
raise PipError("Could not determine editor to use.")
raise PipError('Could not determine editor to use.')

View file

@ -1,15 +1,17 @@
from __future__ import annotations
import locale
import logging
import os
import sys
from optparse import Values
from types import ModuleType
from typing import Any, Dict, List, Optional
from typing import Any
from typing import Dict
from typing import List
from typing import Optional
import pip._vendor
from pip._vendor.certifi import where
from pip._vendor.packaging.version import parse as parse_version
from pip import __file__ as pip_location
from pip._internal.cli import cmdoptions
from pip._internal.cli.base_command import Command
@ -19,51 +21,53 @@ from pip._internal.configuration import Configuration
from pip._internal.metadata import get_environment
from pip._internal.utils.logging import indent_log
from pip._internal.utils.misc import get_pip_version
from pip._vendor.certifi import where
from pip._vendor.packaging.version import parse as parse_version
logger = logging.getLogger(__name__)
def show_value(name: str, value: Any) -> None:
logger.info("%s: %s", name, value)
logger.info('%s: %s', name, value)
def show_sys_implementation() -> None:
logger.info("sys.implementation:")
logger.info('sys.implementation:')
implementation_name = sys.implementation.name
with indent_log():
show_value("name", implementation_name)
show_value('name', implementation_name)
def create_vendor_txt_map() -> Dict[str, str]:
def create_vendor_txt_map() -> dict[str, str]:
vendor_txt_path = os.path.join(
os.path.dirname(pip_location), "_vendor", "vendor.txt"
os.path.dirname(pip_location), '_vendor', 'vendor.txt',
)
with open(vendor_txt_path) as f:
# Purge non version specifying lines.
# Also, remove any space prefix or suffixes (including comments).
lines = [
line.strip().split(" ", 1)[0] for line in f.readlines() if "==" in line
line.strip().split(' ', 1)[0] for line in f.readlines() if '==' in line
]
# Transform into "module" -> version dict.
return dict(line.split("==", 1) for line in lines) # type: ignore
return dict(line.split('==', 1) for line in lines) # type: ignore
def get_module_from_module_name(module_name: str) -> ModuleType:
# Module name can be uppercase in vendor.txt for some reason...
module_name = module_name.lower()
# PATCH: setuptools is actually only pkg_resources.
if module_name == "setuptools":
module_name = "pkg_resources"
if module_name == 'setuptools':
module_name = 'pkg_resources'
__import__(f"pip._vendor.{module_name}", globals(), locals(), level=0)
__import__(f'pip._vendor.{module_name}', globals(), locals(), level=0)
return getattr(pip._vendor, module_name)
def get_vendor_version_from_module(module_name: str) -> Optional[str]:
def get_vendor_version_from_module(module_name: str) -> str | None:
module = get_module_from_module_name(module_name)
version = getattr(module, "__version__", None)
version = getattr(module, '__version__', None)
if not version:
# Try to find version in debundled module info.
@ -75,29 +79,29 @@ def get_vendor_version_from_module(module_name: str) -> Optional[str]:
return version
def show_actual_vendor_versions(vendor_txt_versions: Dict[str, str]) -> None:
def show_actual_vendor_versions(vendor_txt_versions: dict[str, str]) -> None:
"""Log the actual version and print extra info if there is
a conflict or if the actual version could not be imported.
"""
for module_name, expected_version in vendor_txt_versions.items():
extra_message = ""
extra_message = ''
actual_version = get_vendor_version_from_module(module_name)
if not actual_version:
extra_message = (
" (Unable to locate actual module version, using"
" vendor.txt specified version)"
' (Unable to locate actual module version, using'
' vendor.txt specified version)'
)
actual_version = expected_version
elif parse_version(actual_version) != parse_version(expected_version):
extra_message = (
" (CONFLICT: vendor.txt suggests version should"
" be {})".format(expected_version)
' (CONFLICT: vendor.txt suggests version should'
' be {})'.format(expected_version)
)
logger.info("%s==%s%s", module_name, actual_version, extra_message)
logger.info('%s==%s%s', module_name, actual_version, extra_message)
def show_vendor_versions() -> None:
logger.info("vendored library versions:")
logger.info('vendored library versions:')
vendor_txt_versions = create_vendor_txt_map()
with indent_log():
@ -112,11 +116,11 @@ def show_tags(options: Values) -> None:
# Display the target options that were explicitly provided.
formatted_target = target_python.format_given()
suffix = ""
suffix = ''
if formatted_target:
suffix = f" (target: {formatted_target})"
suffix = f' (target: {formatted_target})'
msg = "Compatible tags: {}{}".format(len(tags), suffix)
msg = f'Compatible tags: {len(tags)}{suffix}'
logger.info(msg)
if options.verbose < 1 and len(tags) > tag_limit:
@ -131,7 +135,7 @@ def show_tags(options: Values) -> None:
if tags_limited:
msg = (
"...\n[First {tag_limit} tags shown. Pass --verbose to show all.]"
'...\n[First {tag_limit} tags shown. Pass --verbose to show all.]'
).format(tag_limit=tag_limit)
logger.info(msg)
@ -139,21 +143,21 @@ def show_tags(options: Values) -> None:
def ca_bundle_info(config: Configuration) -> str:
levels = set()
for key, _ in config.items():
levels.add(key.split(".")[0])
levels.add(key.split('.')[0])
if not levels:
return "Not specified"
return 'Not specified'
levels_that_override_global = ["install", "wheel", "download"]
levels_that_override_global = ['install', 'wheel', 'download']
global_overriding_level = [
level for level in levels if level in levels_that_override_global
]
if not global_overriding_level:
return "global"
return 'global'
if "global" in levels:
levels.remove("global")
return ", ".join(levels)
if 'global' in levels:
levels.remove('global')
return ', '.join(levels)
class DebugCommand(Command):
@ -170,30 +174,30 @@ class DebugCommand(Command):
self.parser.insert_option_group(0, self.cmd_opts)
self.parser.config.load()
def run(self, options: Values, args: List[str]) -> int:
def run(self, options: Values, args: list[str]) -> int:
logger.warning(
"This command is only meant for debugging. "
"Do not use this with automation for parsing and getting these "
"details, since the output and options of this command may "
"change without notice."
'This command is only meant for debugging. '
'Do not use this with automation for parsing and getting these '
'details, since the output and options of this command may '
'change without notice.',
)
show_value("pip version", get_pip_version())
show_value("sys.version", sys.version)
show_value("sys.executable", sys.executable)
show_value("sys.getdefaultencoding", sys.getdefaultencoding())
show_value("sys.getfilesystemencoding", sys.getfilesystemencoding())
show_value('pip version', get_pip_version())
show_value('sys.version', sys.version)
show_value('sys.executable', sys.executable)
show_value('sys.getdefaultencoding', sys.getdefaultencoding())
show_value('sys.getfilesystemencoding', sys.getfilesystemencoding())
show_value(
"locale.getpreferredencoding",
'locale.getpreferredencoding',
locale.getpreferredencoding(),
)
show_value("sys.platform", sys.platform)
show_value('sys.platform', sys.platform)
show_sys_implementation()
show_value("'cert' config value", ca_bundle_info(self.parser.config))
show_value("REQUESTS_CA_BUNDLE", os.environ.get("REQUESTS_CA_BUNDLE"))
show_value("CURL_CA_BUNDLE", os.environ.get("CURL_CA_BUNDLE"))
show_value("pip._vendor.certifi.where()", where())
show_value("pip._vendor.DEBUNDLED", pip._vendor.DEBUNDLED)
show_value('REQUESTS_CA_BUNDLE', os.environ.get('REQUESTS_CA_BUNDLE'))
show_value('CURL_CA_BUNDLE', os.environ.get('CURL_CA_BUNDLE'))
show_value('pip._vendor.certifi.where()', where())
show_value('pip._vendor.DEBUNDLED', pip._vendor.DEBUNDLED)
show_vendor_versions()

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import logging
import os
from optparse import Values
@ -5,10 +7,13 @@ from typing import List
from pip._internal.cli import cmdoptions
from pip._internal.cli.cmdoptions import make_target_python
from pip._internal.cli.req_command import RequirementCommand, with_cleanup
from pip._internal.cli.req_command import RequirementCommand
from pip._internal.cli.req_command import with_cleanup
from pip._internal.cli.status_codes import SUCCESS
from pip._internal.req.req_tracker import get_requirement_tracker
from pip._internal.utils.misc import ensure_dir, normalize_path, write_output
from pip._internal.utils.misc import ensure_dir
from pip._internal.utils.misc import normalize_path
from pip._internal.utils.misc import write_output
from pip._internal.utils.temp_dir import TempDirectory
logger = logging.getLogger(__name__)
@ -52,14 +57,14 @@ class DownloadCommand(RequirementCommand):
self.cmd_opts.add_option(cmdoptions.ignore_requires_python())
self.cmd_opts.add_option(
"-d",
"--dest",
"--destination-dir",
"--destination-directory",
dest="download_dir",
metavar="dir",
'-d',
'--dest',
'--destination-dir',
'--destination-directory',
dest='download_dir',
metavar='dir',
default=os.curdir,
help="Download packages into <dir>.",
help='Download packages into <dir>.',
)
cmdoptions.add_target_python_options(self.cmd_opts)
@ -73,7 +78,7 @@ class DownloadCommand(RequirementCommand):
self.parser.insert_option_group(0, self.cmd_opts)
@with_cleanup
def run(self, options: Values, args: List[str]) -> int:
def run(self, options: Values, args: list[str]) -> int:
options.ignore_installed = True
# editable doesn't really make sense for `pip download`, but the bowels
@ -99,7 +104,7 @@ class DownloadCommand(RequirementCommand):
directory = TempDirectory(
delete=not options.no_clean,
kind="download",
kind='download',
globally_managed=True,
)
@ -128,13 +133,13 @@ class DownloadCommand(RequirementCommand):
requirement_set = resolver.resolve(reqs, check_supported_wheels=True)
downloaded: List[str] = []
downloaded: list[str] = []
for req in requirement_set.requirements.values():
if req.satisfied_by is None:
assert req.name is not None
preparer.save_linked_requirement(req)
downloaded.append(req.name)
if downloaded:
write_output("Successfully downloaded %s", " ".join(downloaded))
write_output('Successfully downloaded %s', ' '.join(downloaded))
return SUCCESS

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import sys
from optparse import Values
from typing import List
@ -8,7 +10,7 @@ from pip._internal.cli.status_codes import SUCCESS
from pip._internal.operations.freeze import freeze
from pip._internal.utils.compat import stdlib_pkgs
DEV_PKGS = {"pip", "setuptools", "distribute", "wheel"}
DEV_PKGS = {'pip', 'setuptools', 'distribute', 'wheel'}
class FreezeCommand(Command):
@ -20,61 +22,61 @@ class FreezeCommand(Command):
usage = """
%prog [options]"""
log_streams = ("ext://sys.stderr", "ext://sys.stderr")
log_streams = ('ext://sys.stderr', 'ext://sys.stderr')
def add_options(self) -> None:
self.cmd_opts.add_option(
"-r",
"--requirement",
dest="requirements",
action="append",
'-r',
'--requirement',
dest='requirements',
action='append',
default=[],
metavar="file",
metavar='file',
help=(
"Use the order in the given requirements file and its "
"comments when generating output. This option can be "
"used multiple times."
'Use the order in the given requirements file and its '
'comments when generating output. This option can be '
'used multiple times.'
),
)
self.cmd_opts.add_option(
"-l",
"--local",
dest="local",
action="store_true",
'-l',
'--local',
dest='local',
action='store_true',
default=False,
help=(
"If in a virtualenv that has global access, do not output "
"globally-installed packages."
'If in a virtualenv that has global access, do not output '
'globally-installed packages.'
),
)
self.cmd_opts.add_option(
"--user",
dest="user",
action="store_true",
'--user',
dest='user',
action='store_true',
default=False,
help="Only output packages installed in user-site.",
help='Only output packages installed in user-site.',
)
self.cmd_opts.add_option(cmdoptions.list_path())
self.cmd_opts.add_option(
"--all",
dest="freeze_all",
action="store_true",
'--all',
dest='freeze_all',
action='store_true',
help=(
"Do not skip these packages in the output:"
" {}".format(", ".join(DEV_PKGS))
'Do not skip these packages in the output:'
' {}'.format(', '.join(DEV_PKGS))
),
)
self.cmd_opts.add_option(
"--exclude-editable",
dest="exclude_editable",
action="store_true",
help="Exclude editable package from output.",
'--exclude-editable',
dest='exclude_editable',
action='store_true',
help='Exclude editable package from output.',
)
self.cmd_opts.add_option(cmdoptions.list_exclude())
self.parser.insert_option_group(0, self.cmd_opts)
def run(self, options: Values, args: List[str]) -> int:
def run(self, options: Values, args: list[str]) -> int:
skip = set(stdlib_pkgs)
if not options.freeze_all:
skip.update(DEV_PKGS)
@ -93,5 +95,5 @@ class FreezeCommand(Command):
skip=skip,
exclude_editable=options.exclude_editable,
):
sys.stdout.write(line + "\n")
sys.stdout.write(line + '\n')
return SUCCESS

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import hashlib
import logging
import sys
@ -5,9 +7,12 @@ from optparse import Values
from typing import List
from pip._internal.cli.base_command import Command
from pip._internal.cli.status_codes import ERROR, SUCCESS
from pip._internal.utils.hashes import FAVORITE_HASH, STRONG_HASHES
from pip._internal.utils.misc import read_chunks, write_output
from pip._internal.cli.status_codes import ERROR
from pip._internal.cli.status_codes import SUCCESS
from pip._internal.utils.hashes import FAVORITE_HASH
from pip._internal.utils.hashes import STRONG_HASHES
from pip._internal.utils.misc import read_chunks
from pip._internal.utils.misc import write_output
logger = logging.getLogger(__name__)
@ -20,24 +25,24 @@ class HashCommand(Command):
installs.
"""
usage = "%prog [options] <file> ..."
usage = '%prog [options] <file> ...'
ignore_require_venv = True
def add_options(self) -> None:
self.cmd_opts.add_option(
"-a",
"--algorithm",
dest="algorithm",
'-a',
'--algorithm',
dest='algorithm',
choices=STRONG_HASHES,
action="store",
action='store',
default=FAVORITE_HASH,
help="The hash algorithm to use: one of {}".format(
", ".join(STRONG_HASHES)
help='The hash algorithm to use: one of {}'.format(
', '.join(STRONG_HASHES),
),
)
self.parser.insert_option_group(0, self.cmd_opts)
def run(self, options: Values, args: List[str]) -> int:
def run(self, options: Values, args: list[str]) -> int:
if not args:
self.parser.print_usage(sys.stderr)
return ERROR
@ -45,14 +50,14 @@ class HashCommand(Command):
algorithm = options.algorithm
for path in args:
write_output(
"%s:\n--hash=%s:%s", path, algorithm, _hash_of_file(path, algorithm)
'%s:\n--hash=%s:%s', path, algorithm, _hash_of_file(path, algorithm),
)
return SUCCESS
def _hash_of_file(path: str, algorithm: str) -> str:
"""Return the hash digest of a file."""
with open(path, "rb") as archive:
with open(path, 'rb') as archive:
hash = hashlib.new(algorithm)
for chunk in read_chunks(archive):
hash.update(chunk)

View file

@ -1,3 +1,5 @@
from __future__ import annotations
from optparse import Values
from typing import List
@ -13,7 +15,7 @@ class HelpCommand(Command):
%prog <command>"""
ignore_require_venv = True
def run(self, options: Values, args: List[str]) -> int:
def run(self, options: Values, args: list[str]) -> int:
from pip._internal.commands import (
commands_dict,
create_command,
@ -33,7 +35,7 @@ class HelpCommand(Command):
if guess:
msg.append(f'maybe you meant "{guess}"')
raise CommandError(" - ".join(msg))
raise CommandError(' - '.join(msg))
command = create_command(cmd_name)
command.parser.print_help()

View file

@ -1,20 +1,29 @@
from __future__ import annotations
import logging
from optparse import Values
from typing import Any, Iterable, List, Optional, Union
from pip._vendor.packaging.version import LegacyVersion, Version
from typing import Any
from typing import Iterable
from typing import List
from typing import Optional
from typing import Union
from pip._internal.cli import cmdoptions
from pip._internal.cli.req_command import IndexGroupCommand
from pip._internal.cli.status_codes import ERROR, SUCCESS
from pip._internal.cli.status_codes import ERROR
from pip._internal.cli.status_codes import SUCCESS
from pip._internal.commands.search import print_dist_installation_info
from pip._internal.exceptions import CommandError, DistributionNotFound, PipError
from pip._internal.exceptions import CommandError
from pip._internal.exceptions import DistributionNotFound
from pip._internal.exceptions import PipError
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.utils.misc import write_output
from pip._vendor.packaging.version import LegacyVersion
from pip._vendor.packaging.version import Version
logger = logging.getLogger(__name__)
@ -44,22 +53,22 @@ class IndexCommand(IndexGroupCommand):
self.parser.insert_option_group(0, index_opts)
self.parser.insert_option_group(0, self.cmd_opts)
def run(self, options: Values, args: List[str]) -> int:
def run(self, options: Values, args: list[str]) -> int:
handlers = {
"versions": self.get_available_package_versions,
'versions': self.get_available_package_versions,
}
logger.warning(
"pip index is currently an experimental command. "
"It may be removed/changed in a future release "
"without prior warning."
'pip index is currently an experimental command. '
'It may be removed/changed in a future release '
'without prior warning.',
)
# Determine action
if not args or args[0] not in handlers:
logger.error(
"Need an action (%s) to perform.",
", ".join(sorted(handlers)),
'Need an action (%s) to perform.',
', '.join(sorted(handlers)),
)
return ERROR
@ -78,8 +87,8 @@ class IndexCommand(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 the index command.
@ -97,12 +106,12 @@ class IndexCommand(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,
)
def get_available_package_versions(self, options: Values, args: List[Any]) -> None:
def get_available_package_versions(self, options: Values, args: list[Any]) -> None:
if len(args) != 1:
raise CommandError("You need to specify exactly one argument")
raise CommandError('You need to specify exactly one argument')
target_python = cmdoptions.make_target_python(options)
query = args[0]
@ -115,7 +124,7 @@ class IndexCommand(IndexGroupCommand):
ignore_requires_python=options.ignore_requires_python,
)
versions: Iterable[Union[LegacyVersion, Version]] = (
versions: Iterable[LegacyVersion | Version] = (
candidate.version for candidate in finder.find_all_candidates(query)
)
@ -128,12 +137,12 @@ class IndexCommand(IndexGroupCommand):
if not versions:
raise DistributionNotFound(
"No matching distribution found for {}".format(query)
f'No matching distribution found for {query}',
)
formatted_versions = [str(ver) for ver in sorted(versions, reverse=True)]
latest = formatted_versions[0]
write_output("{} ({})".format(query, latest))
write_output("Available versions: {}".format(", ".join(formatted_versions)))
write_output(f'{query} ({latest})')
write_output('Available versions: {}'.format(', '.join(formatted_versions)))
print_dist_installation_info(query, latest)

View file

@ -1,27 +1,31 @@
from __future__ import annotations
import errno
import operator
import os
import shutil
import site
from optparse import SUPPRESS_HELP, Values
from typing import Iterable, List, Optional
from pip._vendor.packaging.utils import canonicalize_name
from optparse import SUPPRESS_HELP
from optparse import Values
from typing import Iterable
from typing import List
from typing import Optional
from pip._internal.cache import WheelCache
from pip._internal.cli import cmdoptions
from pip._internal.cli.cmdoptions import make_target_python
from pip._internal.cli.req_command import (
RequirementCommand,
warn_if_run_as_root,
with_cleanup,
)
from pip._internal.cli.status_codes import ERROR, SUCCESS
from pip._internal.exceptions import CommandError, InstallationError
from pip._internal.cli.req_command import RequirementCommand
from pip._internal.cli.req_command import warn_if_run_as_root
from pip._internal.cli.req_command import with_cleanup
from pip._internal.cli.status_codes import ERROR
from pip._internal.cli.status_codes import SUCCESS
from pip._internal.exceptions import CommandError
from pip._internal.exceptions import InstallationError
from pip._internal.locations import get_scheme
from pip._internal.metadata import get_environment
from pip._internal.models.format_control import FormatControl
from pip._internal.operations.check import ConflictDetails, check_install_conflicts
from pip._internal.operations.check import check_install_conflicts
from pip._internal.operations.check import ConflictDetails
from pip._internal.req import install_given_reqs
from pip._internal.req.req_install import InstallRequirement
from pip._internal.req.req_tracker import get_requirement_tracker
@ -29,31 +33,26 @@ from pip._internal.utils.compat import WINDOWS
from pip._internal.utils.distutils_args import parse_distutils_args
from pip._internal.utils.filesystem import test_writable_dir
from pip._internal.utils.logging import getLogger
from pip._internal.utils.misc import (
ensure_dir,
get_pip_version,
protect_pip_from_modification_on_windows,
write_output,
)
from pip._internal.utils.misc import ensure_dir
from pip._internal.utils.misc import get_pip_version
from pip._internal.utils.misc import protect_pip_from_modification_on_windows
from pip._internal.utils.misc import write_output
from pip._internal.utils.temp_dir import TempDirectory
from pip._internal.utils.virtualenv import (
running_under_virtualenv,
virtualenv_no_global,
)
from pip._internal.wheel_builder import (
BinaryAllowedPredicate,
build,
should_build_for_install_command,
)
from pip._internal.utils.virtualenv import running_under_virtualenv
from pip._internal.utils.virtualenv import virtualenv_no_global
from pip._internal.wheel_builder import BinaryAllowedPredicate
from pip._internal.wheel_builder import build
from pip._internal.wheel_builder import should_build_for_install_command
from pip._vendor.packaging.utils import canonicalize_name
logger = getLogger(__name__)
def get_check_binary_allowed(format_control: FormatControl) -> BinaryAllowedPredicate:
def check_binary_allowed(req: InstallRequirement) -> bool:
canonical_name = canonicalize_name(req.name or "")
canonical_name = canonicalize_name(req.name or '')
allowed_formats = format_control.get_allowed_formats(canonical_name)
return "binary" in allowed_formats
return 'binary' in allowed_formats
return check_binary_allowed
@ -86,102 +85,102 @@ class InstallCommand(RequirementCommand):
self.cmd_opts.add_option(cmdoptions.editable())
self.cmd_opts.add_option(
"-t",
"--target",
dest="target_dir",
metavar="dir",
'-t',
'--target',
dest='target_dir',
metavar='dir',
default=None,
help=(
"Install packages into <dir>. "
"By default this will not replace existing files/folders in "
"<dir>. Use --upgrade to replace existing packages in <dir> "
"with new versions."
'Install packages into <dir>. '
'By default this will not replace existing files/folders in '
'<dir>. Use --upgrade to replace existing packages in <dir> '
'with new versions.'
),
)
cmdoptions.add_target_python_options(self.cmd_opts)
self.cmd_opts.add_option(
"--user",
dest="use_user_site",
action="store_true",
'--user',
dest='use_user_site',
action='store_true',
help=(
"Install to the Python user install directory for your "
"platform. Typically ~/.local/, or %APPDATA%\\Python on "
"Windows. (See the Python documentation for site.USER_BASE "
"for full details.)"
'Install to the Python user install directory for your '
'platform. Typically ~/.local/, or %APPDATA%\\Python on '
'Windows. (See the Python documentation for site.USER_BASE '
'for full details.)'
),
)
self.cmd_opts.add_option(
"--no-user",
dest="use_user_site",
action="store_false",
'--no-user',
dest='use_user_site',
action='store_false',
help=SUPPRESS_HELP,
)
self.cmd_opts.add_option(
"--root",
dest="root_path",
metavar="dir",
'--root',
dest='root_path',
metavar='dir',
default=None,
help="Install everything relative to this alternate root directory.",
help='Install everything relative to this alternate root directory.',
)
self.cmd_opts.add_option(
"--prefix",
dest="prefix_path",
metavar="dir",
'--prefix',
dest='prefix_path',
metavar='dir',
default=None,
help=(
"Installation prefix where lib, bin and other top-level "
"folders are placed"
'Installation prefix where lib, bin and other top-level '
'folders are placed'
),
)
self.cmd_opts.add_option(cmdoptions.src())
self.cmd_opts.add_option(
"-U",
"--upgrade",
dest="upgrade",
action="store_true",
'-U',
'--upgrade',
dest='upgrade',
action='store_true',
help=(
"Upgrade all specified packages to the newest available "
"version. The handling of dependencies depends on the "
"upgrade-strategy used."
'Upgrade all specified packages to the newest available '
'version. The handling of dependencies depends on the '
'upgrade-strategy used.'
),
)
self.cmd_opts.add_option(
"--upgrade-strategy",
dest="upgrade_strategy",
default="only-if-needed",
choices=["only-if-needed", "eager"],
'--upgrade-strategy',
dest='upgrade_strategy',
default='only-if-needed',
choices=['only-if-needed', 'eager'],
help=(
"Determines how dependency upgrading should be handled "
"[default: %default]. "
'Determines how dependency upgrading should be handled '
'[default: %default]. '
'"eager" - dependencies are upgraded regardless of '
"whether the currently installed version satisfies the "
"requirements of the upgraded package(s). "
'whether the currently installed version satisfies the '
'requirements of the upgraded package(s). '
'"only-if-needed" - are upgraded only when they do not '
"satisfy the requirements of the upgraded package(s)."
'satisfy the requirements of the upgraded package(s).'
),
)
self.cmd_opts.add_option(
"--force-reinstall",
dest="force_reinstall",
action="store_true",
help="Reinstall all packages even if they are already up-to-date.",
'--force-reinstall',
dest='force_reinstall',
action='store_true',
help='Reinstall all packages even if they are already up-to-date.',
)
self.cmd_opts.add_option(
"-I",
"--ignore-installed",
dest="ignore_installed",
action="store_true",
'-I',
'--ignore-installed',
dest='ignore_installed',
action='store_true',
help=(
"Ignore the installed packages, overwriting them. "
"This can break your system if the existing package "
"is of a different version or was installed "
"with a different package manager!"
'Ignore the installed packages, overwriting them. '
'This can break your system if the existing package '
'is of a different version or was installed '
'with a different package manager!'
),
)
@ -194,33 +193,33 @@ class InstallCommand(RequirementCommand):
self.cmd_opts.add_option(cmdoptions.global_options())
self.cmd_opts.add_option(
"--compile",
action="store_true",
dest="compile",
'--compile',
action='store_true',
dest='compile',
default=True,
help="Compile Python source files to bytecode",
help='Compile Python source files to bytecode',
)
self.cmd_opts.add_option(
"--no-compile",
action="store_false",
dest="compile",
help="Do not compile Python source files to bytecode",
'--no-compile',
action='store_false',
dest='compile',
help='Do not compile Python source files to bytecode',
)
self.cmd_opts.add_option(
"--no-warn-script-location",
action="store_false",
dest="warn_script_location",
'--no-warn-script-location',
action='store_false',
dest='warn_script_location',
default=True,
help="Do not warn when installing scripts outside PATH",
help='Do not warn when installing scripts outside PATH',
)
self.cmd_opts.add_option(
"--no-warn-conflicts",
action="store_false",
dest="warn_about_conflicts",
'--no-warn-conflicts',
action='store_false',
dest='warn_about_conflicts',
default=True,
help="Do not warn about broken dependencies",
help='Do not warn about broken dependencies',
)
self.cmd_opts.add_option(cmdoptions.no_binary())
@ -238,12 +237,12 @@ class InstallCommand(RequirementCommand):
self.parser.insert_option_group(0, self.cmd_opts)
@with_cleanup
def run(self, options: Values, args: List[str]) -> int:
def run(self, options: Values, args: list[str]) -> int:
if options.use_user_site and options.target_dir is not None:
raise CommandError("Can not combine '--user' and '--target'")
cmdoptions.check_install_build_global(options)
upgrade_strategy = "to-satisfy-only"
upgrade_strategy = 'to-satisfy-only'
if options.upgrade:
upgrade_strategy = options.upgrade_strategy
@ -251,7 +250,7 @@ class InstallCommand(RequirementCommand):
install_options = options.install_options or []
logger.verbose("Using %s", get_pip_version())
logger.verbose('Using %s', get_pip_version())
options.use_user_site = decide_user_install(
options.use_user_site,
prefix_path=options.prefix_path,
@ -260,8 +259,8 @@ class InstallCommand(RequirementCommand):
isolated_mode=options.isolated_mode,
)
target_temp_dir: Optional[TempDirectory] = None
target_temp_dir_path: Optional[str] = None
target_temp_dir: TempDirectory | None = None
target_temp_dir_path: str | None = None
if options.target_dir:
options.ignore_installed = True
options.target_dir = os.path.abspath(options.target_dir)
@ -272,11 +271,11 @@ class InstallCommand(RequirementCommand):
# fmt: on
):
raise CommandError(
"Target path exists but is not a directory, will not continue."
'Target path exists but is not a directory, will not continue.',
)
# Create a target directory for using with the target option
target_temp_dir = TempDirectory(kind="target")
target_temp_dir = TempDirectory(kind='target')
target_temp_dir_path = target_temp_dir.path
self.enter_context(target_temp_dir)
@ -297,7 +296,7 @@ class InstallCommand(RequirementCommand):
directory = TempDirectory(
delete=not options.no_clean,
kind="install",
kind='install',
globally_managed=True,
)
@ -337,11 +336,11 @@ class InstallCommand(RequirementCommand):
self.trace_basic_info(finder)
requirement_set = resolver.resolve(
reqs, check_supported_wheels=not options.target_dir
reqs, check_supported_wheels=not options.target_dir,
)
try:
pip_req = requirement_set.get_requirement("pip")
pip_req = requirement_set.get_requirement('pip')
except KeyError:
modifying_pip = False
else:
@ -368,15 +367,15 @@ class InstallCommand(RequirementCommand):
# If we're using PEP 517, we cannot do a legacy setup.py install
# so we fail here.
pep517_build_failure_names: List[str] = [
pep517_build_failure_names: list[str] = [
r.name for r in build_failures if r.use_pep517 # type: ignore
]
if pep517_build_failure_names:
raise InstallationError(
"Could not build wheels for {}, which is required to "
"install pyproject.toml-based projects".format(
", ".join(pep517_build_failure_names)
)
'Could not build wheels for {}, which is required to '
'install pyproject.toml-based projects'.format(
', '.join(pep517_build_failure_names),
),
)
# For now, we just warn about failures building legacy
@ -389,7 +388,7 @@ class InstallCommand(RequirementCommand):
to_install = resolver.get_installation_order(requirement_set)
# Check for conflicts in the package set we're installing.
conflicts: Optional[ConflictDetails] = None
conflicts: ConflictDetails | None = None
should_warn_about_conflicts = (
not options.ignore_dependencies and options.warn_about_conflicts
)
@ -423,14 +422,14 @@ class InstallCommand(RequirementCommand):
)
env = get_environment(lib_locations)
installed.sort(key=operator.attrgetter("name"))
installed.sort(key=operator.attrgetter('name'))
items = []
for result in installed:
item = result.name
try:
installed_dist = env.get_distribution(item)
if installed_dist is not None:
item = f"{item}-{installed_dist.version}"
item = f'{item}-{installed_dist.version}'
except Exception:
pass
items.append(item)
@ -441,10 +440,10 @@ class InstallCommand(RequirementCommand):
resolver_variant=self.determine_resolver_variant(options),
)
installed_desc = " ".join(items)
installed_desc = ' '.join(items)
if installed_desc:
write_output(
"Successfully installed %s",
'Successfully installed %s',
installed_desc,
)
except OSError as error:
@ -462,14 +461,14 @@ class InstallCommand(RequirementCommand):
if options.target_dir:
assert target_temp_dir
self._handle_target_dir(
options.target_dir, target_temp_dir, options.upgrade
options.target_dir, target_temp_dir, options.upgrade,
)
warn_if_run_as_root()
return SUCCESS
def _handle_target_dir(
self, target_dir: str, target_temp_dir: TempDirectory, upgrade: bool
self, target_dir: str, target_temp_dir: TempDirectory, upgrade: bool,
) -> None:
ensure_dir(target_dir)
@ -479,7 +478,7 @@ class InstallCommand(RequirementCommand):
# Checking both purelib and platlib directories for installed
# packages to be moved to target directory
scheme = get_scheme("", home=target_temp_dir.path)
scheme = get_scheme('', home=target_temp_dir.path)
purelib_dir = scheme.purelib
platlib_dir = scheme.platlib
data_dir = scheme.data
@ -501,17 +500,17 @@ class InstallCommand(RequirementCommand):
if os.path.exists(target_item_dir):
if not upgrade:
logger.warning(
"Target directory %s already exists. Specify "
"--upgrade to force replacement.",
'Target directory %s already exists. Specify '
'--upgrade to force replacement.',
target_item_dir,
)
continue
if os.path.islink(target_item_dir):
logger.warning(
"Target directory %s already exists and is "
"a link. pip will not automatically replace "
"links, please remove if replacement is "
"desired.",
'Target directory %s already exists and is '
'a link. pip will not automatically replace '
'links, please remove if replacement is '
'desired.',
target_item_dir,
)
continue
@ -523,37 +522,37 @@ class InstallCommand(RequirementCommand):
shutil.move(os.path.join(lib_dir, item), target_item_dir)
def _determine_conflicts(
self, to_install: List[InstallRequirement]
) -> Optional[ConflictDetails]:
self, to_install: list[InstallRequirement],
) -> ConflictDetails | None:
try:
return check_install_conflicts(to_install)
except Exception:
logger.exception(
"Error while checking for conflicts. Please file an issue on "
"pip's issue tracker: https://github.com/pypa/pip/issues/new"
'Error while checking for conflicts. Please file an issue on '
"pip's issue tracker: https://github.com/pypa/pip/issues/new",
)
return None
def _warn_about_conflicts(
self, conflict_details: ConflictDetails, resolver_variant: str
self, conflict_details: ConflictDetails, resolver_variant: str,
) -> None:
package_set, (missing, conflicting) = conflict_details
if not missing and not conflicting:
return
parts: List[str] = []
if resolver_variant == "legacy":
parts: list[str] = []
if resolver_variant == 'legacy':
parts.append(
"pip's legacy dependency resolver does not consider dependency "
"conflicts when selecting packages. This behaviour is the "
"source of the following dependency conflicts."
'conflicts when selecting packages. This behaviour is the '
'source of the following dependency conflicts.',
)
else:
assert resolver_variant == "2020-resolver"
assert resolver_variant == '2020-resolver'
parts.append(
"pip's dependency resolver does not currently take into account "
"all the packages that are installed. This behaviour is the "
"source of the following dependency conflicts."
'all the packages that are installed. This behaviour is the '
'source of the following dependency conflicts.',
)
# NOTE: There is some duplication here, with commands/check.py
@ -561,8 +560,8 @@ class InstallCommand(RequirementCommand):
version = package_set[project_name][0]
for dependency in missing[project_name]:
message = (
"{name} {version} requires {requirement}, "
"which is not installed."
'{name} {version} requires {requirement}, '
'which is not installed.'
).format(
name=project_name,
version=version,
@ -574,30 +573,30 @@ class InstallCommand(RequirementCommand):
version = package_set[project_name][0]
for dep_name, dep_version, req in conflicting[project_name]:
message = (
"{name} {version} requires {requirement}, but {you} have "
"{dep_name} {dep_version} which is incompatible."
'{name} {version} requires {requirement}, but {you} have '
'{dep_name} {dep_version} which is incompatible.'
).format(
name=project_name,
version=version,
requirement=req,
dep_name=dep_name,
dep_version=dep_version,
you=("you" if resolver_variant == "2020-resolver" else "you'll"),
you=('you' if resolver_variant == '2020-resolver' else "you'll"),
)
parts.append(message)
logger.critical("\n".join(parts))
logger.critical('\n'.join(parts))
def get_lib_location_guesses(
user: bool = False,
home: Optional[str] = None,
root: Optional[str] = None,
home: str | None = None,
root: str | None = None,
isolated: bool = False,
prefix: Optional[str] = None,
) -> List[str]:
prefix: str | None = None,
) -> list[str]:
scheme = get_scheme(
"",
'',
user=user,
home=home,
root=root,
@ -607,7 +606,7 @@ def get_lib_location_guesses(
return [scheme.purelib, scheme.platlib]
def site_packages_writable(root: Optional[str], isolated: bool) -> bool:
def site_packages_writable(root: str | None, isolated: bool) -> bool:
return all(
test_writable_dir(d)
for d in set(get_lib_location_guesses(root=root, isolated=isolated))
@ -615,10 +614,10 @@ def site_packages_writable(root: Optional[str], isolated: bool) -> bool:
def decide_user_install(
use_user_site: Optional[bool],
prefix_path: Optional[str] = None,
target_dir: Optional[str] = None,
root_path: Optional[str] = None,
use_user_site: bool | None,
prefix_path: str | None = None,
target_dir: str | None = None,
root_path: str | None = None,
isolated_mode: bool = False,
) -> bool:
"""Determine whether to do a user install based on the input options.
@ -632,21 +631,21 @@ def decide_user_install(
# In some cases (config from tox), use_user_site can be set to an integer
# rather than a bool, which 'use_user_site is False' wouldn't catch.
if (use_user_site is not None) and (not use_user_site):
logger.debug("Non-user install by explicit request")
logger.debug('Non-user install by explicit request')
return False
if use_user_site:
if prefix_path:
raise CommandError(
"Can not combine '--user' and '--prefix' as they imply "
"different installation locations"
'different installation locations',
)
if virtualenv_no_global():
raise InstallationError(
"Can not perform a '--user' install. User site-packages "
"are not visible in this virtualenv."
'are not visible in this virtualenv.',
)
logger.debug("User install by explicit request")
logger.debug('User install by explicit request')
return True
# If we are here, user installs have not been explicitly requested/avoided
@ -654,36 +653,36 @@ def decide_user_install(
# user install incompatible with --prefix/--target
if prefix_path or target_dir:
logger.debug("Non-user install due to --prefix or --target option")
logger.debug('Non-user install due to --prefix or --target option')
return False
# If user installs are not enabled, choose a non-user install
if not site.ENABLE_USER_SITE:
logger.debug("Non-user install because user site-packages disabled")
logger.debug('Non-user install because user site-packages disabled')
return False
# If we have permission for a non-user install, do that,
# otherwise do a user install.
if site_packages_writable(root=root_path, isolated=isolated_mode):
logger.debug("Non-user install because site-packages writeable")
logger.debug('Non-user install because site-packages writeable')
return False
logger.info(
"Defaulting to user installation because normal site-packages "
"is not writeable"
'Defaulting to user installation because normal site-packages '
'is not writeable',
)
return True
def reject_location_related_install_options(
requirements: List[InstallRequirement], options: Optional[List[str]]
requirements: list[InstallRequirement], options: list[str] | None,
) -> None:
"""If any location-changing --install-option arguments were passed for
requirements or on the command-line, then show a deprecation warning.
"""
def format_options(option_names: Iterable[str]) -> List[str]:
return ["--{}".format(name.replace("_", "-")) for name in option_names]
def format_options(option_names: Iterable[str]) -> list[str]:
return ['--{}'.format(name.replace('_', '-')) for name in option_names]
offenders = []
@ -692,30 +691,30 @@ def reject_location_related_install_options(
location_options = parse_distutils_args(install_options)
if location_options:
offenders.append(
"{!r} from {}".format(
format_options(location_options.keys()), requirement
)
'{!r} from {}'.format(
format_options(location_options.keys()), requirement,
),
)
if options:
location_options = parse_distutils_args(options)
if location_options:
offenders.append(
"{!r} from command line".format(format_options(location_options.keys()))
f'{format_options(location_options.keys())!r} from command line',
)
if not offenders:
return
raise CommandError(
"Location-changing options found in --install-option: {}."
" This is unsupported, use pip-level options like --user,"
" --prefix, --root, and --target instead.".format("; ".join(offenders))
'Location-changing options found in --install-option: {}.'
' This is unsupported, use pip-level options like --user,'
' --prefix, --root, and --target instead.'.format('; '.join(offenders)),
)
def create_os_error_message(
error: OSError, show_traceback: bool, using_user_site: bool
error: OSError, show_traceback: bool, using_user_site: bool,
) -> str:
"""Format an error message for an OSError
@ -724,48 +723,48 @@ def create_os_error_message(
parts = []
# Mention the error if we are not going to show a traceback
parts.append("Could not install packages due to an OSError")
parts.append('Could not install packages due to an OSError')
if not show_traceback:
parts.append(": ")
parts.append(': ')
parts.append(str(error))
else:
parts.append(".")
parts.append('.')
# Spilt the error indication from a helper message (if any)
parts[-1] += "\n"
parts[-1] += '\n'
# Suggest useful actions to the user:
# (1) using user site-packages or (2) verifying the permissions
if error.errno == errno.EACCES:
user_option_part = "Consider using the `--user` option"
permissions_part = "Check the permissions"
user_option_part = 'Consider using the `--user` option'
permissions_part = 'Check the permissions'
if not running_under_virtualenv() and not using_user_site:
parts.extend(
[
user_option_part,
" or ",
' or ',
permissions_part.lower(),
]
],
)
else:
parts.append(permissions_part)
parts.append(".\n")
parts.append('.\n')
# Suggest the user to enable Long Paths if path length is
# more than 260
if (
WINDOWS
and error.errno == errno.ENOENT
and error.filename
and len(error.filename) > 260
WINDOWS and
error.errno == errno.ENOENT and
error.filename and
len(error.filename) > 260
):
parts.append(
"HINT: This error might have occurred since "
"this system does not have Windows Long Path "
"support enabled. You can find information on "
"how to enable this at "
"https://pip.pypa.io/warnings/enable-long-paths\n"
'HINT: This error might have occurred since '
'this system does not have Windows Long Path '
'support enabled. You can find information on '
'how to enable this at '
'https://pip.pypa.io/warnings/enable-long-paths\n',
)
return "".join(parts).strip() + "\n"
return ''.join(parts).strip() + '\n'

View file

@ -1,9 +1,15 @@
from __future__ import annotations
import json
import logging
from optparse import Values
from typing import TYPE_CHECKING, Iterator, List, Optional, Sequence, Tuple, cast
from pip._vendor.packaging.utils import canonicalize_name
from typing import cast
from typing import Iterator
from typing import List
from typing import Optional
from typing import Sequence
from typing import Tuple
from typing import TYPE_CHECKING
from pip._internal.cli import cmdoptions
from pip._internal.cli.req_command import IndexGroupCommand
@ -11,11 +17,14 @@ from pip._internal.cli.status_codes import SUCCESS
from pip._internal.exceptions import CommandError
from pip._internal.index.collector import LinkCollector
from pip._internal.index.package_finder import PackageFinder
from pip._internal.metadata import BaseDistribution, get_environment
from pip._internal.metadata import BaseDistribution
from pip._internal.metadata import get_environment
from pip._internal.models.selection_prefs import SelectionPreferences
from pip._internal.network.session import PipSession
from pip._internal.utils.compat import stdlib_pkgs
from pip._internal.utils.misc import tabulate, write_output
from pip._internal.utils.misc import tabulate
from pip._internal.utils.misc import write_output
from pip._vendor.packaging.utils import canonicalize_name
if TYPE_CHECKING:
from pip._internal.metadata.base import DistributionVersion
@ -49,81 +58,81 @@ class ListCommand(IndexGroupCommand):
def add_options(self) -> None:
self.cmd_opts.add_option(
"-o",
"--outdated",
action="store_true",
'-o',
'--outdated',
action='store_true',
default=False,
help="List outdated packages",
help='List outdated packages',
)
self.cmd_opts.add_option(
"-u",
"--uptodate",
action="store_true",
'-u',
'--uptodate',
action='store_true',
default=False,
help="List uptodate packages",
help='List uptodate packages',
)
self.cmd_opts.add_option(
"-e",
"--editable",
action="store_true",
'-e',
'--editable',
action='store_true',
default=False,
help="List editable projects.",
help='List editable projects.',
)
self.cmd_opts.add_option(
"-l",
"--local",
action="store_true",
'-l',
'--local',
action='store_true',
default=False,
help=(
"If in a virtualenv that has global access, do not list "
"globally-installed packages."
'If in a virtualenv that has global access, do not list '
'globally-installed packages.'
),
)
self.cmd_opts.add_option(
"--user",
dest="user",
action="store_true",
'--user',
dest='user',
action='store_true',
default=False,
help="Only output packages installed in user-site.",
help='Only output packages installed in user-site.',
)
self.cmd_opts.add_option(cmdoptions.list_path())
self.cmd_opts.add_option(
"--pre",
action="store_true",
'--pre',
action='store_true',
default=False,
help=(
"Include pre-release and development versions. By default, "
"pip only finds stable versions."
'Include pre-release and development versions. By default, '
'pip only finds stable versions.'
),
)
self.cmd_opts.add_option(
"--format",
action="store",
dest="list_format",
default="columns",
choices=("columns", "freeze", "json"),
help="Select the output format among: columns (default), freeze, or json",
'--format',
action='store',
dest='list_format',
default='columns',
choices=('columns', 'freeze', 'json'),
help='Select the output format among: columns (default), freeze, or json',
)
self.cmd_opts.add_option(
"--not-required",
action="store_true",
dest="not_required",
help="List packages that are not dependencies of installed packages.",
'--not-required',
action='store_true',
dest='not_required',
help='List packages that are not dependencies of installed packages.',
)
self.cmd_opts.add_option(
"--exclude-editable",
action="store_false",
dest="include_editable",
help="Exclude editable package from output.",
'--exclude-editable',
action='store_false',
dest='include_editable',
help='Exclude editable package from output.',
)
self.cmd_opts.add_option(
"--include-editable",
action="store_true",
dest="include_editable",
help="Include editable package from output.",
'--include-editable',
action='store_true',
dest='include_editable',
help='Include editable package from output.',
default=True,
)
self.cmd_opts.add_option(cmdoptions.list_exclude())
@ -133,7 +142,7 @@ class ListCommand(IndexGroupCommand):
self.parser.insert_option_group(0, self.cmd_opts)
def _build_package_finder(
self, options: Values, session: PipSession
self, options: Values, session: PipSession,
) -> PackageFinder:
"""
Create a package finder appropriate to this list command.
@ -149,12 +158,12 @@ class ListCommand(IndexGroupCommand):
return PackageFinder.create(
link_collector=link_collector,
selection_prefs=selection_prefs,
use_deprecated_html5lib="html5lib" in options.deprecated_features_enabled,
use_deprecated_html5lib='html5lib' in options.deprecated_features_enabled,
)
def run(self, options: Values, args: List[str]) -> int:
def run(self, options: Values, args: list[str]) -> int:
if options.outdated and options.uptodate:
raise CommandError("Options --outdated and --uptodate cannot be combined.")
raise CommandError('Options --outdated and --uptodate cannot be combined.')
cmdoptions.check_list_path_option(options)
@ -162,8 +171,8 @@ class ListCommand(IndexGroupCommand):
if options.excludes:
skip.update(canonicalize_name(n) for n in options.excludes)
packages: "_ProcessedDists" = [
cast("_DistWithLatestInfo", d)
packages: _ProcessedDists = [
cast('_DistWithLatestInfo', d)
for d in get_environment(options.path).iter_installed_distributions(
local_only=options.local,
user_only=options.user,
@ -189,8 +198,8 @@ class ListCommand(IndexGroupCommand):
return SUCCESS
def get_outdated(
self, packages: "_ProcessedDists", options: Values
) -> "_ProcessedDists":
self, packages: _ProcessedDists, options: Values,
) -> _ProcessedDists:
return [
dist
for dist in self.iter_packages_latest_infos(packages, options)
@ -198,8 +207,8 @@ class ListCommand(IndexGroupCommand):
]
def get_uptodate(
self, packages: "_ProcessedDists", options: Values
) -> "_ProcessedDists":
self, packages: _ProcessedDists, options: Values,
) -> _ProcessedDists:
return [
dist
for dist in self.iter_packages_latest_infos(packages, options)
@ -207,8 +216,8 @@ class ListCommand(IndexGroupCommand):
]
def get_not_required(
self, packages: "_ProcessedDists", options: Values
) -> "_ProcessedDists":
self, packages: _ProcessedDists, options: Values,
) -> _ProcessedDists:
dep_keys = {
canonicalize_name(dep.name)
for dist in packages
@ -221,14 +230,14 @@ class ListCommand(IndexGroupCommand):
return list({pkg for pkg in packages if pkg.canonical_name not in dep_keys})
def iter_packages_latest_infos(
self, packages: "_ProcessedDists", options: Values
) -> Iterator["_DistWithLatestInfo"]:
self, packages: _ProcessedDists, options: Values,
) -> Iterator[_DistWithLatestInfo]:
with self._build_session(options) as session:
finder = self._build_package_finder(options, session)
def latest_info(
dist: "_DistWithLatestInfo",
) -> Optional["_DistWithLatestInfo"]:
dist: _DistWithLatestInfo,
) -> _DistWithLatestInfo | None:
all_candidates = finder.find_all_candidates(dist.canonical_name)
if not options.pre:
# Remove prereleases
@ -247,9 +256,9 @@ class ListCommand(IndexGroupCommand):
remote_version = best_candidate.version
if best_candidate.link.is_wheel:
typ = "wheel"
typ = 'wheel'
else:
typ = "sdist"
typ = 'sdist'
dist.latest_version = remote_version
dist.latest_filetype = typ
return dist
@ -259,28 +268,28 @@ class ListCommand(IndexGroupCommand):
yield dist
def output_package_listing(
self, packages: "_ProcessedDists", options: Values
self, packages: _ProcessedDists, options: Values,
) -> None:
packages = sorted(
packages,
key=lambda dist: dist.canonical_name,
)
if options.list_format == "columns" and packages:
if options.list_format == 'columns' and packages:
data, header = format_for_columns(packages, options)
self.output_package_listing_columns(data, header)
elif options.list_format == "freeze":
elif options.list_format == 'freeze':
for dist in packages:
if options.verbose >= 1:
write_output(
"%s==%s (%s)", dist.raw_name, dist.version, dist.location
'%s==%s (%s)', dist.raw_name, dist.version, dist.location,
)
else:
write_output("%s==%s", dist.raw_name, dist.version)
elif options.list_format == "json":
write_output('%s==%s', dist.raw_name, dist.version)
elif options.list_format == 'json':
write_output(format_for_json(packages, options))
def output_package_listing_columns(
self, data: List[List[str]], header: List[str]
self, data: list[list[str]], header: list[str],
) -> None:
# insert the header first: we need to know the size of column names
if len(data) > 0:
@ -290,33 +299,33 @@ class ListCommand(IndexGroupCommand):
# Create and add a separator.
if len(data) > 0:
pkg_strings.insert(1, " ".join(map(lambda x: "-" * x, sizes)))
pkg_strings.insert(1, ' '.join(map(lambda x: '-' * x, sizes)))
for val in pkg_strings:
write_output(val)
def format_for_columns(
pkgs: "_ProcessedDists", options: Values
) -> Tuple[List[List[str]], List[str]]:
pkgs: _ProcessedDists, options: Values,
) -> tuple[list[list[str]], list[str]]:
"""
Convert the package data into something usable
by output_package_listing_columns.
"""
header = ["Package", "Version"]
header = ['Package', 'Version']
running_outdated = options.outdated
if running_outdated:
header.extend(["Latest", "Type"])
header.extend(['Latest', 'Type'])
has_editables = any(x.editable for x in pkgs)
if has_editables:
header.append("Editable project location")
header.append('Editable project location')
if options.verbose >= 1:
header.append("Location")
header.append('Location')
if options.verbose >= 1:
header.append("Installer")
header.append('Installer')
data = []
for proj in pkgs:
@ -329,10 +338,10 @@ def format_for_columns(
row.append(proj.latest_filetype)
if has_editables:
row.append(proj.editable_project_location or "")
row.append(proj.editable_project_location or '')
if options.verbose >= 1:
row.append(proj.location or "")
row.append(proj.location or '')
if options.verbose >= 1:
row.append(proj.installer)
@ -341,21 +350,21 @@ def format_for_columns(
return data, header
def format_for_json(packages: "_ProcessedDists", options: Values) -> str:
def format_for_json(packages: _ProcessedDists, options: Values) -> str:
data = []
for dist in packages:
info = {
"name": dist.raw_name,
"version": str(dist.version),
'name': dist.raw_name,
'version': str(dist.version),
}
if options.verbose >= 1:
info["location"] = dist.location or ""
info["installer"] = dist.installer
info['location'] = dist.location or ''
info['installer'] = dist.installer
if options.outdated:
info["latest_version"] = str(dist.latest_version)
info["latest_filetype"] = dist.latest_filetype
info['latest_version'] = str(dist.latest_version)
info['latest_filetype'] = dist.latest_filetype
editable_project_location = dist.editable_project_location
if editable_project_location:
info["editable_project_location"] = editable_project_location
info['editable_project_location'] = editable_project_location
data.append(info)
return json.dumps(data)

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import logging
import shutil
import sys
@ -5,19 +7,22 @@ import textwrap
import xmlrpc.client
from collections import OrderedDict
from optparse import Values
from typing import TYPE_CHECKING, Dict, List, Optional
from pip._vendor.packaging.version import parse as parse_version
from typing import Dict
from typing import List
from typing import Optional
from typing import TYPE_CHECKING
from pip._internal.cli.base_command import Command
from pip._internal.cli.req_command import SessionCommandMixin
from pip._internal.cli.status_codes import NO_MATCHES_FOUND, SUCCESS
from pip._internal.cli.status_codes import NO_MATCHES_FOUND
from pip._internal.cli.status_codes import SUCCESS
from pip._internal.exceptions import CommandError
from pip._internal.metadata import get_default_environment
from pip._internal.models.index import PyPI
from pip._internal.network.xmlrpc import PipXmlrpcTransport
from pip._internal.utils.logging import indent_log
from pip._internal.utils.misc import write_output
from pip._vendor.packaging.version import parse as parse_version
if TYPE_CHECKING:
from typing import TypedDict
@ -25,7 +30,7 @@ if TYPE_CHECKING:
class TransformedHit(TypedDict):
name: str
summary: str
versions: List[str]
versions: list[str]
logger = logging.getLogger(__name__)
@ -40,19 +45,19 @@ class SearchCommand(Command, SessionCommandMixin):
def add_options(self) -> None:
self.cmd_opts.add_option(
"-i",
"--index",
dest="index",
metavar="URL",
'-i',
'--index',
dest='index',
metavar='URL',
default=PyPI.pypi_url,
help="Base URL of Python Package Index (default %default)",
help='Base URL of Python Package Index (default %default)',
)
self.parser.insert_option_group(0, self.cmd_opts)
def run(self, options: Values, args: List[str]) -> int:
def run(self, options: Values, args: list[str]) -> int:
if not args:
raise CommandError("Missing required argument (search query).")
raise CommandError('Missing required argument (search query).')
query = args
pypi_hits = self.search(query, options)
hits = transform_hits(pypi_hits)
@ -66,7 +71,7 @@ class SearchCommand(Command, SessionCommandMixin):
return SUCCESS
return NO_MATCHES_FOUND
def search(self, query: List[str], options: Values) -> List[Dict[str, str]]:
def search(self, query: list[str], options: Values) -> list[dict[str, str]]:
index_url = options.index
session = self.get_default_session(options)
@ -74,9 +79,9 @@ class SearchCommand(Command, SessionCommandMixin):
transport = PipXmlrpcTransport(index_url, session)
pypi = xmlrpc.client.ServerProxy(index_url, transport)
try:
hits = pypi.search({"name": query, "summary": query}, "or")
hits = pypi.search({'name': query, 'summary': query}, 'or')
except xmlrpc.client.Fault as fault:
message = "XMLRPC request failed [code: {code}]\n{string}".format(
message = 'XMLRPC request failed [code: {code}]\n{string}'.format(
code=fault.faultCode,
string=fault.faultString,
)
@ -85,30 +90,30 @@ class SearchCommand(Command, SessionCommandMixin):
return hits
def transform_hits(hits: List[Dict[str, str]]) -> List["TransformedHit"]:
def transform_hits(hits: list[dict[str, str]]) -> list[TransformedHit]:
"""
The list from pypi is really a list of versions. We want a list of
packages with the list of versions stored inline. This converts the
list from pypi into one we can use.
"""
packages: Dict[str, "TransformedHit"] = OrderedDict()
packages: dict[str, TransformedHit] = OrderedDict()
for hit in hits:
name = hit["name"]
summary = hit["summary"]
version = hit["version"]
name = hit['name']
summary = hit['summary']
version = hit['version']
if name not in packages.keys():
packages[name] = {
"name": name,
"summary": summary,
"versions": [version],
'name': name,
'summary': summary,
'versions': [version],
}
else:
packages[name]["versions"].append(version)
packages[name]['versions'].append(version)
# if this is the highest version, replace summary and score
if version == highest_version(packages[name]["versions"]):
packages[name]["summary"] = summary
if version == highest_version(packages[name]['versions']):
packages[name]['summary'] = summary
return list(packages.values())
@ -119,23 +124,23 @@ def print_dist_installation_info(name: str, latest: str) -> None:
if dist is not None:
with indent_log():
if dist.version == latest:
write_output("INSTALLED: %s (latest)", dist.version)
write_output('INSTALLED: %s (latest)', dist.version)
else:
write_output("INSTALLED: %s", dist.version)
write_output('INSTALLED: %s', dist.version)
if parse_version(latest).pre:
write_output(
"LATEST: %s (pre-release; install"
" with `pip install --pre`)",
'LATEST: %s (pre-release; install'
' with `pip install --pre`)',
latest,
)
else:
write_output("LATEST: %s", latest)
write_output('LATEST: %s', latest)
def print_results(
hits: List["TransformedHit"],
name_column_width: Optional[int] = None,
terminal_width: Optional[int] = None,
hits: list[TransformedHit],
name_column_width: int | None = None,
terminal_width: int | None = None,
) -> None:
if not hits:
return
@ -143,26 +148,26 @@ def print_results(
name_column_width = (
max(
[
len(hit["name"]) + len(highest_version(hit.get("versions", ["-"])))
len(hit['name']) + len(highest_version(hit.get('versions', ['-'])))
for hit in hits
]
)
+ 4
],
) +
4
)
for hit in hits:
name = hit["name"]
summary = hit["summary"] or ""
latest = highest_version(hit.get("versions", ["-"]))
name = hit['name']
summary = hit['summary'] or ''
latest = highest_version(hit.get('versions', ['-']))
if terminal_width is not None:
target_width = terminal_width - name_column_width - 5
if target_width > 10:
# wrap and indent summary to fit terminal
summary_lines = textwrap.wrap(summary, target_width)
summary = ("\n" + " " * (name_column_width + 3)).join(summary_lines)
summary = ('\n' + ' ' * (name_column_width + 3)).join(summary_lines)
name_latest = f"{name} ({latest})"
line = f"{name_latest:{name_column_width}} - {summary}"
name_latest = f'{name} ({latest})'
line = f'{name_latest:{name_column_width}} - {summary}'
try:
write_output(line)
print_dist_installation_info(name, latest)
@ -170,5 +175,5 @@ def print_results(
pass
def highest_version(versions: List[str]) -> str:
def highest_version(versions: list[str]) -> str:
return max(versions, key=parse_version)

View file

@ -1,13 +1,19 @@
from __future__ import annotations
import logging
from optparse import Values
from typing import Iterator, List, NamedTuple, Optional
from pip._vendor.packaging.utils import canonicalize_name
from typing import Iterator
from typing import List
from typing import NamedTuple
from typing import Optional
from pip._internal.cli.base_command import Command
from pip._internal.cli.status_codes import ERROR, SUCCESS
from pip._internal.metadata import BaseDistribution, get_default_environment
from pip._internal.cli.status_codes import ERROR
from pip._internal.cli.status_codes import SUCCESS
from pip._internal.metadata import BaseDistribution
from pip._internal.metadata import get_default_environment
from pip._internal.utils.misc import write_output
from pip._vendor.packaging.utils import canonicalize_name
logger = logging.getLogger(__name__)
@ -25,25 +31,25 @@ class ShowCommand(Command):
def add_options(self) -> None:
self.cmd_opts.add_option(
"-f",
"--files",
dest="files",
action="store_true",
'-f',
'--files',
dest='files',
action='store_true',
default=False,
help="Show the full list of installed files for each package.",
help='Show the full list of installed files for each package.',
)
self.parser.insert_option_group(0, self.cmd_opts)
def run(self, options: Values, args: List[str]) -> int:
def run(self, options: Values, args: list[str]) -> int:
if not args:
logger.warning("ERROR: Please provide a package name or names.")
logger.warning('ERROR: Please provide a package name or names.')
return ERROR
query = args
results = search_packages_info(query)
if not print_results(
results, list_files=options.files, verbose=options.verbose
results, list_files=options.files, verbose=options.verbose,
):
return ERROR
return SUCCESS
@ -53,21 +59,21 @@ class _PackageInfo(NamedTuple):
name: str
version: str
location: str
requires: List[str]
required_by: List[str]
requires: list[str]
required_by: list[str]
installer: str
metadata_version: str
classifiers: List[str]
classifiers: list[str]
summary: str
homepage: str
author: str
author_email: str
license: str
entry_points: List[str]
files: Optional[List[str]]
entry_points: list[str]
files: list[str] | None
def search_packages_info(query: List[str]) -> Iterator[_PackageInfo]:
def search_packages_info(query: list[str]) -> Iterator[_PackageInfo]:
"""
Gather details from installed distributions. Print distribution name,
version, location, and installed files. Installed files requires a
@ -79,14 +85,14 @@ def search_packages_info(query: List[str]) -> Iterator[_PackageInfo]:
installed = {dist.canonical_name: dist for dist in env.iter_distributions()}
query_names = [canonicalize_name(name) for name in query]
missing = sorted(
[name for name, pkg in zip(query, query_names) if pkg not in installed]
[name for name, pkg in zip(query, query_names) if pkg not in installed],
)
if missing:
logger.warning("Package(s) not found: %s", ", ".join(missing))
logger.warning('Package(s) not found: %s', ', '.join(missing))
def _get_requiring_packages(current_dist: BaseDistribution) -> Iterator[str]:
return (
dist.metadata["Name"] or "UNKNOWN"
dist.metadata['Name'] or 'UNKNOWN'
for dist in installed.values()
if current_dist.canonical_name
in {canonicalize_name(d.name) for d in dist.iter_dependencies()}
@ -102,14 +108,14 @@ def search_packages_info(query: List[str]) -> Iterator[_PackageInfo]:
required_by = sorted(_get_requiring_packages(dist), key=str.lower)
try:
entry_points_text = dist.read_text("entry_points.txt")
entry_points_text = dist.read_text('entry_points.txt')
entry_points = entry_points_text.splitlines(keepends=False)
except FileNotFoundError:
entry_points = []
files_iter = dist.iter_declared_entries()
if files_iter is None:
files: Optional[List[str]] = None
files: list[str] | None = None
else:
files = sorted(files_iter)
@ -118,17 +124,17 @@ def search_packages_info(query: List[str]) -> Iterator[_PackageInfo]:
yield _PackageInfo(
name=dist.raw_name,
version=str(dist.version),
location=dist.location or "",
location=dist.location or '',
requires=requires,
required_by=required_by,
installer=dist.installer,
metadata_version=dist.metadata_version or "",
classifiers=metadata.get_all("Classifier", []),
summary=metadata.get("Summary", ""),
homepage=metadata.get("Home-page", ""),
author=metadata.get("Author", ""),
author_email=metadata.get("Author-email", ""),
license=metadata.get("License", ""),
metadata_version=dist.metadata_version or '',
classifiers=metadata.get_all('Classifier', []),
summary=metadata.get('Summary', ''),
homepage=metadata.get('Home-page', ''),
author=metadata.get('Author', ''),
author_email=metadata.get('Author-email', ''),
license=metadata.get('License', ''),
entry_points=entry_points,
files=files,
)
@ -146,33 +152,33 @@ def print_results(
for i, dist in enumerate(distributions):
results_printed = True
if i > 0:
write_output("---")
write_output('---')
write_output("Name: %s", dist.name)
write_output("Version: %s", dist.version)
write_output("Summary: %s", dist.summary)
write_output("Home-page: %s", dist.homepage)
write_output("Author: %s", dist.author)
write_output("Author-email: %s", dist.author_email)
write_output("License: %s", dist.license)
write_output("Location: %s", dist.location)
write_output("Requires: %s", ", ".join(dist.requires))
write_output("Required-by: %s", ", ".join(dist.required_by))
write_output('Name: %s', dist.name)
write_output('Version: %s', dist.version)
write_output('Summary: %s', dist.summary)
write_output('Home-page: %s', dist.homepage)
write_output('Author: %s', dist.author)
write_output('Author-email: %s', dist.author_email)
write_output('License: %s', dist.license)
write_output('Location: %s', dist.location)
write_output('Requires: %s', ', '.join(dist.requires))
write_output('Required-by: %s', ', '.join(dist.required_by))
if verbose:
write_output("Metadata-Version: %s", dist.metadata_version)
write_output("Installer: %s", dist.installer)
write_output("Classifiers:")
write_output('Metadata-Version: %s', dist.metadata_version)
write_output('Installer: %s', dist.installer)
write_output('Classifiers:')
for classifier in dist.classifiers:
write_output(" %s", classifier)
write_output("Entry-points:")
write_output(' %s', classifier)
write_output('Entry-points:')
for entry in dist.entry_points:
write_output(" %s", entry.strip())
write_output(' %s', entry.strip())
if list_files:
write_output("Files:")
write_output('Files:')
if dist.files is None:
write_output("Cannot locate RECORD or installed-files.txt")
write_output('Cannot locate RECORD or installed-files.txt')
else:
for line in dist.files:
write_output(" %s", line.strip())
write_output(' %s', line.strip())
return results_printed

View file

@ -1,19 +1,19 @@
from __future__ import annotations
import logging
from optparse import Values
from typing import List
from pip._vendor.packaging.utils import canonicalize_name
from pip._internal.cli.base_command import Command
from pip._internal.cli.req_command import SessionCommandMixin, warn_if_run_as_root
from pip._internal.cli.req_command import SessionCommandMixin
from pip._internal.cli.req_command import warn_if_run_as_root
from pip._internal.cli.status_codes import SUCCESS
from pip._internal.exceptions import InstallationError
from pip._internal.req import parse_requirements
from pip._internal.req.constructors import (
install_req_from_line,
install_req_from_parsed_requirement,
)
from pip._internal.req.constructors import install_req_from_line
from pip._internal.req.constructors import install_req_from_parsed_requirement
from pip._internal.utils.misc import protect_pip_from_modification_on_windows
from pip._vendor.packaging.utils import canonicalize_name
logger = logging.getLogger(__name__)
@ -35,28 +35,28 @@ class UninstallCommand(Command, SessionCommandMixin):
def add_options(self) -> None:
self.cmd_opts.add_option(
"-r",
"--requirement",
dest="requirements",
action="append",
'-r',
'--requirement',
dest='requirements',
action='append',
default=[],
metavar="file",
metavar='file',
help=(
"Uninstall all the packages listed in the given requirements "
"file. This option can be used multiple times."
'Uninstall all the packages listed in the given requirements '
'file. This option can be used multiple times.'
),
)
self.cmd_opts.add_option(
"-y",
"--yes",
dest="yes",
action="store_true",
'-y',
'--yes',
dest='yes',
action='store_true',
help="Don't ask for confirmation of uninstall deletions.",
)
self.parser.insert_option_group(0, self.cmd_opts)
def run(self, options: Values, args: List[str]) -> int:
def run(self, options: Values, args: list[str]) -> int:
session = self.get_default_session(options)
reqs_to_uninstall = {}
@ -69,28 +69,28 @@ class UninstallCommand(Command, SessionCommandMixin):
reqs_to_uninstall[canonicalize_name(req.name)] = req
else:
logger.warning(
"Invalid requirement: %r ignored -"
" the uninstall command expects named"
" requirements.",
'Invalid requirement: %r ignored -'
' the uninstall command expects named'
' requirements.',
name,
)
for filename in options.requirements:
for parsed_req in parse_requirements(
filename, options=options, session=session
filename, options=options, session=session,
):
req = install_req_from_parsed_requirement(
parsed_req, isolated=options.isolated_mode
parsed_req, isolated=options.isolated_mode,
)
if req.name:
reqs_to_uninstall[canonicalize_name(req.name)] = req
if not reqs_to_uninstall:
raise InstallationError(
f"You must give at least one requirement to {self.name} (see "
f'"pip help {self.name}")'
f'You must give at least one requirement to {self.name} (see '
f'"pip help {self.name}")',
)
protect_pip_from_modification_on_windows(
modifying_pip="pip" in reqs_to_uninstall
modifying_pip='pip' in reqs_to_uninstall,
)
for req in reqs_to_uninstall.values():

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import logging
import os
import shutil
@ -6,14 +8,17 @@ from typing import List
from pip._internal.cache import WheelCache
from pip._internal.cli import cmdoptions
from pip._internal.cli.req_command import RequirementCommand, with_cleanup
from pip._internal.cli.req_command import RequirementCommand
from pip._internal.cli.req_command import with_cleanup
from pip._internal.cli.status_codes import SUCCESS
from pip._internal.exceptions import CommandError
from pip._internal.req.req_install import InstallRequirement
from pip._internal.req.req_tracker import get_requirement_tracker
from pip._internal.utils.misc import ensure_dir, normalize_path
from pip._internal.utils.misc import ensure_dir
from pip._internal.utils.misc import normalize_path
from pip._internal.utils.temp_dir import TempDirectory
from pip._internal.wheel_builder import build, should_build_for_wheel_command
from pip._internal.wheel_builder import build
from pip._internal.wheel_builder import should_build_for_wheel_command
logger = logging.getLogger(__name__)
@ -43,14 +48,14 @@ class WheelCommand(RequirementCommand):
def add_options(self) -> None:
self.cmd_opts.add_option(
"-w",
"--wheel-dir",
dest="wheel_dir",
metavar="dir",
'-w',
'--wheel-dir',
dest='wheel_dir',
metavar='dir',
default=os.curdir,
help=(
"Build wheels into <dir>, where the default is the "
"current working directory."
'Build wheels into <dir>, where the default is the '
'current working directory.'
),
)
self.cmd_opts.add_option(cmdoptions.no_binary())
@ -68,9 +73,9 @@ class WheelCommand(RequirementCommand):
self.cmd_opts.add_option(cmdoptions.progress_bar())
self.cmd_opts.add_option(
"--no-verify",
dest="no_verify",
action="store_true",
'--no-verify',
dest='no_verify',
action='store_true',
default=False,
help="Don't verify if built wheel is valid.",
)
@ -79,12 +84,12 @@ class WheelCommand(RequirementCommand):
self.cmd_opts.add_option(cmdoptions.global_options())
self.cmd_opts.add_option(
"--pre",
action="store_true",
'--pre',
action='store_true',
default=False,
help=(
"Include pre-release and development versions. By default, "
"pip only finds stable versions."
'Include pre-release and development versions. By default, '
'pip only finds stable versions.'
),
)
@ -99,7 +104,7 @@ class WheelCommand(RequirementCommand):
self.parser.insert_option_group(0, self.cmd_opts)
@with_cleanup
def run(self, options: Values, args: List[str]) -> int:
def run(self, options: Values, args: list[str]) -> int:
cmdoptions.check_install_build_global(options)
session = self.get_default_session(options)
@ -114,7 +119,7 @@ class WheelCommand(RequirementCommand):
directory = TempDirectory(
delete=not options.no_clean,
kind="wheel",
kind='wheel',
globally_managed=True,
)
@ -144,7 +149,7 @@ class WheelCommand(RequirementCommand):
requirement_set = resolver.resolve(reqs, check_supported_wheels=True)
reqs_to_build: List[InstallRequirement] = []
reqs_to_build: list[InstallRequirement] = []
for req in requirement_set.requirements.values():
if req.is_wheel:
preparer.save_linked_requirement(req)
@ -167,12 +172,12 @@ class WheelCommand(RequirementCommand):
shutil.copy(req.local_file_path, options.wheel_dir)
except OSError as e:
logger.warning(
"Building wheel for %s failed: %s",
'Building wheel for %s failed: %s',
req.name,
e,
)
build_failures.append(req)
if len(build_failures) != 0:
raise CommandError("Failed to build one or more wheels")
raise CommandError('Failed to build one or more wheels')
return SUCCESS