Voice et bot modif

This commit is contained in:
pi 2026-06-16 17:09:34 +00:00
parent 189d56026b
commit 7333a22bcd
10774 changed files with 634644 additions and 933308 deletions

View file

@ -1,8 +1,8 @@
from __future__ import annotations
import contextlib
import errno
import getpass
import hashlib
import io
import logging
import os
import posixpath
@ -11,8 +11,6 @@ import stat
import sys
import sysconfig
import urllib.parse
from collections.abc import Generator, Iterable, Iterator, Mapping, Sequence
from dataclasses import dataclass
from functools import partial
from io import StringIO
from itertools import filterfalse, tee, zip_longest
@ -22,20 +20,29 @@ from typing import (
Any,
BinaryIO,
Callable,
ContextManager,
Dict,
Generator,
Iterable,
Iterator,
List,
Optional,
TextIO,
Tuple,
Type,
TypeVar,
Union,
cast,
)
from pip._vendor.packaging.requirements import Requirement
from pip._vendor.pyproject_hooks import BuildBackendHookCaller
from pip._vendor.tenacity import retry, stop_after_delay, wait_fixed
from pip import __version__
from pip._internal.exceptions import CommandError, ExternallyManagedEnvironment
from pip._internal.locations import get_major_minor_version
from pip._internal.utils.compat import WINDOWS
from pip._internal.utils.retry import retry
from pip._internal.utils.virtualenv import running_under_virtualenv
__all__ = [
@ -49,6 +56,7 @@ __all__ = [
"normalize_path",
"renames",
"get_prog",
"captured_stdout",
"ensure_dir",
"remove_auth_from_url",
"check_externally_managed",
@ -58,14 +66,12 @@ __all__ = [
logger = logging.getLogger(__name__)
T = TypeVar("T")
ExcInfo = tuple[type[BaseException], BaseException, TracebackType]
VersionInfo = tuple[int, int, int]
NetlocTuple = tuple[str, tuple[Optional[str], Optional[str]]]
ExcInfo = Tuple[Type[BaseException], BaseException, TracebackType]
VersionInfo = Tuple[int, int, int]
NetlocTuple = Tuple[str, Tuple[Optional[str], Optional[str]]]
OnExc = Callable[[FunctionType, Path, BaseException], Any]
OnErr = Callable[[FunctionType, Path, ExcInfo], Any]
FILE_CHUNK_SIZE = 1024 * 1024
def get_pip_version() -> str:
pip_pkg_dir = os.path.join(os.path.dirname(__file__), "..", "..")
@ -74,7 +80,7 @@ def get_pip_version() -> str:
return f"pip {__version__} from {pip_pkg_dir} (python {get_major_minor_version()})"
def normalize_version_info(py_version_info: tuple[int, ...]) -> tuple[int, int, int]:
def normalize_version_info(py_version_info: Tuple[int, ...]) -> Tuple[int, int, int]:
"""
Convert a tuple of ints representing a Python version to one of length
three.
@ -116,13 +122,23 @@ def get_prog() -> str:
# Retry every half second for up to 3 seconds
@retry(stop_after_delay=3, wait=0.5)
def rmtree(dir: str, ignore_errors: bool = False, onexc: OnExc | None = None) -> None:
# Tenacity raises RetryError by default, explicitly raise the original exception
@retry(reraise=True, stop=stop_after_delay(3), wait=wait_fixed(0.5))
def rmtree(
dir: str,
ignore_errors: bool = False,
onexc: Optional[OnExc] = None,
) -> None:
if ignore_errors:
onexc = _onerror_ignore
if onexc is None:
onexc = _onerror_reraise
handler: OnErr = partial(rmtree_errorhandler, onexc=onexc)
handler: OnErr = partial(
# `[func, path, Union[ExcInfo, BaseException]] -> Any` is equivalent to
# `Union[([func, path, ExcInfo] -> Any), ([func, path, BaseException] -> Any)]`.
cast(Union[OnExc, OnErr], rmtree_errorhandler),
onexc=onexc,
)
if sys.version_info >= (3, 12):
# See https://docs.python.org/3.12/whatsnew/3.12.html#shutil.
shutil.rmtree(dir, onexc=handler) # type: ignore
@ -135,13 +151,13 @@ def _onerror_ignore(*_args: Any) -> None:
def _onerror_reraise(*_args: Any) -> None:
raise # noqa: PLE0704 - Bare exception used to reraise existing exception
raise
def rmtree_errorhandler(
func: FunctionType,
path: Path,
exc_info: ExcInfo | BaseException,
exc_info: Union[ExcInfo, BaseException],
*,
onexc: OnExc = _onerror_reraise,
) -> None:
@ -268,7 +284,7 @@ def format_size(bytes: float) -> str:
return f"{int(bytes)} bytes"
def tabulate(rows: Iterable[Iterable[Any]]) -> tuple[list[str], list[int]]:
def tabulate(rows: Iterable[Iterable[Any]]) -> Tuple[List[str], List[int]]:
"""Return a list of formatted rows and a list of column sizes.
For example::
@ -300,7 +316,7 @@ def is_installable_dir(path: str) -> bool:
def read_chunks(
file: BinaryIO, size: int = FILE_CHUNK_SIZE
file: BinaryIO, size: int = io.DEFAULT_BUFFER_SIZE
) -> Generator[bytes, None, None]:
"""Yield pieces of data from a file-like object until EOF."""
while True:
@ -323,7 +339,7 @@ def normalize_path(path: str, resolve_symlinks: bool = True) -> str:
return os.path.normcase(path)
def splitext(path: str) -> tuple[str, str]:
def splitext(path: str) -> Tuple[str, str]:
"""Like os.path.splitext, but take off .tar too"""
base, ext = posixpath.splitext(path)
if base.lower().endswith(".tar"):
@ -371,7 +387,7 @@ class StreamWrapper(StringIO):
orig_stream: TextIO
@classmethod
def from_stream(cls, orig_stream: TextIO) -> StreamWrapper:
def from_stream(cls, orig_stream: TextIO) -> "StreamWrapper":
ret = cls()
ret.orig_stream = orig_stream
return ret
@ -383,15 +399,49 @@ class StreamWrapper(StringIO):
return self.orig_stream.encoding
@contextlib.contextmanager
def captured_output(stream_name: str) -> Generator[StreamWrapper, None, None]:
"""Return a context manager used by captured_stdout/stdin/stderr
that temporarily replaces the sys stream *stream_name* with a StringIO.
Taken from Lib/support/__init__.py in the CPython repo.
"""
orig_stdout = getattr(sys, stream_name)
setattr(sys, stream_name, StreamWrapper.from_stream(orig_stdout))
try:
yield getattr(sys, stream_name)
finally:
setattr(sys, stream_name, orig_stdout)
def captured_stdout() -> ContextManager[StreamWrapper]:
"""Capture the output of sys.stdout:
with captured_stdout() as stdout:
print('hello')
self.assertEqual(stdout.getvalue(), 'hello\n')
Taken from Lib/support/__init__.py in the CPython repo.
"""
return captured_output("stdout")
def captured_stderr() -> ContextManager[StreamWrapper]:
"""
See captured_stdout().
"""
return captured_output("stderr")
# Simulates an enum
def enum(*sequential: Any, **named: Any) -> type[Any]:
def enum(*sequential: Any, **named: Any) -> Type[Any]:
enums = dict(zip(sequential, range(len(sequential))), **named)
reverse = {value: key for key, value in enums.items()}
enums["reverse_mapping"] = reverse
return type("Enum", (), enums)
def build_netloc(host: str, port: int | None) -> str:
def build_netloc(host: str, port: Optional[int]) -> str:
"""
Build a netloc from a host-port pair
"""
@ -413,7 +463,7 @@ def build_url_from_netloc(netloc: str, scheme: str = "https") -> str:
return f"{scheme}://{netloc}"
def parse_netloc(netloc: str) -> tuple[str | None, int | None]:
def parse_netloc(netloc: str) -> Tuple[Optional[str], Optional[int]]:
"""
Return the host-port pair from a netloc.
"""
@ -435,7 +485,7 @@ def split_auth_from_netloc(netloc: str) -> NetlocTuple:
# behaves if more than one @ is present (which can be checked using
# the password attribute of urlsplit()'s return value).
auth, netloc = netloc.rsplit("@", 1)
pw: str | None = None
pw: Optional[str] = None
if ":" in auth:
# Split from the left because that's how urllib.parse.urlsplit()
# behaves if more than one : is present (which again can be checked
@ -472,8 +522,8 @@ def redact_netloc(netloc: str) -> str:
def _transform_url(
url: str, transform_netloc: Callable[[str], tuple[Any, ...]]
) -> tuple[str, NetlocTuple]:
url: str, transform_netloc: Callable[[str], Tuple[Any, ...]]
) -> Tuple[str, NetlocTuple]:
"""Transform and replace netloc in a url.
transform_netloc is a function taking the netloc and returning a
@ -495,13 +545,13 @@ def _get_netloc(netloc: str) -> NetlocTuple:
return split_auth_from_netloc(netloc)
def _redact_netloc(netloc: str) -> tuple[str]:
def _redact_netloc(netloc: str) -> Tuple[str]:
return (redact_netloc(netloc),)
def split_auth_netloc_from_url(
url: str,
) -> tuple[str, str, tuple[str | None, str | None]]:
) -> Tuple[str, str, Tuple[Optional[str], Optional[str]]]:
"""
Parse a url into separate netloc, auth, and url with no auth.
@ -530,10 +580,10 @@ def redact_auth_from_requirement(req: Requirement) -> str:
return str(req).replace(req.url, redact_auth_from_url(req.url))
@dataclass(frozen=True)
class HiddenText:
secret: str
redacted: str
def __init__(self, secret: str, redacted: str) -> None:
self.secret = secret
self.redacted = redacted
def __repr__(self) -> str:
return f"<HiddenText {str(self)!r}>"
@ -543,7 +593,7 @@ class HiddenText:
# This is useful for testing.
def __eq__(self, other: Any) -> bool:
if type(self) is not type(other):
if type(self) != type(other):
return False
# The string being used for redaction doesn't also have to match,
@ -606,7 +656,7 @@ def is_console_interactive() -> bool:
return sys.stdin is not None and sys.stdin.isatty()
def hash_file(path: str, blocksize: int = 1 << 20) -> tuple[Any, int]:
def hash_file(path: str, blocksize: int = 1 << 20) -> Tuple[Any, int]:
"""Return (hash, length) for path using hashlib.sha256()"""
h = hashlib.sha256()
@ -618,7 +668,7 @@ def hash_file(path: str, blocksize: int = 1 << 20) -> tuple[Any, int]:
return h, length
def pairwise(iterable: Iterable[Any]) -> Iterator[tuple[Any, Any]]:
def pairwise(iterable: Iterable[Any]) -> Iterator[Tuple[Any, Any]]:
"""
Return paired elements.
@ -630,8 +680,9 @@ def pairwise(iterable: Iterable[Any]) -> Iterator[tuple[Any, Any]]:
def partition(
pred: Callable[[T], bool], iterable: Iterable[T]
) -> tuple[Iterable[T], Iterable[T]]:
pred: Callable[[T], bool],
iterable: Iterable[T],
) -> Tuple[Iterable[T], Iterable[T]]:
"""
Use a predicate to partition entries into false entries and true entries,
like
@ -648,9 +699,9 @@ class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller):
config_holder: Any,
source_dir: str,
build_backend: str,
backend_path: str | None = None,
runner: Callable[..., None] | None = None,
python_executable: str | None = None,
backend_path: Optional[str] = None,
runner: Optional[Callable[..., None]] = None,
python_executable: Optional[str] = None,
):
super().__init__(
source_dir, build_backend, backend_path, runner, python_executable
@ -660,8 +711,8 @@ class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller):
def build_wheel(
self,
wheel_directory: str,
config_settings: Mapping[str, Any] | None = None,
metadata_directory: str | None = None,
config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
metadata_directory: Optional[str] = None,
) -> str:
cs = self.config_holder.config_settings
return super().build_wheel(
@ -671,7 +722,7 @@ class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller):
def build_sdist(
self,
sdist_directory: str,
config_settings: Mapping[str, Any] | None = None,
config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
) -> str:
cs = self.config_holder.config_settings
return super().build_sdist(sdist_directory, config_settings=cs)
@ -679,8 +730,8 @@ class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller):
def build_editable(
self,
wheel_directory: str,
config_settings: Mapping[str, Any] | None = None,
metadata_directory: str | None = None,
config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
metadata_directory: Optional[str] = None,
) -> str:
cs = self.config_holder.config_settings
return super().build_editable(
@ -688,27 +739,27 @@ class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller):
)
def get_requires_for_build_wheel(
self, config_settings: Mapping[str, Any] | None = None
) -> Sequence[str]:
self, config_settings: Optional[Dict[str, Union[str, List[str]]]] = None
) -> List[str]:
cs = self.config_holder.config_settings
return super().get_requires_for_build_wheel(config_settings=cs)
def get_requires_for_build_sdist(
self, config_settings: Mapping[str, Any] | None = None
) -> Sequence[str]:
self, config_settings: Optional[Dict[str, Union[str, List[str]]]] = None
) -> List[str]:
cs = self.config_holder.config_settings
return super().get_requires_for_build_sdist(config_settings=cs)
def get_requires_for_build_editable(
self, config_settings: Mapping[str, Any] | None = None
) -> Sequence[str]:
self, config_settings: Optional[Dict[str, Union[str, List[str]]]] = None
) -> List[str]:
cs = self.config_holder.config_settings
return super().get_requires_for_build_editable(config_settings=cs)
def prepare_metadata_for_build_wheel(
self,
metadata_directory: str,
config_settings: Mapping[str, Any] | None = None,
config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
_allow_fallback: bool = True,
) -> str:
cs = self.config_holder.config_settings
@ -721,45 +772,12 @@ class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller):
def prepare_metadata_for_build_editable(
self,
metadata_directory: str,
config_settings: Mapping[str, Any] | None = None,
config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
_allow_fallback: bool = True,
) -> str | None:
) -> str:
cs = self.config_holder.config_settings
return super().prepare_metadata_for_build_editable(
metadata_directory=metadata_directory,
config_settings=cs,
_allow_fallback=_allow_fallback,
)
def warn_if_run_as_root() -> None:
"""Output a warning for sudo users on Unix.
In a virtual environment, sudo pip still writes to virtualenv.
On Windows, users may run pip as Administrator without issues.
This warning only applies to Unix root users outside of virtualenv.
"""
if running_under_virtualenv():
return
if not hasattr(os, "getuid"):
return
# On Windows, there are no "system managed" Python packages. Installing as
# Administrator via pip is the correct way of updating system environments.
#
# We choose sys.platform over utils.compat.WINDOWS here to enable Mypy platform
# checks: https://mypy.readthedocs.io/en/stable/common_issues.html
if sys.platform == "win32" or sys.platform == "cygwin":
return
if os.getuid() != 0:
return
logger.warning(
"Running pip as the 'root' user can result in broken permissions and "
"conflicting behaviour with the system package manager, possibly "
"rendering your system unusable. "
"It is recommended to use a virtual environment instead: "
"https://pip.pypa.io/warnings/venv. "
"Use the --root-user-action option if you know what you are doing and "
"want to suppress this warning."
)