From 9b01c267f928118549cd5699c380572c9b621005 Mon Sep 17 00:00:00 2001 From: Sarbajit Sengupta Date: Sat, 31 Oct 2026 23:48:36 +0530 Subject: [PATCH 1/2] Validate throttle capacity and refill period before initialization --- diskcache/recipes.py | 7 +++++++ tests/test_recipes.py | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/diskcache/recipes.py b/diskcache/recipes.py index babb68f..dc41efa 100644 --- a/diskcache/recipes.py +++ b/diskcache/recipes.py @@ -266,6 +266,9 @@ def throttle( Assumes keys will not be evicted. Set the eviction policy to 'none' on the cache to guarantee the keys are not evicted. + The token capacity `count` must be finite and at least one. The refill + period `seconds` must be finite and greater than zero. + >>> import diskcache, time >>> cache = diskcache.Cache() >>> count = 0 @@ -282,6 +285,10 @@ def throttle( """ def decorator(func): + if not math.isfinite(count) or count < 1: + raise ValueError('count must be finite and at least one') + if not math.isfinite(seconds) or seconds <= 0: + raise ValueError('seconds must be finite and greater than zero') rate = count / float(seconds) key = full_name(func) if name is None else name now = time_func() diff --git a/tests/test_recipes.py b/tests/test_recipes.py index ae74459..073e424 100644 --- a/tests/test_recipes.py +++ b/tests/test_recipes.py @@ -28,6 +28,26 @@ def test_averager(cache): assert nums.pop() == 9.5 +@pytest.mark.parametrize('count', [0, -1, 0.5, float('nan'), float('inf')]) +def test_throttle_rejects_invalid_capacity(cache, count): + with pytest.raises(ValueError, match='count'): + dc.throttle(cache, count, 1, name='invalid')(lambda: None) + assert 'invalid' not in cache + + +@pytest.mark.parametrize('seconds', [0, -1, float('nan'), float('inf')]) +def test_throttle_rejects_invalid_period(cache, seconds): + with pytest.raises(ValueError, match='seconds'): + dc.throttle(cache, 1, seconds, name='invalid')(lambda: None) + assert 'invalid' not in cache + + +@pytest.mark.parametrize('count, seconds', [(1, 0.25), (1.5, 2), (2, 1)]) +def test_throttle_valid_capacity_and_period(cache, count, seconds): + function = dc.throttle(cache, count, seconds)(lambda: 'result') + assert function() == 'result' + + def test_lock(cache): state = {'num': 0} lock = dc.Lock(cache, 'demo') From 580d2660cd0e34257589df824e00b43801dd2de2 Mon Sep 17 00:00:00 2001 From: Sarbajit Sengupta Date: Sat, 31 Oct 2026 23:50:48 +0530 Subject: [PATCH 2/2] Keep throttle validation regressions separate from bucket tests --- tests/test_recipes.py | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/test_recipes.py b/tests/test_recipes.py index 073e424..42c0a4e 100644 --- a/tests/test_recipes.py +++ b/tests/test_recipes.py @@ -28,26 +28,6 @@ def test_averager(cache): assert nums.pop() == 9.5 -@pytest.mark.parametrize('count', [0, -1, 0.5, float('nan'), float('inf')]) -def test_throttle_rejects_invalid_capacity(cache, count): - with pytest.raises(ValueError, match='count'): - dc.throttle(cache, count, 1, name='invalid')(lambda: None) - assert 'invalid' not in cache - - -@pytest.mark.parametrize('seconds', [0, -1, float('nan'), float('inf')]) -def test_throttle_rejects_invalid_period(cache, seconds): - with pytest.raises(ValueError, match='seconds'): - dc.throttle(cache, 1, seconds, name='invalid')(lambda: None) - assert 'invalid' not in cache - - -@pytest.mark.parametrize('count, seconds', [(1, 0.25), (1.5, 2), (2, 1)]) -def test_throttle_valid_capacity_and_period(cache, count, seconds): - function = dc.throttle(cache, count, seconds)(lambda: 'result') - assert function() == 'result' - - def test_lock(cache): state = {'num': 0} lock = dc.Lock(cache, 'demo') @@ -124,3 +104,23 @@ def worker(num): while (time.time() - start) < 1: worker(100) assert state['num'] > 0 + + +@pytest.mark.parametrize('count', [0, -1, 0.5, float('nan'), float('inf')]) +def test_throttle_rejects_invalid_capacity(cache, count): + with pytest.raises(ValueError, match='count'): + dc.throttle(cache, count, 1, name='invalid')(lambda: None) + assert 'invalid' not in cache + + +@pytest.mark.parametrize('seconds', [0, -1, float('nan'), float('inf')]) +def test_throttle_rejects_invalid_period(cache, seconds): + with pytest.raises(ValueError, match='seconds'): + dc.throttle(cache, 1, seconds, name='invalid')(lambda: None) + assert 'invalid' not in cache + + +@pytest.mark.parametrize('count, seconds', [(1, 0.25), (1.5, 2), (2, 1)]) +def test_throttle_valid_capacity_and_period(cache, count, seconds): + function = dc.throttle(cache, count, seconds)(lambda: 'result') + assert function() == 'result'