Fullscreen eingestellt.
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -88,7 +88,7 @@ def join_continuation(lines):
|
||||
['foobarbaz']
|
||||
|
||||
Not sure why, but...
|
||||
The character preceeding the backslash is also elided.
|
||||
The character preceding the backslash is also elided.
|
||||
|
||||
>>> list(join_continuation(['goo\\', 'dly']))
|
||||
['godly']
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
"""Stuff that differs in different Python versions and platform
|
||||
distributions."""
|
||||
|
||||
import importlib.resources
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from typing import IO
|
||||
|
||||
__all__ = ["get_path_uid", "stdlib_pkgs", "WINDOWS"]
|
||||
|
||||
@@ -51,6 +53,20 @@ def get_path_uid(path: str) -> int:
|
||||
return file_uid
|
||||
|
||||
|
||||
# The importlib.resources.open_text function was deprecated in 3.11 with suggested
|
||||
# replacement we use below.
|
||||
if sys.version_info < (3, 11):
|
||||
open_text_resource = importlib.resources.open_text
|
||||
else:
|
||||
|
||||
def open_text_resource(
|
||||
package: str, resource: str, encoding: str = "utf-8", errors: str = "strict"
|
||||
) -> IO[str]:
|
||||
return (importlib.resources.files(package) / resource).open(
|
||||
"r", encoding=encoding, errors=errors
|
||||
)
|
||||
|
||||
|
||||
# packages in the stdlib that may have installation metadata, but should not be
|
||||
# considered 'installed'. this theoretically could be determined based on
|
||||
# dist.location (py27:`sysconfig.get_paths()['stdlib']`,
|
||||
|
||||
@@ -87,9 +87,11 @@ def deprecated(
|
||||
(reason, f"{DEPRECATION_MSG_PREFIX}{{}}"),
|
||||
(
|
||||
gone_in,
|
||||
"pip {} will enforce this behaviour change."
|
||||
if not is_gone
|
||||
else "Since pip {}, this is no longer supported.",
|
||||
(
|
||||
"pip {} will enforce this behaviour change."
|
||||
if not is_gone
|
||||
else "Since pip {}, this is no longer supported."
|
||||
),
|
||||
),
|
||||
(
|
||||
replacement,
|
||||
@@ -97,9 +99,11 @@ def deprecated(
|
||||
),
|
||||
(
|
||||
feature_flag,
|
||||
"You can use the flag --use-feature={} to test the upcoming behaviour."
|
||||
if not is_gone
|
||||
else None,
|
||||
(
|
||||
"You can use the flag --use-feature={} to test the upcoming behaviour."
|
||||
if not is_gone
|
||||
else None
|
||||
),
|
||||
),
|
||||
(
|
||||
issue,
|
||||
|
||||
@@ -12,8 +12,8 @@ def direct_url_as_pep440_direct_reference(direct_url: DirectUrl, name: str) -> s
|
||||
requirement = name + " @ "
|
||||
fragments = []
|
||||
if isinstance(direct_url.info, VcsInfo):
|
||||
requirement += "{}+{}@{}".format(
|
||||
direct_url.info.vcs, direct_url.url, direct_url.info.commit_id
|
||||
requirement += (
|
||||
f"{direct_url.info.vcs}+{direct_url.url}@{direct_url.info.commit_id}"
|
||||
)
|
||||
elif isinstance(direct_url.info, ArchiveInfo):
|
||||
requirement += direct_url.url
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import hashlib
|
||||
from typing import TYPE_CHECKING, BinaryIO, Dict, Iterable, List, Optional
|
||||
from typing import TYPE_CHECKING, BinaryIO, Dict, Iterable, List, NoReturn, Optional
|
||||
|
||||
from pip._internal.exceptions import HashMismatch, HashMissing, InstallationError
|
||||
from pip._internal.utils.misc import read_chunks
|
||||
@@ -7,10 +7,6 @@ from pip._internal.utils.misc import read_chunks
|
||||
if TYPE_CHECKING:
|
||||
from hashlib import _Hash
|
||||
|
||||
# NoReturn introduced in 3.6.2; imported only for type checking to maintain
|
||||
# pip compatibility with older patch versions of Python 3.6
|
||||
from typing import NoReturn
|
||||
|
||||
|
||||
# The recommended hash algo of the moment. Change this whenever the state of
|
||||
# the art changes; it won't hurt backward compatibility.
|
||||
|
||||
@@ -212,7 +212,6 @@ class MaxLevelFilter(Filter):
|
||||
|
||||
|
||||
class ExcludeLoggerFilter(Filter):
|
||||
|
||||
"""
|
||||
A logging Filter that excludes records from a logger (or its children).
|
||||
"""
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import contextlib
|
||||
import errno
|
||||
import getpass
|
||||
import hashlib
|
||||
@@ -11,6 +10,7 @@ import stat
|
||||
import sys
|
||||
import sysconfig
|
||||
import urllib.parse
|
||||
from dataclasses import dataclass
|
||||
from functools import partial
|
||||
from io import StringIO
|
||||
from itertools import filterfalse, tee, zip_longest
|
||||
@@ -20,7 +20,6 @@ from typing import (
|
||||
Any,
|
||||
BinaryIO,
|
||||
Callable,
|
||||
ContextManager,
|
||||
Dict,
|
||||
Generator,
|
||||
Iterable,
|
||||
@@ -56,7 +55,6 @@ __all__ = [
|
||||
"normalize_path",
|
||||
"renames",
|
||||
"get_prog",
|
||||
"captured_stdout",
|
||||
"ensure_dir",
|
||||
"remove_auth_from_url",
|
||||
"check_externally_managed",
|
||||
@@ -399,40 +397,6 @@ 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]:
|
||||
enums = dict(zip(sequential, range(len(sequential))), **named)
|
||||
@@ -580,10 +544,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:
|
||||
def __init__(self, secret: str, redacted: str) -> None:
|
||||
self.secret = secret
|
||||
self.redacted = redacted
|
||||
secret: str
|
||||
redacted: str
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<HiddenText {str(self)!r}>"
|
||||
@@ -781,3 +745,36 @@ class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller):
|
||||
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."
|
||||
)
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
"""Utilities for defining models
|
||||
"""
|
||||
|
||||
import operator
|
||||
from typing import Any, Callable, Type
|
||||
|
||||
|
||||
class KeyBasedCompareMixin:
|
||||
"""Provides comparison capabilities that is based on a key"""
|
||||
|
||||
__slots__ = ["_compare_key", "_defining_class"]
|
||||
|
||||
def __init__(self, key: Any, defining_class: Type["KeyBasedCompareMixin"]) -> None:
|
||||
self._compare_key = key
|
||||
self._defining_class = defining_class
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self._compare_key)
|
||||
|
||||
def __lt__(self, other: Any) -> bool:
|
||||
return self._compare(other, operator.__lt__)
|
||||
|
||||
def __le__(self, other: Any) -> bool:
|
||||
return self._compare(other, operator.__le__)
|
||||
|
||||
def __gt__(self, other: Any) -> bool:
|
||||
return self._compare(other, operator.__gt__)
|
||||
|
||||
def __ge__(self, other: Any) -> bool:
|
||||
return self._compare(other, operator.__ge__)
|
||||
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
return self._compare(other, operator.__eq__)
|
||||
|
||||
def _compare(self, other: Any, method: Callable[[Any, Any], bool]) -> bool:
|
||||
if not isinstance(other, self._defining_class):
|
||||
return NotImplemented
|
||||
|
||||
return method(self._compare_key, other._compare_key)
|
||||
@@ -2,16 +2,7 @@ import logging
|
||||
import os
|
||||
import shlex
|
||||
import subprocess
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Callable,
|
||||
Iterable,
|
||||
List,
|
||||
Mapping,
|
||||
Optional,
|
||||
Union,
|
||||
)
|
||||
from typing import Any, Callable, Iterable, List, Literal, Mapping, Optional, Union
|
||||
|
||||
from pip._vendor.rich.markup import escape
|
||||
|
||||
@@ -20,12 +11,6 @@ from pip._internal.exceptions import InstallationSubprocessError
|
||||
from pip._internal.utils.logging import VERBOSE, subprocess_logger
|
||||
from pip._internal.utils.misc import HiddenText
|
||||
|
||||
if TYPE_CHECKING:
|
||||
# Literal was introduced in Python 3.8.
|
||||
#
|
||||
# TODO: Remove `if TYPE_CHECKING` when dropping support for Python 3.7.
|
||||
from typing import Literal
|
||||
|
||||
CommandArgs = List[Union[str, HiddenText]]
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import logging
|
||||
import os
|
||||
import shutil
|
||||
import stat
|
||||
import sys
|
||||
import tarfile
|
||||
import zipfile
|
||||
from typing import Iterable, List, Optional
|
||||
@@ -85,12 +86,16 @@ def is_within_directory(directory: str, target: str) -> bool:
|
||||
return prefix == abs_directory
|
||||
|
||||
|
||||
def _get_default_mode_plus_executable() -> int:
|
||||
return 0o777 & ~current_umask() | 0o111
|
||||
|
||||
|
||||
def set_extracted_file_to_default_mode_plus_executable(path: str) -> None:
|
||||
"""
|
||||
Make file present at path have execute for user/group/world
|
||||
(chmod +x) is no-op on windows per python docs
|
||||
"""
|
||||
os.chmod(path, (0o777 & ~current_umask() | 0o111))
|
||||
os.chmod(path, _get_default_mode_plus_executable())
|
||||
|
||||
|
||||
def zip_item_is_executable(info: ZipInfo) -> bool:
|
||||
@@ -151,8 +156,8 @@ def untar_file(filename: str, location: str) -> None:
|
||||
Untar the file (with path `filename`) to the destination `location`.
|
||||
All files are written based on system defaults and umask (i.e. permissions
|
||||
are not preserved), except that regular file members with any execute
|
||||
permissions (user, group, or world) have "chmod +x" applied after being
|
||||
written. Note that for windows, any execute changes using os.chmod are
|
||||
permissions (user, group, or world) have "chmod +x" applied on top of the
|
||||
default. Note that for windows, any execute changes using os.chmod are
|
||||
no-ops per the python docs.
|
||||
"""
|
||||
ensure_dir(location)
|
||||
@@ -170,62 +175,137 @@ def untar_file(filename: str, location: str) -> None:
|
||||
filename,
|
||||
)
|
||||
mode = "r:*"
|
||||
|
||||
tar = tarfile.open(filename, mode, encoding="utf-8")
|
||||
try:
|
||||
leading = has_leading_dir([member.name for member in tar.getmembers()])
|
||||
for member in tar.getmembers():
|
||||
fn = member.name
|
||||
|
||||
# PEP 706 added `tarfile.data_filter`, and made some other changes to
|
||||
# Python's tarfile module (see below). The features were backported to
|
||||
# security releases.
|
||||
try:
|
||||
data_filter = tarfile.data_filter
|
||||
except AttributeError:
|
||||
_untar_without_filter(filename, location, tar, leading)
|
||||
else:
|
||||
default_mode_plus_executable = _get_default_mode_plus_executable()
|
||||
|
||||
if leading:
|
||||
fn = split_leading_dir(fn)[1]
|
||||
path = os.path.join(location, fn)
|
||||
if not is_within_directory(location, path):
|
||||
message = (
|
||||
"The tar file ({}) has a file ({}) trying to install "
|
||||
"outside target directory ({})"
|
||||
)
|
||||
raise InstallationError(message.format(filename, path, location))
|
||||
if member.isdir():
|
||||
ensure_dir(path)
|
||||
elif member.issym():
|
||||
# Strip the leading directory from all files in the archive,
|
||||
# including hardlink targets (which are relative to the
|
||||
# unpack location).
|
||||
for member in tar.getmembers():
|
||||
name_lead, name_rest = split_leading_dir(member.name)
|
||||
member.name = name_rest
|
||||
if member.islnk():
|
||||
lnk_lead, lnk_rest = split_leading_dir(member.linkname)
|
||||
if lnk_lead == name_lead:
|
||||
member.linkname = lnk_rest
|
||||
|
||||
def pip_filter(member: tarfile.TarInfo, path: str) -> tarfile.TarInfo:
|
||||
orig_mode = member.mode
|
||||
try:
|
||||
tar._extract_member(member, path)
|
||||
except Exception as exc:
|
||||
# Some corrupt tar files seem to produce this
|
||||
# (specifically bad symlinks)
|
||||
logger.warning(
|
||||
"In the tar file %s the member %s is invalid: %s",
|
||||
filename,
|
||||
member.name,
|
||||
exc,
|
||||
try:
|
||||
member = data_filter(member, location)
|
||||
except tarfile.LinkOutsideDestinationError:
|
||||
if sys.version_info[:3] in {
|
||||
(3, 8, 17),
|
||||
(3, 9, 17),
|
||||
(3, 10, 12),
|
||||
(3, 11, 4),
|
||||
}:
|
||||
# The tarfile filter in specific Python versions
|
||||
# raises LinkOutsideDestinationError on valid input
|
||||
# (https://github.com/python/cpython/issues/107845)
|
||||
# Ignore the error there, but do use the
|
||||
# more lax `tar_filter`
|
||||
member = tarfile.tar_filter(member, location)
|
||||
else:
|
||||
raise
|
||||
except tarfile.TarError as exc:
|
||||
message = "Invalid member in the tar file {}: {}"
|
||||
# Filter error messages mention the member name.
|
||||
# No need to add it here.
|
||||
raise InstallationError(
|
||||
message.format(
|
||||
filename,
|
||||
exc,
|
||||
)
|
||||
)
|
||||
continue
|
||||
else:
|
||||
try:
|
||||
fp = tar.extractfile(member)
|
||||
except (KeyError, AttributeError) as exc:
|
||||
# Some corrupt tar files seem to produce this
|
||||
# (specifically bad symlinks)
|
||||
logger.warning(
|
||||
"In the tar file %s the member %s is invalid: %s",
|
||||
filename,
|
||||
member.name,
|
||||
exc,
|
||||
)
|
||||
continue
|
||||
ensure_dir(os.path.dirname(path))
|
||||
assert fp is not None
|
||||
with open(path, "wb") as destfp:
|
||||
shutil.copyfileobj(fp, destfp)
|
||||
fp.close()
|
||||
# Update the timestamp (useful for cython compiled files)
|
||||
tar.utime(member, path)
|
||||
# member have any execute permissions for user/group/world?
|
||||
if member.mode & 0o111:
|
||||
set_extracted_file_to_default_mode_plus_executable(path)
|
||||
if member.isfile() and orig_mode & 0o111:
|
||||
member.mode = default_mode_plus_executable
|
||||
else:
|
||||
# See PEP 706 note above.
|
||||
# The PEP changed this from `int` to `Optional[int]`,
|
||||
# where None means "use the default". Mypy doesn't
|
||||
# know this yet.
|
||||
member.mode = None # type: ignore [assignment]
|
||||
return member
|
||||
|
||||
tar.extractall(location, filter=pip_filter)
|
||||
|
||||
finally:
|
||||
tar.close()
|
||||
|
||||
|
||||
def _untar_without_filter(
|
||||
filename: str,
|
||||
location: str,
|
||||
tar: tarfile.TarFile,
|
||||
leading: bool,
|
||||
) -> None:
|
||||
"""Fallback for Python without tarfile.data_filter"""
|
||||
for member in tar.getmembers():
|
||||
fn = member.name
|
||||
if leading:
|
||||
fn = split_leading_dir(fn)[1]
|
||||
path = os.path.join(location, fn)
|
||||
if not is_within_directory(location, path):
|
||||
message = (
|
||||
"The tar file ({}) has a file ({}) trying to install "
|
||||
"outside target directory ({})"
|
||||
)
|
||||
raise InstallationError(message.format(filename, path, location))
|
||||
if member.isdir():
|
||||
ensure_dir(path)
|
||||
elif member.issym():
|
||||
try:
|
||||
tar._extract_member(member, path)
|
||||
except Exception as exc:
|
||||
# Some corrupt tar files seem to produce this
|
||||
# (specifically bad symlinks)
|
||||
logger.warning(
|
||||
"In the tar file %s the member %s is invalid: %s",
|
||||
filename,
|
||||
member.name,
|
||||
exc,
|
||||
)
|
||||
continue
|
||||
else:
|
||||
try:
|
||||
fp = tar.extractfile(member)
|
||||
except (KeyError, AttributeError) as exc:
|
||||
# Some corrupt tar files seem to produce this
|
||||
# (specifically bad symlinks)
|
||||
logger.warning(
|
||||
"In the tar file %s the member %s is invalid: %s",
|
||||
filename,
|
||||
member.name,
|
||||
exc,
|
||||
)
|
||||
continue
|
||||
ensure_dir(os.path.dirname(path))
|
||||
assert fp is not None
|
||||
with open(path, "wb") as destfp:
|
||||
shutil.copyfileobj(fp, destfp)
|
||||
fp.close()
|
||||
# Update the timestamp (useful for cython compiled files)
|
||||
tar.utime(member, path)
|
||||
# member have any execute permissions for user/group/world?
|
||||
if member.mode & 0o111:
|
||||
set_extracted_file_to_default_mode_plus_executable(path)
|
||||
|
||||
|
||||
def unpack_file(
|
||||
filename: str,
|
||||
location: str,
|
||||
|
||||
@@ -2,17 +2,10 @@ import os
|
||||
import string
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from typing import Optional
|
||||
|
||||
from .compat import WINDOWS
|
||||
|
||||
|
||||
def get_url_scheme(url: str) -> Optional[str]:
|
||||
if ":" not in url:
|
||||
return None
|
||||
return url.split(":", 1)[0].lower()
|
||||
|
||||
|
||||
def path_to_url(path: str) -> str:
|
||||
"""
|
||||
Convert a path to a file: URL. The path will be made absolute and have
|
||||
|
||||
@@ -28,7 +28,7 @@ def parse_wheel(wheel_zip: ZipFile, name: str) -> Tuple[str, Message]:
|
||||
metadata = wheel_metadata(wheel_zip, info_dir)
|
||||
version = wheel_version(metadata)
|
||||
except UnsupportedWheel as e:
|
||||
raise UnsupportedWheel(f"{name} has an invalid wheel, {str(e)}")
|
||||
raise UnsupportedWheel(f"{name} has an invalid wheel, {e}")
|
||||
|
||||
check_compatibility(version, name)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user