[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,2 +1,3 @@
"""Contains purely network-related utilities.
"""
from __future__ import annotations

View file

@ -3,23 +3,27 @@
Contains interface (MultiDomainBasicAuth) and associated glue code for
providing credentials in the context of network requests.
"""
from __future__ import annotations
import urllib.parse
from typing import Any, Dict, List, Optional, Tuple
from pip._vendor.requests.auth import AuthBase, HTTPBasicAuth
from pip._vendor.requests.models import Request, Response
from pip._vendor.requests.utils import get_netrc_auth
from typing import Any
from typing import Dict
from typing import List
from typing import Optional
from typing import Tuple
from pip._internal.utils.logging import getLogger
from pip._internal.utils.misc import (
ask,
ask_input,
ask_password,
remove_auth_from_url,
split_auth_netloc_from_url,
)
from pip._internal.utils.misc import ask
from pip._internal.utils.misc import ask_input
from pip._internal.utils.misc import ask_password
from pip._internal.utils.misc import remove_auth_from_url
from pip._internal.utils.misc import split_auth_netloc_from_url
from pip._internal.vcs.versioncontrol import AuthInfo
from pip._vendor.requests.auth import AuthBase
from pip._vendor.requests.auth import HTTPBasicAuth
from pip._vendor.requests.models import Request
from pip._vendor.requests.models import Response
from pip._vendor.requests.utils import get_netrc_auth
logger = getLogger(__name__)
@ -31,13 +35,13 @@ except ImportError:
keyring = None # type: ignore[assignment]
except Exception as exc:
logger.warning(
"Keyring is skipped due to an exception: %s",
'Keyring is skipped due to an exception: %s',
str(exc),
)
keyring = None # type: ignore[assignment]
def get_keyring_auth(url: Optional[str], username: Optional[str]) -> Optional[AuthInfo]:
def get_keyring_auth(url: str | None, username: str | None) -> AuthInfo | None:
"""Return the tuple auth for a given url from keyring."""
global keyring
if not url or not keyring:
@ -49,21 +53,21 @@ def get_keyring_auth(url: Optional[str], username: Optional[str]) -> Optional[Au
except AttributeError:
pass
else:
logger.debug("Getting credentials from keyring for %s", url)
logger.debug('Getting credentials from keyring for %s', url)
cred = get_credential(url, username)
if cred is not None:
return cred.username, cred.password
return None
if username:
logger.debug("Getting password from keyring for %s", url)
logger.debug('Getting password from keyring for %s', url)
password = keyring.get_password(url, username)
if password:
return username, password
except Exception as exc:
logger.warning(
"Keyring is skipped due to an exception: %s",
'Keyring is skipped due to an exception: %s',
str(exc),
)
keyring = None # type: ignore[assignment]
@ -72,19 +76,19 @@ def get_keyring_auth(url: Optional[str], username: Optional[str]) -> Optional[Au
class MultiDomainBasicAuth(AuthBase):
def __init__(
self, prompting: bool = True, index_urls: Optional[List[str]] = None
self, prompting: bool = True, index_urls: list[str] | None = None,
) -> None:
self.prompting = prompting
self.index_urls = index_urls
self.passwords: Dict[str, AuthInfo] = {}
self.passwords: dict[str, AuthInfo] = {}
# When the user is prompted to enter credentials and keyring is
# available, we will offer to save them. If the user accepts,
# this value is set to the credentials they entered. After the
# request authenticates, the caller should call
# ``save_credentials`` to save these.
self._credentials_to_save: Optional[Credentials] = None
self._credentials_to_save: Credentials | None = None
def _get_index_url(self, url: str) -> Optional[str]:
def _get_index_url(self, url: str) -> str | None:
"""Return the original index URL matching the requested URL.
Cached or dynamically generated credentials may work against
@ -101,7 +105,7 @@ class MultiDomainBasicAuth(AuthBase):
return None
for u in self.index_urls:
prefix = remove_auth_from_url(u).rstrip("/") + "/"
prefix = remove_auth_from_url(u).rstrip('/') + '/'
if url.startswith(prefix):
return u
return None
@ -121,7 +125,7 @@ class MultiDomainBasicAuth(AuthBase):
# Start with the credentials embedded in the url
username, password = url_user_password
if username is not None and password is not None:
logger.debug("Found credentials in url for %s", netloc)
logger.debug('Found credentials in url for %s', netloc)
return url_user_password
# Find a matching index url for this request
@ -131,20 +135,20 @@ class MultiDomainBasicAuth(AuthBase):
index_info = split_auth_netloc_from_url(index_url)
if index_info:
index_url, _, index_url_user_password = index_info
logger.debug("Found index url %s", index_url)
logger.debug('Found index url %s', index_url)
# If an index URL was found, try its embedded credentials
if index_url and index_url_user_password[0] is not None:
username, password = index_url_user_password
if username is not None and password is not None:
logger.debug("Found credentials in index url for %s", netloc)
logger.debug('Found credentials in index url for %s', netloc)
return index_url_user_password
# Get creds from netrc if we still don't have them
if allow_netrc:
netrc_auth = get_netrc_auth(original_url)
if netrc_auth:
logger.debug("Found credentials in netrc for %s", netloc)
logger.debug('Found credentials in netrc for %s', netloc)
return netrc_auth
# If we don't have a password and keyring is available, use it.
@ -157,14 +161,14 @@ class MultiDomainBasicAuth(AuthBase):
)
# fmt: on
if kr_auth:
logger.debug("Found credentials in keyring for %s", netloc)
logger.debug('Found credentials in keyring for %s', netloc)
return kr_auth
return username, password
def _get_url_and_credentials(
self, original_url: str
) -> Tuple[str, Optional[str], Optional[str]]:
self, original_url: str,
) -> tuple[str, str | None, str | None]:
"""Return the credentials to use for the provided URL.
If allowed, netrc and keyring may be used to obtain the
@ -195,18 +199,18 @@ class MultiDomainBasicAuth(AuthBase):
# this netloc will show up as "cached" in the conditional above.
# Further, HTTPBasicAuth doesn't accept None, so it makes sense to
# cache the value that is going to be used.
username = username or ""
password = password or ""
username = username or ''
password = password or ''
# Store any acquired credentials.
self.passwords[netloc] = (username, password)
assert (
# Credentials were found
(username is not None and password is not None)
(username is not None and password is not None) or
# Credentials were not found
or (username is None and password is None)
), f"Could not load credentials from url: {original_url}"
(username is None and password is None)
), f'Could not load credentials from url: {original_url}'
return url, username, password
@ -222,28 +226,28 @@ class MultiDomainBasicAuth(AuthBase):
req = HTTPBasicAuth(username, password)(req)
# Attach a hook to handle 401 responses
req.register_hook("response", self.handle_401)
req.register_hook('response', self.handle_401)
return req
# Factored out to allow for easy patching in tests
def _prompt_for_password(
self, netloc: str
) -> Tuple[Optional[str], Optional[str], bool]:
username = ask_input(f"User for {netloc}: ")
self, netloc: str,
) -> tuple[str | None, str | None, bool]:
username = ask_input(f'User for {netloc}: ')
if not username:
return None, None, False
auth = get_keyring_auth(netloc, username)
if auth and auth[0] is not None and auth[1] is not None:
return auth[0], auth[1], False
password = ask_password("Password: ")
password = ask_password('Password: ')
return username, password, True
# Factored out to allow for easy patching in tests
def _should_save_password_to_keyring(self) -> bool:
if not keyring:
return False
return ask("Save credentials to keyring [y/N]: ", ["y", "n"]) == "y"
return ask('Save credentials to keyring [y/N]: ', ['y', 'n']) == 'y'
def handle_401(self, resp: Response, **kwargs: Any) -> Response:
# We only care about 401 responses, anything else we want to just
@ -284,14 +288,14 @@ class MultiDomainBasicAuth(AuthBase):
resp.raw.release_conn()
# Add our new username and password to the request
req = HTTPBasicAuth(username or "", password or "")(resp.request)
req.register_hook("response", self.warn_on_401)
req = HTTPBasicAuth(username or '', password or '')(resp.request)
req.register_hook('response', self.warn_on_401)
# On successful request, save the credentials that were used to
# keyring. (Note that if the user responded "no" above, this member
# is not set and nothing will be saved.)
if self._credentials_to_save:
req.register_hook("response", self.save_credentials)
req.register_hook('response', self.save_credentials)
# Send our new request
new_resp = resp.connection.send(req, **kwargs)
@ -303,13 +307,13 @@ class MultiDomainBasicAuth(AuthBase):
"""Response callback to warn about incorrect credentials."""
if resp.status_code == 401:
logger.warning(
"401 Error, Credentials not correct for %s",
'401 Error, Credentials not correct for %s',
resp.request.url,
)
def save_credentials(self, resp: Response, **kwargs: Any) -> None:
"""Response callback to save credentials on success."""
assert keyring is not None, "should never reach here without keyring"
assert keyring is not None, 'should never reach here without keyring'
if not keyring:
return
@ -317,7 +321,7 @@ class MultiDomainBasicAuth(AuthBase):
self._credentials_to_save = None
if creds and resp.status_code < 400:
try:
logger.info("Saving credentials to keyring")
logger.info('Saving credentials to keyring')
keyring.set_password(*creds)
except Exception:
logger.exception("Failed to save credentials")
logger.exception('Failed to save credentials')

View file

@ -1,20 +1,22 @@
"""HTTP cache implementation.
"""
from __future__ import annotations
import os
from contextlib import contextmanager
from typing import Iterator, Optional
from typing import Iterator
from typing import Optional
from pip._internal.utils.filesystem import adjacent_tmp_file
from pip._internal.utils.filesystem import replace
from pip._internal.utils.misc import ensure_dir
from pip._vendor.cachecontrol.cache import BaseCache
from pip._vendor.cachecontrol.caches import FileCache
from pip._vendor.requests.models import Response
from pip._internal.utils.filesystem import adjacent_tmp_file, replace
from pip._internal.utils.misc import ensure_dir
def is_from_cache(response: Response) -> bool:
return getattr(response, "from_cache", False)
return getattr(response, 'from_cache', False)
@contextmanager
@ -35,7 +37,7 @@ class SafeFileCache(BaseCache):
"""
def __init__(self, directory: str) -> None:
assert directory is not None, "Cache directory must not be None."
assert directory is not None, 'Cache directory must not be None.'
super().__init__()
self.directory = directory
@ -47,13 +49,13 @@ class SafeFileCache(BaseCache):
parts = list(hashed[:5]) + [hashed]
return os.path.join(self.directory, *parts)
def get(self, key: str) -> Optional[bytes]:
def get(self, key: str) -> bytes | None:
path = self._get_cache_path(key)
with suppressed_cache_errors():
with open(path, "rb") as f:
with open(path, 'rb') as f:
return f.read()
def set(self, key: str, value: bytes, expires: Optional[int] = None) -> None:
def set(self, key: str, value: bytes, expires: int | None = None) -> None:
path = self._get_cache_path(key)
with suppressed_cache_errors():
ensure_dir(os.path.dirname(path))

View file

@ -1,12 +1,14 @@
"""Download files with progress indicators.
"""
from __future__ import annotations
import cgi
import logging
import mimetypes
import os
from typing import Iterable, Optional, Tuple
from pip._vendor.requests.models import CONTENT_CHUNK_SIZE, Response
from typing import Iterable
from typing import Optional
from typing import Tuple
from pip._internal.cli.progress_bars import get_download_progress_renderer
from pip._internal.exceptions import NetworkConnectionError
@ -14,15 +16,21 @@ from pip._internal.models.index import PyPI
from pip._internal.models.link import Link
from pip._internal.network.cache import is_from_cache
from pip._internal.network.session import PipSession
from pip._internal.network.utils import HEADERS, raise_for_status, response_chunks
from pip._internal.utils.misc import format_size, redact_auth_from_url, splitext
from pip._internal.network.utils import HEADERS
from pip._internal.network.utils import raise_for_status
from pip._internal.network.utils import response_chunks
from pip._internal.utils.misc import format_size
from pip._internal.utils.misc import redact_auth_from_url
from pip._internal.utils.misc import splitext
from pip._vendor.requests.models import CONTENT_CHUNK_SIZE
from pip._vendor.requests.models import Response
logger = logging.getLogger(__name__)
def _get_http_response_size(resp: Response) -> Optional[int]:
def _get_http_response_size(resp: Response) -> int | None:
try:
return int(resp.headers["content-length"])
return int(resp.headers['content-length'])
except (ValueError, KeyError, TypeError):
return None
@ -42,12 +50,12 @@ 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)
logger.info('Using cached %s', logged_url)
else:
logger.info("Downloading %s", logged_url)
logger.info('Downloading %s', logged_url)
if logger.getEffectiveLevel() > logging.INFO:
show_progress = False
@ -82,7 +90,7 @@ def parse_content_disposition(content_disposition: str, default_filename: str) -
return the default filename if the result is empty.
"""
_type, params = cgi.parse_header(content_disposition)
filename = params.get("filename")
filename = params.get('filename')
if filename:
# We need to sanitize the filename to prevent directory traversal
# in case the filename contains ".." path parts.
@ -96,12 +104,12 @@ def _get_http_response_filename(resp: Response, link: Link) -> str:
"""
filename = link.filename # fallback
# Have a look at the Content-Disposition header for a better guess
content_disposition = resp.headers.get("content-disposition")
content_disposition = resp.headers.get('content-disposition')
if content_disposition:
filename = parse_content_disposition(content_disposition, filename)
ext: Optional[str] = splitext(filename)[1]
ext: str | None = splitext(filename)[1]
if not ext:
ext = mimetypes.guess_extension(resp.headers.get("content-type", ""))
ext = mimetypes.guess_extension(resp.headers.get('content-type', ''))
if ext:
filename += ext
if not ext and link.url != resp.url:
@ -112,7 +120,7 @@ def _get_http_response_filename(resp: Response, link: Link) -> str:
def _http_get_download(session: PipSession, link: Link) -> Response:
target_url = link.url.split("#", 1)[0]
target_url = link.url.split('#', 1)[0]
resp = session.get(target_url, headers=HEADERS, stream=True)
raise_for_status(resp)
return resp
@ -127,14 +135,14 @@ class Downloader:
self._session = session
self._progress_bar = progress_bar
def __call__(self, link: Link, location: str) -> Tuple[str, str]:
def __call__(self, link: Link, location: str) -> tuple[str, str]:
"""Download the file given by link into location."""
try:
resp = _http_get_download(self._session, link)
except NetworkConnectionError as e:
assert e.response is not None
logger.critical(
"HTTP error %s while getting %s", e.response.status_code, link
'HTTP error %s while getting %s', e.response.status_code, link,
)
raise
@ -142,10 +150,10 @@ class Downloader:
filepath = os.path.join(location, filename)
chunks = _prepare_download(resp, link, self._progress_bar)
with open(filepath, "wb") as content_file:
with open(filepath, 'wb') as content_file:
for chunk in chunks:
content_file.write(chunk)
content_type = resp.headers.get("Content-Type", "")
content_type = resp.headers.get('Content-Type', '')
return filepath, content_type
@ -159,8 +167,8 @@ class BatchDownloader:
self._progress_bar = progress_bar
def __call__(
self, links: Iterable[Link], location: str
) -> Iterable[Tuple[Link, Tuple[str, str]]]:
self, links: Iterable[Link], location: str,
) -> Iterable[tuple[Link, tuple[str, str]]]:
"""Download the files given by links into location."""
for link in links:
try:
@ -168,7 +176,7 @@ class BatchDownloader:
except NetworkConnectionError as e:
assert e.response is not None
logger.critical(
"HTTP error %s while getting %s",
'HTTP error %s while getting %s',
e.response.status_code,
link,
)
@ -178,8 +186,8 @@ class BatchDownloader:
filepath = os.path.join(location, filename)
chunks = _prepare_download(resp, link, self._progress_bar)
with open(filepath, "wb") as content_file:
with open(filepath, 'wb') as content_file:
for chunk in chunks:
content_file.write(chunk)
content_type = resp.headers.get("Content-Type", "")
content_type = resp.headers.get('Content-Type', '')
yield link, (filepath, content_type)

View file

@ -1,6 +1,7 @@
"""Lazy ZIP over HTTP"""
from __future__ import annotations
__all__ = ["HTTPRangeRequestUnsupported", "dist_from_wheel_url"]
__all__ = ['HTTPRangeRequestUnsupported', 'dist_from_wheel_url']
from bisect import bisect_left, bisect_right
from contextlib import contextmanager
@ -47,25 +48,25 @@ class LazyZipOverHTTP:
"""
def __init__(
self, url: str, session: PipSession, chunk_size: int = CONTENT_CHUNK_SIZE
self, url: str, session: PipSession, chunk_size: int = CONTENT_CHUNK_SIZE,
) -> None:
head = session.head(url, headers=HEADERS)
raise_for_status(head)
assert head.status_code == 200
self._session, self._url, self._chunk_size = session, url, chunk_size
self._length = int(head.headers["Content-Length"])
self._length = int(head.headers['Content-Length'])
self._file = NamedTemporaryFile()
self.truncate(self._length)
self._left: List[int] = []
self._right: List[int] = []
if "bytes" not in head.headers.get("Accept-Ranges", "none"):
raise HTTPRangeRequestUnsupported("range request is not supported")
self._left: list[int] = []
self._right: list[int] = []
if 'bytes' not in head.headers.get('Accept-Ranges', 'none'):
raise HTTPRangeRequestUnsupported('range request is not supported')
self._check_zip()
@property
def mode(self) -> str:
"""Opening mode, which is always rb."""
return "rb"
return 'rb'
@property
def name(self) -> str:
@ -117,7 +118,7 @@ class LazyZipOverHTTP:
"""Return the current position."""
return self._file.tell()
def truncate(self, size: Optional[int] = None) -> int:
def truncate(self, size: int | None = None) -> int:
"""Resize the stream to the given size in bytes.
If size is unspecified resize to the current position.
@ -131,11 +132,11 @@ class LazyZipOverHTTP:
"""Return False."""
return False
def __enter__(self) -> "LazyZipOverHTTP":
def __enter__(self) -> LazyZipOverHTTP:
self._file.__enter__()
return self
def __exit__(self, *exc: Any) -> Optional[bool]:
def __exit__(self, *exc: Any) -> bool | None:
return self._file.__exit__(*exc)
@contextmanager
@ -166,18 +167,18 @@ class LazyZipOverHTTP:
break
def _stream_response(
self, start: int, end: int, base_headers: Dict[str, str] = HEADERS
self, start: int, end: int, base_headers: dict[str, str] = HEADERS,
) -> Response:
"""Return HTTP response to a range request from start to end."""
headers = base_headers.copy()
headers["Range"] = f"bytes={start}-{end}"
headers['Range'] = f'bytes={start}-{end}'
# TODO: Get range requests to be correctly cached
headers["Cache-Control"] = "no-cache"
headers['Cache-Control'] = 'no-cache'
return self._session.get(self._url, headers=headers, stream=True)
def _merge(
self, start: int, end: int, left: int, right: int
) -> Iterator[Tuple[int, int]]:
self, start: int, end: int, left: int, right: int,
) -> Iterator[tuple[int, int]]:
"""Return an iterator of intervals to be fetched.
Args:

View file

@ -1,6 +1,7 @@
"""PipSession and supporting code, containing all pip-specific
network request configuration and behavior.
"""
from __future__ import annotations
import email.utils
import io
@ -15,27 +16,37 @@ import subprocess
import sys
import urllib.parse
import warnings
from typing import Any, Dict, Iterator, List, Mapping, Optional, Sequence, Tuple, Union
from pip._vendor import requests, urllib3
from pip._vendor.cachecontrol import CacheControlAdapter
from pip._vendor.requests.adapters import BaseAdapter, HTTPAdapter
from pip._vendor.requests.models import PreparedRequest, Response
from pip._vendor.requests.structures import CaseInsensitiveDict
from pip._vendor.urllib3.connectionpool import ConnectionPool
from pip._vendor.urllib3.exceptions import InsecureRequestWarning
from typing import Any
from typing import Dict
from typing import Iterator
from typing import List
from typing import Mapping
from typing import Optional
from typing import Sequence
from typing import Tuple
from typing import Union
from pip import __version__
from pip._internal.metadata import get_default_environment
from pip._internal.models.link import Link
from pip._internal.network.auth import MultiDomainBasicAuth
from pip._internal.network.cache import SafeFileCache
# Import ssl from compat so the initial import occurs in only one place.
from pip._internal.utils.compat import has_tls
from pip._internal.utils.glibc import libc_ver
from pip._internal.utils.misc import build_url_from_netloc, parse_netloc
from pip._internal.utils.misc import build_url_from_netloc
from pip._internal.utils.misc import parse_netloc
from pip._internal.utils.urls import url_to_path
from pip._vendor import requests
from pip._vendor import urllib3
from pip._vendor.cachecontrol import CacheControlAdapter
from pip._vendor.requests.adapters import BaseAdapter
from pip._vendor.requests.adapters import HTTPAdapter
from pip._vendor.requests.models import PreparedRequest
from pip._vendor.requests.models import Response
from pip._vendor.requests.structures import CaseInsensitiveDict
from pip._vendor.urllib3.connectionpool import ConnectionPool
from pip._vendor.urllib3.exceptions import InsecureRequestWarning
# Import ssl from compat so the initial import occurs in only one place.
logger = logging.getLogger(__name__)
@ -43,19 +54,19 @@ SecureOrigin = Tuple[str, str, Optional[Union[int, str]]]
# Ignore warning raised when using --trusted-host.
warnings.filterwarnings("ignore", category=InsecureRequestWarning)
warnings.filterwarnings('ignore', category=InsecureRequestWarning)
SECURE_ORIGINS: List[SecureOrigin] = [
SECURE_ORIGINS: list[SecureOrigin] = [
# protocol, hostname, port
# Taken from Chrome's list of secure origins (See: http://bit.ly/1qrySKC)
("https", "*", "*"),
("*", "localhost", "*"),
("*", "127.0.0.0/8", "*"),
("*", "::1/128", "*"),
("file", "*", None),
('https', '*', '*'),
('*', 'localhost', '*'),
('*', '127.0.0.0/8', '*'),
('*', '::1/128', '*'),
('file', '*', None),
# ssh is always secure.
("ssh", "*", "*"),
('ssh', '*', '*'),
]
@ -68,13 +79,13 @@ SECURE_ORIGINS: List[SecureOrigin] = [
# For more background, see: https://github.com/pypa/pip/issues/5499
CI_ENVIRONMENT_VARIABLES = (
# Azure Pipelines
"BUILD_BUILDID",
'BUILD_BUILDID',
# Jenkins
"BUILD_ID",
'BUILD_ID',
# AppVeyor, CircleCI, Codeship, Gitlab CI, Shippable, Travis CI
"CI",
'CI',
# Explicit environment variable.
"PIP_IS_CI",
'PIP_IS_CI',
)
@ -92,100 +103,100 @@ def user_agent() -> str:
"""
Return a string representing the user agent.
"""
data: Dict[str, Any] = {
"installer": {"name": "pip", "version": __version__},
"python": platform.python_version(),
"implementation": {
"name": platform.python_implementation(),
data: dict[str, Any] = {
'installer': {'name': 'pip', 'version': __version__},
'python': platform.python_version(),
'implementation': {
'name': platform.python_implementation(),
},
}
if data["implementation"]["name"] == "CPython":
data["implementation"]["version"] = platform.python_version()
elif data["implementation"]["name"] == "PyPy":
if data['implementation']['name'] == 'CPython':
data['implementation']['version'] = platform.python_version()
elif data['implementation']['name'] == 'PyPy':
pypy_version_info = sys.pypy_version_info # type: ignore
if pypy_version_info.releaselevel == "final":
if pypy_version_info.releaselevel == 'final':
pypy_version_info = pypy_version_info[:3]
data["implementation"]["version"] = ".".join(
[str(x) for x in pypy_version_info]
data['implementation']['version'] = '.'.join(
[str(x) for x in pypy_version_info],
)
elif data["implementation"]["name"] == "Jython":
elif data['implementation']['name'] == 'Jython':
# Complete Guess
data["implementation"]["version"] = platform.python_version()
elif data["implementation"]["name"] == "IronPython":
data['implementation']['version'] = platform.python_version()
elif data['implementation']['name'] == 'IronPython':
# Complete Guess
data["implementation"]["version"] = platform.python_version()
data['implementation']['version'] = platform.python_version()
if sys.platform.startswith("linux"):
if sys.platform.startswith('linux'):
from pip._vendor import distro
linux_distribution = distro.name(), distro.version(), distro.codename()
distro_infos: Dict[str, Any] = dict(
distro_infos: dict[str, Any] = dict(
filter(
lambda x: x[1],
zip(["name", "version", "id"], linux_distribution),
)
zip(['name', 'version', 'id'], linux_distribution),
),
)
libc = dict(
filter(
lambda x: x[1],
zip(["lib", "version"], libc_ver()),
)
zip(['lib', 'version'], libc_ver()),
),
)
if libc:
distro_infos["libc"] = libc
distro_infos['libc'] = libc
if distro_infos:
data["distro"] = distro_infos
data['distro'] = distro_infos
if sys.platform.startswith("darwin") and platform.mac_ver()[0]:
data["distro"] = {"name": "macOS", "version": platform.mac_ver()[0]}
if sys.platform.startswith('darwin') and platform.mac_ver()[0]:
data['distro'] = {'name': 'macOS', 'version': platform.mac_ver()[0]}
if platform.system():
data.setdefault("system", {})["name"] = platform.system()
data.setdefault('system', {})['name'] = platform.system()
if platform.release():
data.setdefault("system", {})["release"] = platform.release()
data.setdefault('system', {})['release'] = platform.release()
if platform.machine():
data["cpu"] = platform.machine()
data['cpu'] = platform.machine()
if has_tls():
import _ssl as ssl
data["openssl_version"] = ssl.OPENSSL_VERSION
data['openssl_version'] = ssl.OPENSSL_VERSION
setuptools_dist = get_default_environment().get_distribution("setuptools")
setuptools_dist = get_default_environment().get_distribution('setuptools')
if setuptools_dist is not None:
data["setuptools_version"] = str(setuptools_dist.version)
data['setuptools_version'] = str(setuptools_dist.version)
if shutil.which("rustc") is not None:
if shutil.which('rustc') is not None:
# If for any reason `rustc --version` fails, silently ignore it
try:
rustc_output = subprocess.check_output(
["rustc", "--version"], stderr=subprocess.STDOUT, timeout=0.5
['rustc', '--version'], stderr=subprocess.STDOUT, timeout=0.5,
)
except Exception:
pass
else:
if rustc_output.startswith(b"rustc "):
if rustc_output.startswith(b'rustc '):
# The format of `rustc --version` is:
# `b'rustc 1.52.1 (9bc8c42bb 2021-05-09)\n'`
# We extract just the middle (1.52.1) part
data["rustc_version"] = rustc_output.split(b" ")[1].decode()
data['rustc_version'] = rustc_output.split(b' ')[1].decode()
# Use None rather than False so as not to give the impression that
# pip knows it is not being run under CI. Rather, it is a null or
# inconclusive result. Also, we include some value rather than no
# value to make it easier to know that the check has been run.
data["ci"] = True if looks_like_ci() else None
data['ci'] = True if looks_like_ci() else None
user_data = os.environ.get("PIP_USER_AGENT_USER_DATA")
user_data = os.environ.get('PIP_USER_AGENT_USER_DATA')
if user_data is not None:
data["user_data"] = user_data
data['user_data'] = user_data
return "{data[installer][name]}/{data[installer][version]} {json}".format(
return '{data[installer][name]}/{data[installer][version]} {json}'.format(
data=data,
json=json.dumps(data, separators=(",", ":"), sort_keys=True),
json=json.dumps(data, separators=(',', ':'), sort_keys=True),
)
@ -194,10 +205,10 @@ class LocalFSAdapter(BaseAdapter):
self,
request: PreparedRequest,
stream: bool = False,
timeout: Optional[Union[float, Tuple[float, float]]] = None,
verify: Union[bool, str] = True,
cert: Optional[Union[str, Tuple[str, str]]] = None,
proxies: Optional[Mapping[str, str]] = None,
timeout: float | tuple[float, float] | None = None,
verify: bool | str = True,
cert: str | tuple[str, str] | None = None,
proxies: Mapping[str, str] | None = None,
) -> Response:
pathname = url_to_path(request.url)
@ -212,19 +223,19 @@ 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"
content_type = mimetypes.guess_type(pathname)[0] or 'text/plain'
resp.headers = CaseInsensitiveDict(
{
"Content-Type": content_type,
"Content-Length": stats.st_size,
"Last-Modified": modified,
}
'Content-Type': content_type,
'Content-Length': stats.st_size,
'Last-Modified': modified,
},
)
resp.raw = open(pathname, "rb")
resp.raw = open(pathname, 'rb')
resp.close = resp.raw.close
return resp
@ -238,8 +249,8 @@ class InsecureHTTPAdapter(HTTPAdapter):
self,
conn: ConnectionPool,
url: str,
verify: Union[bool, str],
cert: Optional[Union[str, Tuple[str, str]]],
verify: bool | str,
cert: str | tuple[str, str] | None,
) -> None:
super().cert_verify(conn=conn, url=url, verify=False, cert=cert)
@ -249,23 +260,23 @@ class InsecureCacheControlAdapter(CacheControlAdapter):
self,
conn: ConnectionPool,
url: str,
verify: Union[bool, str],
cert: Optional[Union[str, Tuple[str, str]]],
verify: bool | str,
cert: str | tuple[str, str] | None,
) -> None:
super().cert_verify(conn=conn, url=url, verify=False, cert=cert)
class PipSession(requests.Session):
timeout: Optional[int] = None
timeout: int | None = None
def __init__(
self,
*args: Any,
retries: int = 0,
cache: Optional[str] = None,
cache: str | None = None,
trusted_hosts: Sequence[str] = (),
index_urls: Optional[List[str]] = None,
index_urls: list[str] | None = None,
**kwargs: Any,
) -> None:
"""
@ -276,10 +287,10 @@ class PipSession(requests.Session):
# Namespace the attribute with "pip_" just in case to prevent
# possible conflicts with the base class.
self.pip_trusted_origins: List[Tuple[str, Optional[int]]] = []
self.pip_trusted_origins: list[tuple[str, int | None]] = []
# Attach our User Agent to the request
self.headers["User-Agent"] = user_agent()
self.headers['User-Agent'] = user_agent()
# Attach our Authentication handler to the session
self.auth = MultiDomainBasicAuth(index_urls=index_urls)
@ -327,16 +338,16 @@ class PipSession(requests.Session):
secure_adapter = HTTPAdapter(max_retries=retries)
self._trusted_host_adapter = insecure_adapter
self.mount("https://", secure_adapter)
self.mount("http://", insecure_adapter)
self.mount('https://', secure_adapter)
self.mount('http://', insecure_adapter)
# Enable file:// urls
self.mount("file://", LocalFSAdapter())
self.mount('file://', LocalFSAdapter())
for host in trusted_hosts:
self.add_trusted_host(host, suppress_logging=True)
def update_index_urls(self, new_index_urls: List[str]) -> None:
def update_index_urls(self, new_index_urls: list[str]) -> None:
"""
:param new_index_urls: New index urls to update the authentication
handler with.
@ -344,7 +355,7 @@ class PipSession(requests.Session):
self.auth.index_urls = new_index_urls
def add_trusted_host(
self, host: str, source: Optional[str] = None, suppress_logging: bool = False
self, host: str, source: str | None = None, suppress_logging: bool = False,
) -> None:
"""
:param host: It is okay to provide a host that has previously been
@ -353,9 +364,9 @@ class PipSession(requests.Session):
string came from.
"""
if not suppress_logging:
msg = f"adding trusted host: {host!r}"
msg = f'adding trusted host: {host!r}'
if source is not None:
msg += f" (from {source})"
msg += f' (from {source})'
logger.info(msg)
host_port = parse_netloc(host)
@ -363,21 +374,21 @@ class PipSession(requests.Session):
self.pip_trusted_origins.append(host_port)
self.mount(
build_url_from_netloc(host, scheme="http") + "/", self._trusted_host_adapter
build_url_from_netloc(host, scheme='http') + '/', self._trusted_host_adapter,
)
self.mount(build_url_from_netloc(host) + "/", self._trusted_host_adapter)
self.mount(build_url_from_netloc(host) + '/', self._trusted_host_adapter)
if not host_port[1]:
self.mount(
build_url_from_netloc(host, scheme="http") + ":",
build_url_from_netloc(host, scheme='http') + ':',
self._trusted_host_adapter,
)
# Mount wildcard ports for the same host.
self.mount(build_url_from_netloc(host) + ":", self._trusted_host_adapter)
self.mount(build_url_from_netloc(host) + ':', self._trusted_host_adapter)
def iter_secure_origins(self) -> Iterator[SecureOrigin]:
yield from SECURE_ORIGINS
for host, port in self.pip_trusted_origins:
yield ("*", host, "*" if port is None else port)
yield ('*', host, '*' if port is None else port)
def is_secure_origin(self, location: Link) -> bool:
# Determine if this url used a secure transport mechanism
@ -392,14 +403,14 @@ class PipSession(requests.Session):
# Don't count the repository type as part of the protocol: in
# cases such as "git+ssh", only use "ssh". (I.e., Only verify against
# the last scheme.)
origin_protocol = origin_protocol.rsplit("+", 1)[-1]
origin_protocol = origin_protocol.rsplit('+', 1)[-1]
# Determine if our origin is a secure origin by looking through our
# hardcoded list of secure origins, as well as any additional ones
# configured on this PackageFinder instance.
for secure_origin in self.iter_secure_origins():
secure_protocol, secure_host, secure_port = secure_origin
if origin_protocol != secure_protocol and secure_protocol != "*":
if origin_protocol != secure_protocol and secure_protocol != '*':
continue
try:
@ -409,9 +420,9 @@ class PipSession(requests.Session):
# We don't have both a valid address or a valid network, so
# we'll check this origin against hostnames.
if (
origin_host
and origin_host.lower() != secure_host.lower()
and secure_host != "*"
origin_host and
origin_host.lower() != secure_host.lower() and
secure_host != '*'
):
continue
else:
@ -422,9 +433,9 @@ class PipSession(requests.Session):
# Check to see if the port matches.
if (
origin_port != secure_port
and secure_port != "*"
and secure_port is not None
origin_port != secure_port and
secure_port != '*' and
secure_port is not None
):
continue
@ -436,9 +447,9 @@ class PipSession(requests.Session):
# will not accept it as a valid location to search. We will however
# log a warning that we are ignoring it.
logger.warning(
"The repository located at %s is not a trusted or secure host and "
"is being ignored. If this repository is available via HTTPS we "
"recommend you use HTTPS instead, otherwise you may silence "
'The repository located at %s is not a trusted or secure host and '
'is being ignored. If this repository is available via HTTPS we '
'recommend you use HTTPS instead, otherwise you may silence '
"this warning and allow it anyway with '--trusted-host %s'.",
origin_host,
origin_host,
@ -448,7 +459,7 @@ class PipSession(requests.Session):
def request(self, method: str, url: str, *args: Any, **kwargs: Any) -> Response:
# Allow setting a default timeout on a session
kwargs.setdefault("timeout", self.timeout)
kwargs.setdefault('timeout', self.timeout)
# Dispatch the actual request
return super().request(method, url, *args, **kwargs)

View file

@ -1,8 +1,11 @@
from typing import Dict, Iterator
from __future__ import annotations
from pip._vendor.requests.models import CONTENT_CHUNK_SIZE, Response
from typing import Dict
from typing import Iterator
from pip._internal.exceptions import NetworkConnectionError
from pip._vendor.requests.models import CONTENT_CHUNK_SIZE
from pip._vendor.requests.models import Response
# The following comments and HTTP headers were originally added by
# Donald Stufft in git commit 22c562429a61bb77172039e480873fb239dd8c03.
@ -23,31 +26,31 @@ from pip._internal.exceptions import NetworkConnectionError
# you're not asking for a compressed file and will then decompress it
# before sending because if that's the case I don't think it'll ever be
# possible to make this work.
HEADERS: Dict[str, str] = {"Accept-Encoding": "identity"}
HEADERS: dict[str, str] = {'Accept-Encoding': 'identity'}
def raise_for_status(resp: Response) -> None:
http_error_msg = ""
http_error_msg = ''
if isinstance(resp.reason, bytes):
# We attempt to decode utf-8 first because some servers
# choose to localize their reason strings. If the string
# isn't utf-8, we fall back to iso-8859-1 for all other
# encodings.
try:
reason = resp.reason.decode("utf-8")
reason = resp.reason.decode('utf-8')
except UnicodeDecodeError:
reason = resp.reason.decode("iso-8859-1")
reason = resp.reason.decode('iso-8859-1')
else:
reason = resp.reason
if 400 <= resp.status_code < 500:
http_error_msg = (
f"{resp.status_code} Client Error: {reason} for url: {resp.url}"
f'{resp.status_code} Client Error: {reason} for url: {resp.url}'
)
elif 500 <= resp.status_code < 600:
http_error_msg = (
f"{resp.status_code} Server Error: {reason} for url: {resp.url}"
f'{resp.status_code} Server Error: {reason} for url: {resp.url}'
)
if http_error_msg:
@ -55,7 +58,7 @@ def raise_for_status(resp: Response) -> None:
def response_chunks(
response: Response, chunk_size: int = CONTENT_CHUNK_SIZE
response: Response, chunk_size: int = CONTENT_CHUNK_SIZE,
) -> Iterator[bytes]:
"""Given a requests Response, provide the data chunks."""
try:

View file

@ -1,10 +1,12 @@
"""xmlrpclib.Transport implementation
"""
from __future__ import annotations
import logging
import urllib.parse
import xmlrpc.client
from typing import TYPE_CHECKING, Tuple
from typing import Tuple
from typing import TYPE_CHECKING
from pip._internal.exceptions import NetworkConnectionError
from pip._internal.network.session import PipSession
@ -22,7 +24,7 @@ class PipXmlrpcTransport(xmlrpc.client.Transport):
"""
def __init__(
self, index_url: str, session: PipSession, use_datetime: bool = False
self, index_url: str, session: PipSession, use_datetime: bool = False,
) -> None:
super().__init__(use_datetime)
index_parts = urllib.parse.urlparse(index_url)
@ -31,16 +33,16 @@ class PipXmlrpcTransport(xmlrpc.client.Transport):
def request(
self,
host: "_HostType",
host: _HostType,
handler: str,
request_body: bytes,
verbose: bool = False,
) -> Tuple["_Marshallable", ...]:
) -> tuple[_Marshallable, ...]:
assert isinstance(host, str)
parts = (self._scheme, host, handler, None, None, None)
url = urllib.parse.urlunparse(parts)
try:
headers = {"Content-Type": "text/xml"}
headers = {'Content-Type': 'text/xml'}
response = self._session.post(
url,
data=request_body,
@ -53,7 +55,7 @@ class PipXmlrpcTransport(xmlrpc.client.Transport):
except NetworkConnectionError as exc:
assert exc.response
logger.critical(
"HTTP error %s while getting %s",
'HTTP error %s while getting %s',
exc.response.status_code,
url,
)