update
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.
@@ -3,6 +3,7 @@
|
||||
Contains interface (MultiDomainBasicAuth) and associated glue code for
|
||||
providing credentials in the context of network requests.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
@@ -47,12 +48,12 @@ class KeyRingBaseProvider(ABC):
|
||||
has_keyring: bool
|
||||
|
||||
@abstractmethod
|
||||
def get_auth_info(self, url: str, username: Optional[str]) -> Optional[AuthInfo]:
|
||||
...
|
||||
def get_auth_info(
|
||||
self, url: str, username: Optional[str]
|
||||
) -> Optional[AuthInfo]: ...
|
||||
|
||||
@abstractmethod
|
||||
def save_auth_info(self, url: str, username: str, password: str) -> None:
|
||||
...
|
||||
def save_auth_info(self, url: str, username: str, password: str) -> None: ...
|
||||
|
||||
|
||||
class KeyRingNullProvider(KeyRingBaseProvider):
|
||||
@@ -151,7 +152,7 @@ class KeyRingCliProvider(KeyRingBaseProvider):
|
||||
env["PYTHONIOENCODING"] = "utf-8"
|
||||
subprocess.run(
|
||||
[self.keyring, "set", service_name, username],
|
||||
input=f"{password}{os.linesep}".encode("utf-8"),
|
||||
input=f"{password}{os.linesep}".encode(),
|
||||
env=env,
|
||||
check=True,
|
||||
)
|
||||
@@ -270,6 +271,10 @@ class MultiDomainBasicAuth(AuthBase):
|
||||
try:
|
||||
return self.keyring_provider.get_auth_info(url, username)
|
||||
except Exception as exc:
|
||||
# Log the full exception (with stacktrace) at debug, so it'll only
|
||||
# show up when running in verbose mode.
|
||||
logger.debug("Keyring is skipped due to an exception", exc_info=True)
|
||||
# Always log a shortened version of the exception.
|
||||
logger.warning(
|
||||
"Keyring is skipped due to an exception: %s",
|
||||
str(exc),
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
|
||||
import os
|
||||
from contextlib import contextmanager
|
||||
from typing import Generator, Optional
|
||||
from datetime import datetime
|
||||
from typing import BinaryIO, Generator, Optional, Union
|
||||
|
||||
from pip._vendor.cachecontrol.cache import BaseCache
|
||||
from pip._vendor.cachecontrol.caches import FileCache
|
||||
from pip._vendor.cachecontrol.cache import SeparateBodyBaseCache
|
||||
from pip._vendor.cachecontrol.caches import SeparateBodyFileCache
|
||||
from pip._vendor.requests.models import Response
|
||||
|
||||
from pip._internal.utils.filesystem import adjacent_tmp_file, replace
|
||||
@@ -28,10 +29,22 @@ def suppressed_cache_errors() -> Generator[None, None, None]:
|
||||
pass
|
||||
|
||||
|
||||
class SafeFileCache(BaseCache):
|
||||
class SafeFileCache(SeparateBodyBaseCache):
|
||||
"""
|
||||
A file based cache which is safe to use even when the target directory may
|
||||
not be accessible or writable.
|
||||
|
||||
There is a race condition when two processes try to write and/or read the
|
||||
same entry at the same time, since each entry consists of two separate
|
||||
files (https://github.com/psf/cachecontrol/issues/324). We therefore have
|
||||
additional logic that makes sure that both files to be present before
|
||||
returning an entry; this fixes the read side of the race condition.
|
||||
|
||||
For the write side, we assume that the server will only ever return the
|
||||
same data for the same URL, which ought to be the case for files pip is
|
||||
downloading. PyPI does not have a mechanism to swap out a wheel for
|
||||
another wheel, for example. If this assumption is not true, the
|
||||
CacheControl issue will need to be fixed.
|
||||
"""
|
||||
|
||||
def __init__(self, directory: str) -> None:
|
||||
@@ -43,27 +56,51 @@ class SafeFileCache(BaseCache):
|
||||
# From cachecontrol.caches.file_cache.FileCache._fn, brought into our
|
||||
# class for backwards-compatibility and to avoid using a non-public
|
||||
# method.
|
||||
hashed = FileCache.encode(name)
|
||||
hashed = SeparateBodyFileCache.encode(name)
|
||||
parts = list(hashed[:5]) + [hashed]
|
||||
return os.path.join(self.directory, *parts)
|
||||
|
||||
def get(self, key: str) -> Optional[bytes]:
|
||||
path = self._get_cache_path(key)
|
||||
# The cache entry is only valid if both metadata and body exist.
|
||||
metadata_path = self._get_cache_path(key)
|
||||
body_path = metadata_path + ".body"
|
||||
if not (os.path.exists(metadata_path) and os.path.exists(body_path)):
|
||||
return None
|
||||
with suppressed_cache_errors():
|
||||
with open(path, "rb") as f:
|
||||
with open(metadata_path, "rb") as f:
|
||||
return f.read()
|
||||
|
||||
def set(self, key: str, value: bytes, expires: Optional[int] = None) -> None:
|
||||
path = self._get_cache_path(key)
|
||||
def _write(self, path: str, data: bytes) -> None:
|
||||
with suppressed_cache_errors():
|
||||
ensure_dir(os.path.dirname(path))
|
||||
|
||||
with adjacent_tmp_file(path) as f:
|
||||
f.write(value)
|
||||
f.write(data)
|
||||
|
||||
replace(f.name, path)
|
||||
|
||||
def set(
|
||||
self, key: str, value: bytes, expires: Union[int, datetime, None] = None
|
||||
) -> None:
|
||||
path = self._get_cache_path(key)
|
||||
self._write(path, value)
|
||||
|
||||
def delete(self, key: str) -> None:
|
||||
path = self._get_cache_path(key)
|
||||
with suppressed_cache_errors():
|
||||
os.remove(path)
|
||||
with suppressed_cache_errors():
|
||||
os.remove(path + ".body")
|
||||
|
||||
def get_body(self, key: str) -> Optional[BinaryIO]:
|
||||
# The cache entry is only valid if both metadata and body exist.
|
||||
metadata_path = self._get_cache_path(key)
|
||||
body_path = metadata_path + ".body"
|
||||
if not (os.path.exists(metadata_path) and os.path.exists(body_path)):
|
||||
return None
|
||||
with suppressed_cache_errors():
|
||||
return open(body_path, "rb")
|
||||
|
||||
def set_body(self, key: str, body: bytes) -> None:
|
||||
path = self._get_cache_path(key) + ".body"
|
||||
self._write(path, body)
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
"""Download files with progress indicators.
|
||||
"""
|
||||
|
||||
import email.message
|
||||
import logging
|
||||
import mimetypes
|
||||
import os
|
||||
from typing import Iterable, Optional, Tuple
|
||||
|
||||
from pip._vendor.requests.models import CONTENT_CHUNK_SIZE, Response
|
||||
from pip._vendor.requests.models import Response
|
||||
|
||||
from pip._internal.cli.progress_bars import get_download_progress_renderer
|
||||
from pip._internal.exceptions import NetworkConnectionError
|
||||
@@ -42,7 +43,7 @@ def _prepare_download(
|
||||
logged_url = redact_auth_from_url(url)
|
||||
|
||||
if total_length:
|
||||
logged_url = "{} ({})".format(logged_url, format_size(total_length))
|
||||
logged_url = f"{logged_url} ({format_size(total_length)})"
|
||||
|
||||
if is_from_cache(resp):
|
||||
logger.info("Using cached %s", logged_url)
|
||||
@@ -55,12 +56,12 @@ def _prepare_download(
|
||||
show_progress = False
|
||||
elif not total_length:
|
||||
show_progress = True
|
||||
elif total_length > (40 * 1000):
|
||||
elif total_length > (512 * 1024):
|
||||
show_progress = True
|
||||
else:
|
||||
show_progress = False
|
||||
|
||||
chunks = response_chunks(resp, CONTENT_CHUNK_SIZE)
|
||||
chunks = response_chunks(resp)
|
||||
|
||||
if not show_progress:
|
||||
return chunks
|
||||
|
||||
@@ -3,6 +3,7 @@ network request configuration and behavior.
|
||||
"""
|
||||
|
||||
import email.utils
|
||||
import functools
|
||||
import io
|
||||
import ipaddress
|
||||
import json
|
||||
@@ -106,6 +107,7 @@ def looks_like_ci() -> bool:
|
||||
return any(name in os.environ for name in CI_ENVIRONMENT_VARIABLES)
|
||||
|
||||
|
||||
@functools.lru_cache(maxsize=1)
|
||||
def user_agent() -> str:
|
||||
"""
|
||||
Return a string representing the user agent.
|
||||
@@ -230,7 +232,7 @@ class LocalFSAdapter(BaseAdapter):
|
||||
# to return a better error message:
|
||||
resp.status_code = 404
|
||||
resp.reason = type(exc).__name__
|
||||
resp.raw = io.BytesIO(f"{resp.reason}: {exc}".encode("utf8"))
|
||||
resp.raw = io.BytesIO(f"{resp.reason}: {exc}".encode())
|
||||
else:
|
||||
modified = email.utils.formatdate(stats.st_mtime, usegmt=True)
|
||||
content_type = mimetypes.guess_type(pathname)[0] or "text/plain"
|
||||
@@ -355,8 +357,9 @@ class PipSession(requests.Session):
|
||||
# is typically considered a transient error so we'll go ahead and
|
||||
# retry it.
|
||||
# A 500 may indicate transient error in Amazon S3
|
||||
# A 502 may be a transient error from a CDN like CloudFlare or CloudFront
|
||||
# A 520 or 527 - may indicate transient error in CloudFlare
|
||||
status_forcelist=[500, 503, 520, 527],
|
||||
status_forcelist=[500, 502, 503, 520, 527],
|
||||
# Add a small amount of back off between failed requests in
|
||||
# order to prevent hammering the service.
|
||||
backoff_factor=0.25,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from typing import Dict, Generator
|
||||
|
||||
from pip._vendor.requests.models import CONTENT_CHUNK_SIZE, Response
|
||||
from pip._vendor.requests.models import Response
|
||||
|
||||
from pip._internal.exceptions import NetworkConnectionError
|
||||
|
||||
@@ -25,6 +25,8 @@ from pip._internal.exceptions import NetworkConnectionError
|
||||
# possible to make this work.
|
||||
HEADERS: Dict[str, str] = {"Accept-Encoding": "identity"}
|
||||
|
||||
DOWNLOAD_CHUNK_SIZE = 256 * 1024
|
||||
|
||||
|
||||
def raise_for_status(resp: Response) -> None:
|
||||
http_error_msg = ""
|
||||
@@ -55,7 +57,7 @@ def raise_for_status(resp: Response) -> None:
|
||||
|
||||
|
||||
def response_chunks(
|
||||
response: Response, chunk_size: int = CONTENT_CHUNK_SIZE
|
||||
response: Response, chunk_size: int = DOWNLOAD_CHUNK_SIZE
|
||||
) -> Generator[bytes, None, None]:
|
||||
"""Given a requests Response, provide the data chunks."""
|
||||
try:
|
||||
|
||||
@@ -13,6 +13,8 @@ from pip._internal.network.utils import raise_for_status
|
||||
if TYPE_CHECKING:
|
||||
from xmlrpc.client import _HostType, _Marshallable
|
||||
|
||||
from _typeshed import SizedBuffer
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -33,7 +35,7 @@ class PipXmlrpcTransport(xmlrpc.client.Transport):
|
||||
self,
|
||||
host: "_HostType",
|
||||
handler: str,
|
||||
request_body: bytes,
|
||||
request_body: "SizedBuffer",
|
||||
verbose: bool = False,
|
||||
) -> Tuple["_Marshallable", ...]:
|
||||
assert isinstance(host, str)
|
||||
|
||||
Reference in New Issue
Block a user