Skip to content

Commit 0eab928

Browse files
authored
Keep reused cache decorators independent (#182)
Allocate memory once per decorated function so reusing cache() or memoize() cannot return another function's result or invalidate its cache.
1 parent cdf4328 commit 0eab928

2 files changed

Lines changed: 36 additions & 3 deletions

File tree

funcy/calc.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def memoize(_func=None, *, key_func=None):
2626
"""
2727
if _func is not None:
2828
return memoize(key_func=key_func)(_func)
29-
return _memory_decorator({}, key_func)
29+
return _memory_decorator(dict, key_func)
3030

3131
memoize.skip = SkipMemory
3232

@@ -36,13 +36,15 @@ def cache(timeout, *, key_func=None):
3636
if isinstance(timeout, timedelta):
3737
timeout = timeout.total_seconds()
3838

39-
return _memory_decorator(CacheMemory(timeout), key_func)
39+
return _memory_decorator(lambda: CacheMemory(timeout), key_func)
4040

4141
cache.skip = SkipMemory
4242

4343

44-
def _memory_decorator(memory, key_func):
44+
def _memory_decorator(memory_factory, key_func):
4545
def decorator(func):
46+
memory = memory_factory()
47+
4648
@wraps(func)
4749
def wrapper(*args, **kwargs):
4850
# We inline this here since @memoize also targets microoptimizations

tests/test_calc.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,37 @@ def total(values):
105105
assert calls == [[1, 2], [1, 2]]
106106

107107

108+
@pytest.mark.parametrize('deco', [memoize(), cache(60)], ids=['memoize', 'cache'])
109+
def test_reused_memory_decorator(deco):
110+
calls = []
111+
112+
@deco
113+
def inc(x):
114+
calls.append('inc')
115+
return x + 1
116+
117+
@deco
118+
def double(x):
119+
calls.append('double')
120+
return x * 2
121+
122+
assert inc(3) == 4
123+
assert double(3) == 6
124+
assert inc(3) == 4
125+
assert double(3) == 6
126+
assert calls == ['inc', 'double']
127+
128+
inc.invalidate(3)
129+
assert double(3) == 6
130+
assert inc(3) == 4
131+
assert calls == ['inc', 'double', 'inc']
132+
133+
inc.invalidate_all()
134+
assert double(3) == 6
135+
assert inc(3) == 4
136+
assert calls == ['inc', 'double', 'inc', 'inc']
137+
138+
108139
def test_make_lookuper():
109140
@make_lookuper
110141
def letter_index():

0 commit comments

Comments
 (0)