Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: reuse request sesison within the environment #2697

Merged
merged 2 commits into from
Mar 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/2697.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Reuse the request sesison within the environment.
4 changes: 1 addition & 3 deletions src/pdm/cli/commands/publish/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import os
import pathlib
import weakref
from typing import TYPE_CHECKING, Any, Iterable
from urllib.parse import urlparse, urlunparse

Expand All @@ -29,7 +28,7 @@ def __init__(
verify_ssl: bool | None = True,
) -> None:
self.url = url
self.session = project.environment._build_session([])
self.session = project.environment.session
if verify_ssl is False:
self.session.verify = verify_ssl
elif ca_certs is not None:
Expand All @@ -38,7 +37,6 @@ def __init__(
self.ui = project.core.ui
username, password = self._ensure_credentials(username, password)
self.session.auth = (username, password)
weakref.finalize(self, self.session.close)

def _ensure_credentials(self, username: str | None, password: str | None) -> tuple[str, str]:
from pdm.models.auth import keyring
Expand Down
28 changes: 18 additions & 10 deletions src/pdm/environments/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
from contextlib import contextmanager
from functools import cached_property, partial
from pathlib import Path
from typing import TYPE_CHECKING, Generator, no_type_check
from threading import local
from typing import TYPE_CHECKING, Generator, cast, no_type_check

from pdm.exceptions import BuildError, PdmUsageError
from pdm.models.in_process import get_pep508_environment, get_python_abis, get_uname, sysconfig_get_platform
Expand Down Expand Up @@ -71,6 +72,8 @@ def __init__(self, project: Project, *, python: str | None = None) -> None:
else:
self._interpreter = PythonInfo.from_path(python)

self._local_cache = local()

@property
def is_global(self) -> bool:
"""For backward compatibility, it is opposite to ``is_local``."""
Expand Down Expand Up @@ -109,10 +112,11 @@ def target_python(self) -> unearth.TargetPython:
tp.supported_tags()
return tp

def _build_session(self, trusted_hosts: list[str]) -> PDMSession:
def _build_session(self) -> PDMSession:
from pdm.models.session import PDMSession

ca_certs = self.project.config.get("pypi.ca_certs")
trusted_hosts = get_trusted_hosts(self.project.sources)
session = PDMSession(
cache_dir=self.project.cache("http"),
trusted_hosts=trusted_hosts,
Expand All @@ -125,8 +129,18 @@ def _build_session(self, trusted_hosts: list[str]) -> PDMSession:
session.cert = (Path(certfn), Path(keyfn) if keyfn else None)

session.auth = self.auth
self.project.core.exit_stack.callback(session.close)
return session

@property
def session(self) -> PDMSession:
"""Build the session and cache it in the thread local storage."""
sess = getattr(self._local_cache, "session", None)
if sess is None:
sess = self._build_session()
self._local_cache.session = sess
return cast("PDMSession", sess)

@contextmanager
def _patch_target_python(self) -> Generator[None, None, None]:
"""Patch the packaging modules to respect the arch of target python."""
Expand Down Expand Up @@ -178,11 +192,8 @@ def get_finder(
f"{self.project.config['pypi.ignore_stored_index']}"
)

trusted_hosts = get_trusted_hosts(sources)

session = self._build_session(trusted_hosts)
finder = PDMPackageFinder(
session=session,
session=self.session,
target_python=self.target_python,
ignore_compatibility=ignore_compatibility,
no_binary=os.getenv("PDM_NO_BINARY", "").split(","),
Expand All @@ -201,10 +212,7 @@ def get_finder(
finder.add_find_links(source.url)
else:
finder.add_index_url(source.url)
try:
yield finder
finally:
session.close()
yield finder

def get_working_set(self) -> WorkingSet:
"""Get the working set based on local packages directory."""
Expand Down
14 changes: 4 additions & 10 deletions tests/models/test_candidates.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import pytest
from unearth import Link

from pdm._types import RepositoryConfig
from pdm.models.candidates import Candidate
from pdm.models.requirements import parse_requirement
from pdm.utils import is_path_relative_to, path_to_url
Expand Down Expand Up @@ -277,16 +276,11 @@ def test_ignore_invalid_py_version(project):


def test_find_candidates_from_find_links(project):
repo = project.get_repository()
repo.sources = [
RepositoryConfig(
name="test",
config_prefix="pypi",
url="http://fixtures.test/index/demo.html",
verify_ssl=False,
type="find_links",
)
project.pyproject.settings["source"] = [
{"name": "test", "url": "http://fixtures.test/index/demo.html", "verify_ssl": False, "type": "find_links"}
]
project.pyproject.write(False)
repo = project.get_repository()
candidates = list(repo.find_candidates(parse_requirement("demo")))
assert len(candidates) == 2

Expand Down
10 changes: 5 additions & 5 deletions tests/test_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -397,8 +397,8 @@ def test_preserve_log_file(project, pdm, tmp_path, mocker):
all_logs = list(tmp_path.joinpath("logs").iterdir())
assert len(all_logs) == 0

with mocker.patch.object(project.core.synchronizer_class, "synchronize", side_effect=Exception):
result = pdm(["add", "pytz"], obj=project)
assert result.exit_code != 0
install_log = next(tmp_path.joinpath("logs").glob("pdm-install-*.log"))
assert install_log.exists()
mocker.patch.object(project.core.synchronizer_class, "synchronize", side_effect=Exception)
result = pdm(["add", "pytz"], obj=project)
assert result.exit_code != 0
install_log = next(tmp_path.joinpath("logs").glob("pdm-install-*.log"))
assert install_log.exists()
Loading