diff --git a/poetry.lock b/poetry.lock index 6a61971b1f9..beb46404fed 100644 --- a/poetry.lock +++ b/poetry.lock @@ -956,7 +956,7 @@ testing = ["func-timeout", "jaraco.itertools", "pytest (>=6)", "pytest-black (>= [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "5ebf2fd87bd0610d2ad1cb4f46102a0330413328cb1d59835a5c39479085c8aa" +content-hash = "0cbb342d793a9fec2c99fbcc07434f320c618054a30dfc4af44896ebc3e9f676" [metadata.files] attrs = [ diff --git a/pyproject.toml b/pyproject.toml index 64cbfcbd627..780f60a8813 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,6 +51,8 @@ cachecontrol = { version = "^0.12.9", extras = ["filecache"] } cachy = "^0.3.0" cleo = "^1.0.0a5" crashtest = "^0.3.0" +dulwich = "^0.20.46" +filelock = "^3.8.0" html5lib = "^1.0" importlib-metadata = { version = "^4.4", python = "<3.10" } jsonschema = "^4.10.0" @@ -70,7 +72,6 @@ tomlkit = ">=0.11.1,<1.0.0,!=0.11.2,!=0.11.3" virtualenv = ">=20.4.3,!=20.4.5,!=20.4.6" xattr = { version = "^0.9.7", markers = "sys_platform == 'darwin'" } urllib3 = "^1.26.0" -dulwich = "^0.20.46" [tool.poetry.group.dev.dependencies] pytest = "^7.1" @@ -155,6 +156,7 @@ module = [ 'cachy.*', 'cleo.*', 'crashtest.*', + 'lockfile.*', 'pexpect.*', 'pkginfo.*', 'requests_toolbelt.*', diff --git a/src/poetry/utils/authenticator.py b/src/poetry/utils/authenticator.py index 8e5239cd5c2..0278e51fdf3 100644 --- a/src/poetry/utils/authenticator.py +++ b/src/poetry/utils/authenticator.py @@ -12,12 +12,14 @@ from typing import TYPE_CHECKING from typing import Any +import lockfile import requests import requests.auth import requests.exceptions from cachecontrol import CacheControl from cachecontrol.caches import FileCache +from filelock import FileLock from poetry.config.config import Config from poetry.exceptions import PoetryException @@ -33,6 +35,26 @@ logger = logging.getLogger(__name__) +class FileLockLockFile(lockfile.LockBase): # type: ignore[misc] + # The default LockFile from the lockfile package as used by cachecontrol can remain + # locked if a process exits ungracefully. See eg + # . + # + # FileLock from the filelock package does not have this problem, so we use that to + # construct something compatible with cachecontrol. + def __init__( + self, path: str, threaded: bool = True, timeout: float | None = None + ) -> None: + super().__init__(path, threaded, timeout) + self.file_lock = FileLock(self.lock_file) + + def acquire(self, timeout: float | None = None) -> None: + self.file_lock.acquire(timeout=timeout) + + def release(self) -> None: + self.file_lock.release() + + @dataclasses.dataclass(frozen=True) class RepositoryCertificateConfig: cert: Path | None = dataclasses.field(default=None) @@ -122,7 +144,8 @@ def __init__( self._config.repository_cache_directory / (cache_id or "_default_cache") / "_http" - ) + ), + lock_class=FileLockLockFile, ) if not disable_cache else None