jenkins-bot has submitted this change. ( 
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1348534?usp=email )

Change subject: [api] Make API cache writes atomic and failure-safe
......................................................................

[api] Make API cache writes atomic and failure-safe

Serialize API cache entries into a unique temporary file using
tempfile.mkstemp before safely publishing via os.replace(). This
prevents cache readers from encountering corrupted partial writes.

It isolates disk cleanup from serialization errors, ensuring disk-level
exceptions (OSError) only trigger warnings rather than failing active
API requests. The cache maintenance script is also updated to gracefully
skip partial '.tmp' files.

Bug: T438266
Change-Id: If648db9deca999cc933cf9a662348f9a83303c3f
---
M pywikibot/data/api/_requests.py
M scripts/maintenance/cache.py
2 files changed, 25 insertions(+), 6 deletions(-)

Approvals:
  jenkins-bot: Verified
  Mahveotm: Looks good to me, approved




diff --git a/pywikibot/data/api/_requests.py b/pywikibot/data/api/_requests.py
index 4b080c0..2c13277 100644
--- a/pywikibot/data/api/_requests.py
+++ b/pywikibot/data/api/_requests.py
@@ -15,6 +15,7 @@
 import pprint
 import re
 import sys
+import tempfile
 import traceback
 from collections.abc import Callable, MutableMapping
 from contextlib import suppress
@@ -1396,12 +1397,25 @@
     def _write_cache(self, data) -> None:
         """Write data to self._cachefile_path()."""
         data = self._uniquedescriptionstr(), data, pywikibot.Timestamp.nowutc()
-        path = self._cachefile_path()
-        with suppress(OSError), path.open('wb') as f:
-            pickle.dump(data, f, protocol=config.pickle_protocol)
-            return
-        # delete invalid cache entry
-        path.unlink()
+        path = None
+        try:
+            path = self._cachefile_path()
+            fd, temp_path_str = tempfile.mkstemp(prefix=path.name + '-',
+                                                 suffix='.tmp',
+                                                 dir=path.parent)
+            tmp_path = Path(temp_path_str)
+            try:
+                with open(fd, 'wb') as f:
+                    pickle.dump(data, f, protocol=config.pickle_protocol)
+                tmp_path.replace(path)
+            finally:
+                with suppress(OSError):
+                    tmp_path.unlink()
+        except OSError as e:
+            if path:
+                pywikibot.warning(f'Could not write cache file {path}: {e}')
+            else:
+                pywikibot.warning(f'Could not write cache file: {e}')

     def submit(self):
         """Submit cached request."""
diff --git a/scripts/maintenance/cache.py b/scripts/maintenance/cache.py
index e85bc75..6dfbf7e 100755
--- a/scripts/maintenance/cache.py
+++ b/scripts/maintenance/cache.py
@@ -257,6 +257,11 @@

         for filepath in filenames:
             filename = os.path.basename(filepath)
+
+            # Skip temporary files for normal parsing/operations
+            if filename.endswith('.tmp'):
+                continue
+
             cache_dir = os.path.dirname(filepath)
             if use_accesstime is not False:
                 stinfo = os.stat(filepath)

--
To view, visit 
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1348534?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.wikimedia.org/r/settings?usp=email

Gerrit-MessageType: merged
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Change-Id: If648db9deca999cc933cf9a662348f9a83303c3f
Gerrit-Change-Number: 1348534
Gerrit-PatchSet: 5
Gerrit-Owner: Aruotes <[email protected]>
Gerrit-Reviewer: Mahveotm <[email protected]>
Gerrit-Reviewer: jenkins-bot
Gerrit-CC: Xqt <[email protected]>
_______________________________________________
Pywikibot-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to