Skip to content

Commit 2b1d419

Browse files
committed
Implement descriptors (wip).
1 parent d065568 commit 2b1d419

File tree

2 files changed

+198
-21
lines changed

2 files changed

+198
-21
lines changed

src/cachetools/_cachedmethod.py

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,14 @@ def __init__(self, method, cache, key, lock=None, cond=None):
1111
self.method = method
1212
self.cache = cache
1313
self.cache_key = key
14+
# TODO: always present? check @cached!
1415
self.cache_lock = lock
1516
self.cache_condition = cond
1617
functools.update_wrapper(self, method)
1718

1819
def __get__(self, obj, objtype=None):
19-
from functools import partial
20-
2120
# print("get", self, obj, objtype)
22-
wrapper = partial(self.__call__, obj)
21+
wrapper = functools.partial(self.__call__, obj)
2322
wrapper.cache = self.cache
2423
wrapper.cache_key = self.cache_key
2524
wrapper.cache_lock = self.cache_lock
@@ -109,17 +108,21 @@ def __call__(self, obj, *args, **kwargs):
109108
self.cache_condition(obj).notify_all()
110109

111110

112-
class InfoDescriptor:
111+
class InfoDescriptor(BaseDescriptor):
113112

114113
HITS = 0
115114
MISSES = 1
116115

117116
def __init__(self, method, cache, key, info, lock=None, cond=None):
118117
BaseDescriptor.__init__(self, method, cache, key, lock, cond)
119-
# FIXME: private?
120118
self.info = info
121119
self.stats = weakref.WeakKeyDictionary()
122120

121+
def __get__(self, obj, objtype=None):
122+
wrapper = BaseDescriptor.__get__(self, obj, objtype)
123+
wrapper.cache_info = functools.partial(self.cache_info, obj)
124+
return wrapper
125+
123126
def __call__(self, obj, *args, **kwargs):
124127
c = self.cache(obj)
125128
k = self.cache_key(obj, *args, **kwargs)
@@ -138,19 +141,19 @@ def __call__(self, obj, *args, **kwargs):
138141
return v
139142

140143
def cache_clear(self, obj=None):
141-
# print("clear", self, obj)
144+
print("clear", self, obj)
142145
c = self.cache(obj)
143146
if c is not None:
144147
self.stats[obj] = [0, 0]
145148
c.clear()
146149

147150
def cache_info(self, obj=None):
148-
# print("info", self, obj)
151+
print("cache_info", self, obj, self.stats)
149152
hits, misses = self.stats.setdefault(obj, [0, 0])
150153
return self.info(self.cache(obj), hits, misses)
151154

152155

153-
class LockedInfoDescriptor(BaseDescriptor):
156+
class LockedInfoDescriptor(InfoDescriptor):
154157

155158
def __call__(self, obj, *args, **kwargs):
156159
# print("call", self, obj, args, kwargs)
@@ -223,14 +226,15 @@ def __call__(self, obj, *args, **kwargs):
223226
def _wrapper(method, cache, key, lock=None, cond=None, info=None):
224227

225228
if info:
226-
if cond is not None and lock is not None:
227-
return ConditionInfoDescriptor(method, cache, key, info, lock, cond)
228-
elif cond is not None:
229-
return ConditionInfoDescriptor(method, cache, key, info, cond, cond)
230-
elif lock is not None:
231-
return LockedInfoDescriptor(method, cache, key, info, lock)
232-
else:
233-
return InfoDescriptor(method, cache, key, info)
229+
# FIXME: Full info support
230+
# if cond is not None and lock is not None:
231+
# return ConditionInfoDescriptor(method, cache, key, info, lock, cond)
232+
# elif cond is not None:
233+
# return ConditionInfoDescriptor(method, cache, key, info, cond, cond)
234+
# elif lock is not None:
235+
# return LockedInfoDescriptor(method, cache, key, info, lock)
236+
# else:
237+
return InfoDescriptor(method, cache, key, info)
234238
else:
235239
if cond is not None and lock is not None:
236240
return ConditionDescriptor(method, cache, key, lock, cond)

tests/test_cachedmethod.py

Lines changed: 178 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ def get_typedmethod(self, value):
2323
self.count += 1
2424
return self.count
2525

26+
@cachedmethod(lambda self: self.cache, info=True)
27+
def get_info(self, value):
28+
print("Cached.get_info() >> %s %s\n", self, value)
29+
self.count += 1
30+
return self.count
31+
2632
@classmethod
2733
@cachedmethod(lambda cls: cls.class_cache)
2834
def get_classmethod(cls, value):
@@ -47,6 +53,11 @@ def get(self, value):
4753
self.count += 1
4854
return self.count
4955

56+
@cachedmethod(lambda self: self.cache, lock=lambda self: self, info=True)
57+
def get_info(self, value):
58+
self.count += 1
59+
return self.count
60+
5061
def __enter__(self):
5162
self.lock_count += 1
5263

@@ -70,6 +81,23 @@ def get(self, value):
7081
def get_lock(self, value):
7182
return Locked.get.__wrapped__(self, value)
7283

84+
@cachedmethod(
85+
lambda self: self.cache,
86+
condition=lambda self: self,
87+
info=True,
88+
)
89+
def get_info(self, value):
90+
return Locked.get_info.__wrapped__(self, value)
91+
92+
@cachedmethod(
93+
lambda self: self.cache,
94+
lock=lambda self: self,
95+
condition=lambda self: self,
96+
info=True,
97+
)
98+
def get_info_lock(self, value):
99+
return Locked.get_info.__wrapped__(self, value)
100+
73101
def wait_for(self, predicate):
74102
self.wait_count += 1
75103

@@ -361,21 +389,166 @@ def test(self):
361389
self.assertEqual(Cached.get_classmethod(1), 2)
362390
self.assertEqual(cached.get_classmethod(1.0), 2)
363391
self.assertEqual(Cached.get_classmethod(1.0), 2)
364-
self.assertEqual(cached.get_classmethod(1.0), 2)
365-
self.assertEqual(Cached.get_classmethod(1.0), 2)
392+
self.assertEqual(Cached.get_classmethod(1.1), 3)
393+
self.assertEqual(cached.get_classmethod(1.1), 3)
366394

367395
cached.class_cache.clear()
368-
self.assertEqual(cached.get_classmethod(1), 3)
396+
self.assertEqual(cached.get_classmethod(1), 4)
369397

370398
def test_typedmethod(self):
371399
Cached.class_cache = LRUCache(2)
372400
Cached.class_count = 0
373401
cached = Cached(None)
374402

375403
self.assertEqual(cached.get_typedclassmethod(0), 1)
404+
self.assertEqual(Cached.get_typedclassmethod(0), 1)
376405
self.assertEqual(cached.get_typedclassmethod(1), 2)
377-
self.assertEqual(cached.get_typedclassmethod(1), 2)
378-
self.assertEqual(cached.get_typedclassmethod(1.0), 3)
406+
self.assertEqual(Cached.get_typedclassmethod(1), 2)
379407
self.assertEqual(cached.get_typedclassmethod(1.0), 3)
408+
self.assertEqual(Cached.get_typedclassmethod(1.0), 3)
380409
self.assertEqual(cached.get_typedclassmethod(0.0), 4)
410+
self.assertEqual(Cached.get_typedclassmethod(0.0), 4)
411+
self.assertEqual(Cached.get_typedclassmethod(0), 5)
381412
self.assertEqual(cached.get_typedclassmethod(0), 5)
413+
414+
415+
class CachedMethodInfoTest(unittest.TestCase):
416+
417+
def test_info(self):
418+
cache = {}
419+
cached = Cached(cache)
420+
421+
self.assertEqual(len(cache), 0)
422+
# self.assertEqual(Cached.get_info.cache_info(cached), (0, 0, None, 0))
423+
self.assertEqual(cached.get_info.cache_info(), (0, 0, None, 0))
424+
self.assertEqual(cached.get_info(0), 1)
425+
self.assertEqual(len(cache), 1)
426+
# self.assertEqual(Cached.get_info.cache_info(), (0, 1, None, 1))
427+
self.assertEqual(cached.get_info(1), 2)
428+
self.assertEqual(len(cache), 2)
429+
# self.assertEqual(Cached.get_info.cache_info(), (0, 2, None, 2))
430+
self.assertEqual(cached.get_info(1), 2)
431+
self.assertEqual(len(cache), 2)
432+
# self.assertEqual(Cached.get_info.cache_info(), (1, 2, None, 2))
433+
Cached.get_info.cache_clear(cached)
434+
self.assertEqual(len(cache), 0)
435+
# self.assertEqual(Cached.get_info.cache_info(), (0, 0, None, 0))
436+
437+
def test_info_locked(self):
438+
cache = {}
439+
cached = Locked(cache)
440+
441+
self.assertEqual(len(cache), 0)
442+
# self.assertEqual(Cached.get_info.cache_info(cached), (0, 0, None, 0))
443+
self.assertEqual(cached.get_info.cache_info(), (0, 0, None, 0))
444+
# self.assertEqual(cached.lock_count, 1)
445+
self.assertEqual(cached.get_info(0), 1)
446+
self.assertEqual(len(cache), 1)
447+
# self.assertEqual(cached.lock_count, 3)
448+
# self.assertEqual(cached.get_info.cache_info(cached), (0, 1, None, 1))
449+
# self.assertEqual(cached.lock_count, 4)
450+
self.assertEqual(cached.get_info(1), 2)
451+
self.assertEqual(len(cache), 2)
452+
# self.assertEqual(cached.lock_count, 6)
453+
# self.assertEqual(cached.get_info.cache_info(cached), (0, 2, None, 2))
454+
# self.assertEqual(cached.lock_count, 7)
455+
self.assertEqual(cached.get_info(1), 2)
456+
self.assertEqual(len(cache), 2)
457+
# self.assertEqual(cached.lock_count, 8)
458+
# self.assertEqual(cached.get_info.cache_info(cached), (1, 2, None, 2))
459+
# self.assertEqual(cached.lock_count, 9)
460+
cached.get_info.cache_clear(cached)
461+
# self.assertEqual(cached.lock_count, 10)
462+
self.assertEqual(len(cache), 0)
463+
# self.assertEqual(cached.get_info.cache_info(cached), (0, 0, None, 0))
464+
465+
def test_info_condition(self):
466+
cache = {}
467+
cached = Conditioned(cache)
468+
469+
self.assertEqual(len(cache), 0)
470+
# self.assertEqual(Cached.get_info.cache_info(cached), (0, 0, None, 0))
471+
# self.assertEqual(cached.get_info.cache_info(), (0, 0, None, 0))
472+
# self.assertEqual(cached.lock_count, 1)
473+
self.assertEqual(cached.get_info(0), 1)
474+
self.assertEqual(len(cache), 1)
475+
# self.assertEqual(cached.lock_count, 4)
476+
# self.assertEqual(cached.get_info.cache_info(cached), (0, 1, None, 1))
477+
# self.assertEqual(cached.lock_count, 5)
478+
self.assertEqual(cached.get_info(1), 2)
479+
self.assertEqual(len(cache), 2)
480+
# self.assertEqual(cached.lock_count, 8)
481+
# self.assertEqual(cached.get_info.cache_info(cached), (0, 2, None, 2))
482+
# self.assertEqual(cached.lock_count, 9)
483+
self.assertEqual(cached.get_info(1), 2)
484+
self.assertEqual(len(cache), 2)
485+
# self.assertEqual(cached.lock_count, 10)
486+
# self.assertEqual(cached.get_info.cache_info(cached), (1, 2, None, 2))
487+
# self.assertEqual(cached.lock_count, 11)
488+
cached.get_info.cache_clear(cached)
489+
# self.assertEqual(cached.lock_count, 12)
490+
self.assertEqual(len(cache), 0)
491+
# self.assertEqual(cached.get_info.cache_info(cached), (0, 0, None, 0))
492+
493+
def test_info_lru(self):
494+
cache = LRUCache(maxsize=2)
495+
cached = Cached(cache)
496+
497+
self.assertEqual(cached.get_info.cache_info(), (0, 0, 2, 0))
498+
# self.assertEqual(Cached.get_info.cache_info(cached), (0, 0, 2, 0))
499+
self.assertEqual(cached.get_info(0), 1)
500+
# self.assertEqual(cached.get_info.cache_info(cached), (0, 1, 2, 1))
501+
self.assertEqual(cached.get_info(1), 2)
502+
# self.assertEqual(cached.get_info.cache_info(cached), (0, 2, 2, 2))
503+
self.assertEqual(cached.get_info(1), 2)
504+
# self.assertEqual(cached.get_info.cache_info(cached), (1, 2, 2, 2))
505+
506+
def test_info_nospace(self):
507+
cached = Cached(LRUCache(maxsize=0))
508+
509+
self.assertEqual(cached.get_info(0), 1)
510+
self.assertEqual(cached.get_info(1), 2)
511+
self.assertEqual(cached.get_info(1), 3)
512+
self.assertEqual(cached.get_info(1.0), 4)
513+
self.assertEqual(cached.get_info(1.0), 5)
514+
# self.assertEqual(cached.get_info.cache_info(cached), (0, 5, 0, 0))
515+
516+
def test_info_locked_nospace(self):
517+
cached = Locked(LRUCache(maxsize=0))
518+
519+
self.assertEqual(cached.get_info(0), 1)
520+
# self.assertEqual(cached.lock_count, 2)
521+
self.assertEqual(cached.get_info(1), 2)
522+
# self.assertEqual(cached.lock_count, 4)
523+
self.assertEqual(cached.get_info(1), 3)
524+
# self.assertEqual(cached.lock_count, 6)
525+
self.assertEqual(cached.get_info(1.0), 4)
526+
# self.assertEqual(cached.lock_count, 8)
527+
self.assertEqual(cached.get_info(1.0), 5)
528+
# self.assertEqual(cached.lock_count, 10)
529+
# self.assertEqual(cached.get_info.cache_info(cached), (0, 5, 0, 0))
530+
531+
def test_info_condition_nospace(self):
532+
cached = Conditioned(LRUCache(maxsize=0))
533+
534+
self.assertEqual(cached.get_info(0), 1)
535+
# self.assertEqual(cached.lock_count, 3)
536+
# self.assertEqual(cached.wait_count, 1)
537+
# self.assertEqual(cached.notify_count, 1)
538+
self.assertEqual(cached.get_info(1), 2)
539+
# self.assertEqual(cached.lock_count, 6)
540+
# self.assertEqual(cached.wait_count, 2)
541+
# self.assertEqual(cached.notify_count, 2)
542+
self.assertEqual(cached.get_info(1), 3)
543+
# self.assertEqual(cached.lock_count, 9)
544+
# self.assertEqual(cached.wait_count, 3)
545+
# self.assertEqual(cached.notify_count, 3)
546+
self.assertEqual(cached.get_info(1.0), 4)
547+
# self.assertEqual(cached.lock_count, 12)
548+
# self.assertEqual(cached.wait_count, 4)
549+
# self.assertEqual(cached.notify_count, 4)
550+
self.assertEqual(cached.get_info(1.0), 5)
551+
# self.assertEqual(cached.lock_count, 15)
552+
# self.assertEqual(cached.wait_count, 5)
553+
# self.assertEqual(cached.notify_count, 5)
554+
# self.assertEqual(cached.get_info.cache_info(cached), (0, 5, 0, 0))

0 commit comments

Comments
 (0)