Skip to content

Commit 60e18ec

Browse files
committed
-
1 parent 414dded commit 60e18ec

File tree

2 files changed

+92
-16
lines changed

2 files changed

+92
-16
lines changed

python_toolbox/caching/cache.py

Lines changed: 54 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
import functools
1212
import datetime as datetime_module
1313

14+
from python_toolbox import binary_search
1415
from python_toolbox import decorator_tools
15-
1616
from python_toolbox.sleek_reffing import SleekCallArgs
1717
from python_toolbox.nifty_collections import OrderedDict
1818

@@ -24,7 +24,7 @@ class CLEAR_ENTIRE_CACHE(object):
2424

2525

2626
@decorator_tools.helpful_decorator_builder
27-
def cache(max_size=infinity, time_limit=None):
27+
def cache(max_size=infinity, time_to_keep=None):
2828
'''
2929
Cache a function, saving results so they won't have to be computed again.
3030
@@ -56,17 +56,19 @@ def f(a, b=2):
5656
# have to go through so much shit. update: probably it will help only for
5757
# completely argumentless function. so do one for those.
5858

59-
if time_limit is not None:
60-
if not isinstance(time_limit, datetime_module.timedelta):
59+
if time_to_keep is not None:
60+
if max_size != infinity:
61+
raise NotImplementedError
62+
if not isinstance(time_to_keep, datetime_module.timedelta):
6163
try:
62-
time_limit = datetime_module.timedelta(**time_limit)
64+
time_to_keep = datetime_module.timedelta(**time_to_keep)
6365
except Exception:
6466
raise TypeError(
6567
'`time_limit` must be either a `timedelta` object or a '
6668
'dict of keyword arguments for constructing a '
6769
'`timedelta` object.'
6870
)
69-
assert isinstance(time_limit, datetime_module.timedelta)
71+
assert isinstance(time_to_keep, datetime_module.timedelta)
7072

7173

7274
def decorator(function):
@@ -76,17 +78,53 @@ def decorator(function):
7678

7779
if max_size == infinity:
7880

79-
cache_dict = {}
81+
if time_to_keep:
8082

81-
def cached(function, *args, **kwargs):
82-
sleek_call_args = \
83-
SleekCallArgs(cache_dict, function, *args, **kwargs)
84-
try:
85-
return cached._cache[sleek_call_args]
86-
except KeyError:
87-
cached._cache[sleek_call_args] = value = \
88-
function(*args, **kwargs)
89-
return value
83+
cache_dict = OrderedDict()
84+
sorting_key_function = lambda sleek_call_args: \
85+
cached._cache[sleek_call_args][0]
86+
87+
88+
def remove_expired_entries():
89+
cutting_point = binary_search.binary_search_by_index(
90+
cached._cache,
91+
sorting_key_function,
92+
datetime_module.datetime.now(),
93+
rounding=binary_search.LOW
94+
)
95+
if cutting_point is not None:
96+
for key in cached._cache.keys()[:cutting_point]:
97+
del cached._cache[key]
98+
99+
100+
def cached(function, *args, **kwargs):
101+
remove_expired_entries()
102+
sleek_call_args = \
103+
SleekCallArgs(cache_dict, function, *args, **kwargs)
104+
try:
105+
return cached._cache[sleek_call_args][0]
106+
except KeyError:
107+
value = function(*args, **kwargs)
108+
cached._cache[sleek_call_args] = (
109+
value,
110+
datetime_module.datetime.now()
111+
)
112+
cached._cache.sort(key=sorting_key_function)
113+
return value
114+
115+
else: # not time_to_keep
116+
117+
cache_dict = {}
118+
119+
def cached(function, *args, **kwargs):
120+
sleek_call_args = \
121+
SleekCallArgs(cache_dict, function, *args, **kwargs)
122+
try:
123+
return cached._cache[sleek_call_args]
124+
except KeyError:
125+
cached._cache[sleek_call_args] = value = \
126+
function(*args, **kwargs)
127+
return value
90128

91129
else: # max_size < infinity
92130

test_python_toolbox/test_caching/test_cache.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55

66
from __future__ import with_statement
77

8+
import datetime as datetime_module
89
import re
910
import weakref
1011

1112
import nose.tools
1213

1314
from python_toolbox.caching import cache
15+
from python_toolbox import temp_value_setting
1416
from python_toolbox import cute_testing
1517
from python_toolbox import gc_tools
1618

@@ -186,4 +188,40 @@ def test_double_caching():
186188
g = cache()(f)
187189

188190
assert f is g
191+
192+
193+
def test_time_to_keep():
194+
f = cache(time_to_keep={'years': 1})(counting_func)
195+
196+
assert f('a') == 1
197+
assert f('b') == 2
198+
assert f('c') == 3
199+
assert f('b') == 2
200+
201+
start_datetime = datetime_module.datetime.now()
202+
fixed_time = start_datetime
203+
def _mock_now():
204+
return fixed_time
205+
206+
with temp_value_setting.TempValueSetter(
207+
(datetime_module.datetime, 'now'), _mock_now):
208+
assert map(f, 'abc') == (1, 2, 3)
209+
fixed_time += datetime_module.timedelta(days=100)
210+
assert map(f, 'abc') == (1, 2, 3)
211+
assert map(f, 'def') == (4, 5, 6)
212+
fixed_time += datetime_module.timedelta(days=100)
213+
assert map(f, 'abc') == (1, 2, 3)
214+
assert map(f, 'def') == (4, 5, 6)
215+
fixed_time += datetime_module.timedelta(days=100)
216+
assert map(f, 'abc') == (1, 2, 3)
217+
assert map(f, 'def') == (4, 5, 6)
218+
fixed_time += datetime_module.timedelta(days=100)
219+
assert map(f, 'abc') == (7, 8, 9)
220+
assert map(f, 'def') == (4, 5, 6)
221+
fixed_time += datetime_module.timedelta(days=100)
222+
assert map(f, 'abc') == (7, 8, 9)
223+
assert map(f, 'def') == (10, 11, 12)
224+
fixed_time += datetime_module.timedelta(days=1000)
225+
assert map(f, 'abcdef') == (13, 14, 15, 16, 17, 18)
226+
189227

0 commit comments

Comments
 (0)