From 2822b0c41116814522ceefafb69901c3cfccdb77 Mon Sep 17 00:00:00 2001 From: Amadeus Date: Thu, 29 Oct 2026 16:23:58 -0300 Subject: [PATCH] bug: count the retries in safe_save safe_save retried with `++_retry`, which in Python is a double unary plus, not an increment, so the count stayed at 0. A lock file that never became stale made safe_save call itself until RecursionError instead of giving up after 10 retries. Co-Authored-By: Claude Opus 5.5 --- lean/components/config/storage.py | 2 +- tests/components/config/test_storage.py | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/lean/components/config/storage.py b/lean/components/config/storage.py index 3dbb61f6d..7ef72cec1 100644 --- a/lean/components/config/storage.py +++ b/lean/components/config/storage.py @@ -46,7 +46,7 @@ def safe_save(data: str, path: Path, _retry: int = 0): # some else might have deleted it pass if _retry < 10: - safe_save(data, path, ++_retry) + safe_save(data, path, _retry + 1) class Storage: diff --git a/tests/components/config/test_storage.py b/tests/components/config/test_storage.py index d737341cb..185f9f4ad 100644 --- a/tests/components/config/test_storage.py +++ b/tests/components/config/test_storage.py @@ -119,3 +119,24 @@ def test_clear_deletes_file() -> None: storage.clear() assert not path.exists() + + +def test_safe_save_gives_up_after_retries_when_the_lock_file_is_never_released() -> None: + from os import utime + from time import time + from unittest import mock + + from lean.components.config.storage import safe_save + + path = Path.cwd() / "config.json" + lock_file = Path(str(path) + ".lock") + lock_file.touch() + # A lock file that never looks stale, so it is never removed + future = time() + 3600 + utime(lock_file, (future, future)) + + with mock.patch("time.sleep") as sleep: + safe_save(data="{}", path=path) + + assert sleep.call_count == 11 + assert not path.exists()