Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-requests-cache for 
openSUSE:Factory checked in at 2026-06-16 13:57:37
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-requests-cache (Old)
 and      /work/SRC/openSUSE:Factory/.python-requests-cache.new.1981 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-requests-cache"

Tue Jun 16 13:57:37 2026 rev:13 rq:1359666 version:1.3.2

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-requests-cache/python-requests-cache.changes  
    2026-05-25 22:00:28.350021315 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-requests-cache.new.1981/python-requests-cache.changes
    2026-06-16 14:02:22.323555856 +0200
@@ -1,0 +2,5 @@
+Mon Jun 15 20:43:00 UTC 2026 - Dirk Müller <[email protected]>
+
+- add avoid-vacuum-in-transaction.patch
+
+-------------------------------------------------------------------

New:
----
  avoid-vacuum-in-transaction.patch

----------(New B)----------
  New:
- add avoid-vacuum-in-transaction.patch
----------(New E)----------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-requests-cache.spec ++++++
--- /var/tmp/diff_new_pack.UCbDpq/_old  2026-06-16 14:02:24.347640252 +0200
+++ /var/tmp/diff_new_pack.UCbDpq/_new  2026-06-16 14:02:24.359640753 +0200
@@ -24,6 +24,8 @@
 Group:          Development/Languages/Python
 URL:            https://github.com/requests-cache/requests-cache
 Source:         
https://github.com/requests-cache/requests-cache/archive/refs/tags/v%{version}.tar.gz#/requests-cache-%{version}.tar.gz
+# PATCH-FIX-UPSTREAM
+Patch1:         avoid-vacuum-in-transaction.patch
 BuildRequires:  %{python_module hatchling >= 1.0.0}
 BuildRequires:  %{python_module pip}
 BuildRequires:  fdupes
@@ -79,7 +81,7 @@
 take a look at `CacheControl <https://github.com/ionrock/cachecontrol>`_.
 
 %prep
-%setup -q -n requests-cache-%{version}
+%autosetup -p1 -n requests-cache-%{version}
 
 %build
 %pyproject_wheel

++++++ avoid-vacuum-in-transaction.patch ++++++
>From 096255138016baeda45616783c2f16cf7410c4f6 Mon Sep 17 00:00:00 2001
From: Jordan Cook <[email protected]>
Date: Sat, 6 Jun 2026 11:48:31 -0500
Subject: [PATCH] Fix SQLite vacuum (can't run in transaction)

---
 requests_cache/backends/sqlite.py |  9 +++-
 tests/integration/test_sqlite.py  | 73 ++++++++++++++++++++++++-------
 2 files changed, 63 insertions(+), 19 deletions(-)

diff --git a/requests_cache/backends/sqlite.py 
b/requests_cache/backends/sqlite.py
index d4116fbd..5b6d7ee4 100644
--- a/requests_cache/backends/sqlite.py
+++ b/requests_cache/backends/sqlite.py
@@ -447,8 +447,13 @@ def sorted(
                     yield result
 
     def vacuum(self):
-        with self.connection(commit=True) as con:
-            con.execute('VACUUM')
+        # VACUUM cannot run inside a transaction; acquire the lock and run it 
directly
+        with self._lock:
+            if self._connection:
+                if self._active_transaction:
+                    self._connection.commit()
+                    self._active_transaction = False
+                self._connection.execute('VACUUM')
 
 
 def _format_sequence(values: Collection) -> Tuple[str, List]:
diff --git a/tests/integration/test_sqlite.py b/tests/integration/test_sqlite.py
index ad1f0a1a..72812997 100644
--- a/tests/integration/test_sqlite.py
+++ b/tests/integration/test_sqlite.py
@@ -5,14 +5,14 @@
 from os.path import join
 from tempfile import NamedTemporaryFile, gettempdir
 from threading import Thread
-from unittest.mock import patch
+from unittest.mock import MagicMock, patch
 
 import pytest
 from platformdirs import user_cache_dir
 
 from requests_cache.backends import BaseCache, SQLiteCache, SQLiteDict
 from requests_cache.backends.sqlite import MEMORY_URI
-from requests_cache.models import CachedResponse
+from requests_cache.models import CachedRequest, CachedResponse
 from requests_cache.policy import utcnow
 from tests.conftest import skip_pypy
 from tests.integration.base_cache_test import BaseCacheTest
@@ -170,28 +170,44 @@ def test_write_acquire_lock(self):
             assert mock_write.call_count == 1
 
     def test_write_retry_acquire_lock(self):
-        """Acquiring the lock should retry until it succeeds"""
+        """Acquiring the lock should retry BEGIN IMMEDIATE until it succeeds"""
         cache = self.init_cache()
         with patch.object(cache, '_connection') as mock_connection:
             mock_connection.execute.side_effect = [sqlite3.OperationalError] * 
10 + [None]
             with cache._acquire_sqlite_lock():
                 pass
-            assert mock_connection.execute.call_count == 11
+            begin_calls = [
+                c for c in mock_connection.execute.call_args_list if 'BEGIN 
IMMEDIATE' in str(c)
+            ]
+            assert len(begin_calls) == 11
 
-    def test_write_retry__other_errors(self):
-        """Errors other than 'OperationalError: database is locked' should not 
be retried"""
+    def test_write__error(self):
+        """Errors from write operations should propagate"""
         cache = self.init_cache()
-
-        error_1 = sqlite3.OperationalError('no more rows available')
-        with patch.object(cache, '_write', side_effect=error_1):
+        with patch.object(cache, '_write', 
side_effect=sqlite3.OperationalError('DB is on fire')):
             with pytest.raises(sqlite3.OperationalError):
                 cache['key_1'] = 'value_1'
-
-        error_2 = sqlite3.DatabaseError('hard drive is on fire')
-        with patch.object(cache, '_write', side_effect=error_2):
+        with patch.object(cache, '_write', 
side_effect=sqlite3.DatabaseError('HDD also on fire')):
             with pytest.raises(sqlite3.DatabaseError):
                 cache['key_1'] = 'value_1'
 
+    def test_vacuum__no_connection(self):
+        cache = self.init_cache()
+        cache._connection = None
+        cache.vacuum()
+
+    def test_vacuum__commits_active_transaction(self):
+        cache = self.init_cache()
+        mock_con = MagicMock()
+        cache._connection = mock_con
+        cache._active_transaction = True
+
+        cache.vacuum()
+
+        mock_con.commit.assert_called_once()
+        mock_con.execute.assert_called_once_with('VACUUM')
+        assert cache._active_transaction is False
+
     @skip_pypy
     @pytest.mark.parametrize('limit', [None, 50])
     def test_sorted__by_size(self, limit):
@@ -203,8 +219,8 @@ def test_sorted__by_size(self, limit):
             cache[f'key_{i}'] = f'value_{i}_{suffix}'
 
         # Sorted items should be in ascending order by size
-        items = list(cache.sorted(key='size'))
-        assert len(items) == limit or 100
+        items = list(cache.sorted(key='size', limit=limit))
+        assert len(items) == (limit or 100)
 
         prev_item = None
         for item in items:
@@ -241,8 +257,8 @@ def test_sorted__by_expires(self, limit):
             cache[f'key_{i}'] = response
 
         # Sorted items should be in ascending order by expiration time
-        items = list(cache.sorted(key='expires'))
-        assert len(items) == limit or 100
+        items = list(cache.sorted(key='expires', limit=limit))
+        assert len(items) == (limit or 100)
 
         prev_item = None
         for item in items:
@@ -328,10 +344,11 @@ def test_clear__failure(self, mock_unlink, mock_clear):
         """When a corrupted cache prevents a normal DROP TABLE, clear() should 
still succeed"""
         session = self.init_session(clear=False)
         session.cache.responses['key_1'] = 'value_1'
+        db_path = session.cache.responses.db_path
         session.cache.clear()
 
         assert len(session.cache.responses) == 0
-        assert mock_unlink.call_count == 1
+        mock_unlink.assert_called_once_with(db_path)
 
     @patch.object(BaseCache, 'clear', side_effect=IOError)
     def test_clear__file_already_deleted(self, mock_clear):
@@ -378,6 +395,28 @@ def test_delete__skip_vacuum(self):
             session.cache.delete('key_1', 'key_2', vacuum=False)
             mock_vacuum.assert_not_called()
 
+    def test_vacuum__frees_disk_space(self):
+        """vacuum() should reclaim disk space after bulk deletion (regression 
test for #1156)"""
+        session = self.init_session()
+        db_path = session.cache.responses.db_path
+
+        for i in range(200):
+            cr = CachedResponse(status_code=200, 
url=f'https://example.com/{i}')
+            cr.request = CachedRequest(method='GET', 
url=f'https://example.com/{i}')
+            cr._content = b'x' * 50_000
+            session.cache.save_response(cr, cache_key=f'k{i}')
+
+        size_before = os.path.getsize(db_path)
+        session.cache.delete(older_than=timedelta(seconds=-1))
+        assert session.cache.responses.count() == 0
+
+        session.cache.responses.vacuum()
+        size_after = os.path.getsize(db_path)
+
+        assert size_after < size_before / 10, (
+            f'vacuum() did not reclaim disk space: {size_before} -> 
{size_after} bytes'
+        )
+
     @patch.object(SQLiteDict, 'sorted')
     def test_filter__expired(self, mock_sorted):
         """Filtering by expired should use a more efficient SQL query"""

Reply via email to