Skip to content
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/3840.bugfix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Cache the update check timestamp even when the version lookup fails, so that pdm no longer appears to hang on every command when PyPI is unreachable.
10 changes: 6 additions & 4 deletions src/pdm/cli/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,15 +409,17 @@ def get_latest_version(project: Project, expire_after: int = 7 * 24 * 3600) -> s
state = json.loads(cache_file.read_text())
current_time = datetime.datetime.now(datetime.timezone.utc).timestamp()
if (last_check := state.get("last-check")) and current_time - last_check < expire_after:
return cast(str, state["latest-version"])
return cast("str | None", state.get("latest-version"))
try:
latest_version = get_latest_pdm_version_from_pypi(project)
except Exception as e:
project.core.ui.warn(f"Failed to get latest version: {e}", verbosity=termui.Verbosity.NORMAL)
latest_version = None
if latest_version is None:
return None
state.update({"latest-version": latest_version, "last-check": current_time})
# Record the check time even on failure, so that an unreachable index doesn't make
# every subsequent command wait for the request to time out again.
if latest_version is not None:
state["latest-version"] = latest_version
state["last-check"] = current_time
with contextlib.suppress(OSError):
cache_file.write_text(json.dumps(state))
return latest_version
Expand Down
9 changes: 9 additions & 0 deletions tests/cli/test_others.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,15 @@ def test_show_update_hint(pdm, project, monkeypatch):
assert "Run `pdm config check_update false` to disable the check." in r.stderr


def test_update_check_caches_failure(project, mocker):
mocked = mocker.patch("pdm.cli.actions.get_latest_pdm_version_from_pypi", side_effect=Exception("network is down"))

assert actions.get_latest_version(project) is None
assert actions.get_latest_version(project) is None

assert mocked.call_count == 1


@pytest.mark.usefixtures("repository")
def test_export_with_platform_markers(pdm, project):
pdm(
Expand Down
Loading