Skip to content

Commit

Permalink
Merge branch 'main' into introduce-resuming-downloads
Browse files Browse the repository at this point in the history
  • Loading branch information
gmargaritis authored Dec 18, 2024
2 parents 64bd385 + 3b91f42 commit d265d53
Show file tree
Hide file tree
Showing 12 changed files with 68 additions and 39 deletions.
1 change: 1 addition & 0 deletions news/12176.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Pip now returns the size, along with the number, of files cleared on ``pip cache purge`` and ``pip cache remove``
Empty file.
2 changes: 2 additions & 0 deletions news/5502.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Configured TLS server and client certificates are now used while installing build dependencies.
Consequently, the private ``_PIP_STANDALONE_CERT`` environment variable is no longer used.
6 changes: 4 additions & 2 deletions src/pip/_internal/build_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,8 @@ def _install_requirements(
# target from config file or env var should be ignored
"--target",
"",
"--cert",
finder.custom_cert or where(),
]
if logger.getEffectiveLevel() <= logging.DEBUG:
args.append("-vv")
Expand All @@ -272,19 +274,19 @@ def _install_requirements(

for host in finder.trusted_hosts:
args.extend(["--trusted-host", host])
if finder.client_cert:
args.extend(["--client-cert", finder.client_cert])
if finder.allow_all_prereleases:
args.append("--pre")
if finder.prefer_binary:
args.append("--prefer-binary")
args.append("--")
args.extend(requirements)
extra_environ = {"_PIP_STANDALONE_CERT": where()}
with open_spinner(f"Installing {kind}") as spinner:
call_subprocess(
args,
command_desc=f"pip subprocess to install {kind}",
spinner=spinner,
extra_environ=extra_environ,
)


Expand Down
5 changes: 4 additions & 1 deletion src/pip/_internal/commands/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from pip._internal.exceptions import CommandError, PipError
from pip._internal.utils import filesystem
from pip._internal.utils.logging import getLogger
from pip._internal.utils.misc import format_size

logger = getLogger(__name__)

Expand Down Expand Up @@ -180,10 +181,12 @@ def remove_cache_items(self, options: Values, args: List[Any]) -> None:
if not files:
logger.warning(no_matching_msg)

bytes_removed = 0
for filename in files:
bytes_removed += os.stat(filename).st_size
os.unlink(filename)
logger.verbose("Removed %s", filename)
logger.info("Files removed: %s", len(files))
logger.info("Files removed: %s (%s)", len(files), format_size(bytes_removed))

def purge_cache(self, options: Values, args: List[Any]) -> None:
if args:
Expand Down
14 changes: 14 additions & 0 deletions src/pip/_internal/index/package_finder.py
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,20 @@ def trusted_hosts(self) -> Iterable[str]:
for host_port in self._link_collector.session.pip_trusted_origins:
yield build_netloc(*host_port)

@property
def custom_cert(self) -> Optional[str]:
# session.verify is either a boolean (use default bundle/no SSL
# verification) or a string path to a custom CA bundle to use. We only
# care about the latter.
verify = self._link_collector.session.verify
return verify if isinstance(verify, str) else None

@property
def client_cert(self) -> Optional[str]:
cert = self._link_collector.session.cert
assert not isinstance(cert, tuple), "pip only supports PEM client certs"
return cert

@property
def allow_all_prereleases(self) -> bool:
return self._candidate_prefs.allow_all_prereleases
Expand Down
2 changes: 1 addition & 1 deletion src/pip/_internal/utils/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -764,7 +764,7 @@ def warn_if_run_as_root() -> None:
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."
"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 "
Expand Down
9 changes: 1 addition & 8 deletions src/pip/_vendor/requests/certs.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,7 @@
environment, you can change the definition of where() to return a separately
packaged CA bundle.
"""

import os

if "_PIP_STANDALONE_CERT" not in os.environ:
from pip._vendor.certifi import where
else:
def where():
return os.environ["_PIP_STANDALONE_CERT"]
from pip._vendor.certifi import where

if __name__ == "__main__":
print(where())
4 changes: 2 additions & 2 deletions tests/functional/test_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ def test_cache_purge_with_empty_cache(script: PipTestEnvironment) -> None:
and exit without an error code."""
result = script.pip("cache", "purge", allow_stderr_warning=True)
assert result.stderr == "WARNING: No matching packages\n"
assert result.stdout == "Files removed: 0\n"
assert result.stdout == "Files removed: 0 (0 bytes)\n"


@pytest.mark.usefixtures("populate_wheel_cache")
Expand All @@ -265,7 +265,7 @@ def test_cache_remove_with_bad_pattern(script: PipTestEnvironment) -> None:
and exit without an error code."""
result = script.pip("cache", "remove", "aaa", allow_stderr_warning=True)
assert result.stderr == 'WARNING: No matching packages for pattern "aaa"\n'
assert result.stdout == "Files removed: 0\n"
assert result.stdout == "Files removed: 0 (0 bytes)\n"


def test_cache_list_too_many_args(script: PipTestEnvironment) -> None:
Expand Down
32 changes: 32 additions & 0 deletions tests/functional/test_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -2361,6 +2361,38 @@ def test_install_sends_client_cert(
assert environ["SSL_CLIENT_CERT"]


def test_install_sends_certs_for_pep518_deps(
script: PipTestEnvironment,
cert_factory: CertFactory,
data: TestData,
common_wheels: Path,
) -> None:
cert_path = cert_factory()
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
ctx.load_cert_chain(cert_path, cert_path)
ctx.load_verify_locations(cafile=cert_path)
ctx.verify_mode = ssl.CERT_REQUIRED

setuptools_pkg = next(common_wheels.glob("setuptools*")).name
server = make_mock_server(ssl_context=ctx)
server.mock.side_effect = [
package_page({setuptools_pkg: f"/files/{setuptools_pkg}"}),
file_response(common_wheels / setuptools_pkg),
]
url = f"https://{server.host}:{server.port}/simple"

args = ["install", str(data.packages / "pep517_setup_and_pyproject")]
args.extend(["--index-url", url])
args.extend(["--cert", cert_path, "--client-cert", cert_path])

with server_running(server):
script.pip(*args)

for call_args in server.mock.call_args_list:
environ, _ = call_args.args
assert environ.get("SSL_CLIENT_CERT", "")


def test_install_skip_work_dir_pkg(script: PipTestEnvironment, data: TestData) -> None:
"""
Test that install of a package in working directory
Expand Down
12 changes: 7 additions & 5 deletions tests/unit/test_utils_retry.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,22 +73,24 @@ def _raise_error() -> NoReturn:
@pytest.mark.skipif(
sys.platform == "win32", reason="Too flaky on Windows due to poor timer resolution"
)
@pytest.mark.flaky(reruns=3, reruns_delay=1)
@pytest.mark.parametrize("wait_duration", [0.015, 0.045, 0.15])
def test_retry_wait(wait_duration: float) -> None:
function, timestamps = create_timestamped_callable()
# Only the first retry will be scheduled before the time limit is exceeded.
wrapped = retry(wait=wait_duration, stop_after_delay=0.01)(function)
wrapped = retry(wait=wait_duration, stop_after_delay=0.1)(function)
start_time = perf_counter()
with pytest.raises(RuntimeError):
wrapped()
assert len(timestamps) == 2
# Add a margin of 10% to permit for unavoidable variation.
assert len(timestamps) >= 2
# Just check the first retry, with a margin of 10% to permit for
# unavoidable variation.
assert timestamps[1] - start_time >= (wait_duration * 0.9)


@pytest.mark.skipif(
sys.platform == "win32", reason="Too flaky on Windows due to poor timer resolution"
)
@pytest.mark.flaky(reruns=3, reruns_delay=1)
@pytest.mark.parametrize(
"call_duration, max_allowed_calls", [(0.01, 11), (0.04, 3), (0.15, 1)]
)
Expand All @@ -109,7 +111,7 @@ class MyClass:
def __init__(self) -> None:
self.calls = 0

@retry(wait=0, stop_after_delay=0.01)
@retry(wait=0, stop_after_delay=3)
def method(self, string: str) -> str:
self.calls += 1
if self.calls >= 5:
Expand Down
20 changes: 0 additions & 20 deletions tools/vendoring/patches/requests.patch
Original file line number Diff line number Diff line change
Expand Up @@ -84,26 +84,6 @@ index 8fbcd656..094e2046 100644

try:
from urllib3.contrib import pyopenssl
diff --git a/src/pip/_vendor/requests/certs.py b/src/pip/_vendor/requests/certs.py
index be422c3e..3daf06f6 100644
--- a/src/pip/_vendor/requests/certs.py
+++ b/src/pip/_vendor/requests/certs.py
@@ -11,7 +11,14 @@ If you are packaging Requests, e.g., for a Linux distribution or a managed
environment, you can change the definition of where() to return a separately
packaged CA bundle.
"""
-from certifi import where
+
+import os
+
+if "_PIP_STANDALONE_CERT" not in os.environ:
+ from certifi import where
+else:
+ def where():
+ return os.environ["_PIP_STANDALONE_CERT"]

if __name__ == "__main__":
print(where())
diff --git a/src/pip/_vendor/requests/__init__.py b/src/pip/_vendor/requests/__init__.py
index 9d4e72c60..04230fc8d 100644
--- a/src/pip/_vendor/requests/__init__.py
Expand Down

0 comments on commit d265d53

Please sign in to comment.